Commit 9f67ede6 authored by Brian Tiger Chow's avatar Brian Tiger Chow

refactor(fsrepo) extract component.Component

parent f37646bf
package component
import (
"io"
"github.com/jbenet/go-ipfs/repo/config"
)
type Component interface {
Open() error
io.Closer
}
type Initializer func(path string, conf *config.Config) error
type InitializationChecker func(path string) bool
package fsrepo package component
import ( import (
common "github.com/jbenet/go-ipfs/repo/common" common "github.com/jbenet/go-ipfs/repo/common"
config "github.com/jbenet/go-ipfs/repo/config" config "github.com/jbenet/go-ipfs/repo/config"
serialize "github.com/jbenet/go-ipfs/repo/fsrepo/serialize"
util "github.com/jbenet/go-ipfs/util" util "github.com/jbenet/go-ipfs/util"
) )
var _ component = &configComponent{} var _ Component = &ConfigComponent{}
var _ componentInitializationChecker = configComponentIsInitialized var _ Initializer = InitConfigComponent
var _ InitializationChecker = ConfigComponentIsInitialized
// configComponent abstracts the config component of the FSRepo. // ConfigComponent abstracts the config component of the FSRepo.
// NB: create with makeConfigComponent function. // NB: create with makeConfigComponent function.
type configComponent struct { // NOT THREAD-SAFE
path string // required at instantiation type ConfigComponent struct {
Path string // required at instantiation
config *config.Config // assigned on Open() config *config.Config // assigned on Open()
} }
// makeConfigComponent instantiates a valid configComponent. // fsrepoConfigInit initializes the FSRepo's ConfigComponent.
func makeConfigComponent(path string) configComponent { func InitConfigComponent(path string, conf *config.Config) error {
return configComponent{path: path} if ConfigComponentIsInitialized(path) {
}
// fsrepoConfigInit initializes the FSRepo's configComponent.
func initConfigComponent(path string, conf *config.Config) error {
if configComponentIsInitialized(path) {
return nil return nil
} }
configFilename, err := config.Filename(path) configFilename, err := config.Filename(path)
...@@ -33,19 +31,19 @@ func initConfigComponent(path string, conf *config.Config) error { ...@@ -33,19 +31,19 @@ func initConfigComponent(path string, conf *config.Config) error {
// initialization is the one time when it's okay to write to the config // initialization is the one time when it's okay to write to the config
// without reading the config from disk and merging any user-provided keys // without reading the config from disk and merging any user-provided keys
// that may exist. // that may exist.
if err := writeConfigFile(configFilename, conf); err != nil { if err := serialize.WriteConfigFile(configFilename, conf); err != nil {
return err return err
} }
return nil return nil
} }
// Open returns an error if the config file is not present. // Open returns an error if the config file is not present.
func (c *configComponent) Open() error { func (c *ConfigComponent) Open() error {
configFilename, err := config.Filename(c.path) configFilename, err := config.Filename(c.Path)
if err != nil { if err != nil {
return err return err
} }
conf, err := load(configFilename) conf, err := serialize.Load(configFilename)
if err != nil { if err != nil {
return err return err
} }
...@@ -54,46 +52,46 @@ func (c *configComponent) Open() error { ...@@ -54,46 +52,46 @@ func (c *configComponent) Open() error {
} }
// Close satisfies the fsrepoComponent interface. // Close satisfies the fsrepoComponent interface.
func (c *configComponent) Close() error { func (c *ConfigComponent) Close() error {
return nil // config doesn't need to be closed. return nil // config doesn't need to be closed.
} }
func (c *configComponent) Config() *config.Config { func (c *ConfigComponent) Config() *config.Config {
return c.config return c.config
} }
// SetConfig updates the config file. // SetConfig updates the config file.
func (c *configComponent) SetConfig(updated *config.Config) error { func (c *ConfigComponent) SetConfig(updated *config.Config) error {
return c.setConfigUnsynced(updated) return c.setConfigUnsynced(updated)
} }
// GetConfigKey retrieves only the value of a particular key. // GetConfigKey retrieves only the value of a particular key.
func (c *configComponent) GetConfigKey(key string) (interface{}, error) { func (c *ConfigComponent) GetConfigKey(key string) (interface{}, error) {
filename, err := config.Filename(c.path) filename, err := config.Filename(c.Path)
if err != nil { if err != nil {
return nil, err return nil, err
} }
var cfg map[string]interface{} var cfg map[string]interface{}
if err := readConfigFile(filename, &cfg); err != nil { if err := serialize.ReadConfigFile(filename, &cfg); err != nil {
return nil, err return nil, err
} }
return common.MapGetKV(cfg, key) return common.MapGetKV(cfg, key)
} }
// SetConfigKey writes the value of a particular key. // SetConfigKey writes the value of a particular key.
func (c *configComponent) SetConfigKey(key string, value interface{}) error { func (c *ConfigComponent) SetConfigKey(key string, value interface{}) error {
filename, err := config.Filename(c.path) filename, err := config.Filename(c.Path)
if err != nil { if err != nil {
return err return err
} }
var mapconf map[string]interface{} var mapconf map[string]interface{}
if err := readConfigFile(filename, &mapconf); err != nil { if err := serialize.ReadConfigFile(filename, &mapconf); err != nil {
return err return err
} }
if err := common.MapSetKV(mapconf, key, value); err != nil { if err := common.MapSetKV(mapconf, key, value); err != nil {
return err return err
} }
if err := writeConfigFile(filename, mapconf); err != nil { if err := serialize.WriteConfigFile(filename, mapconf); err != nil {
return err return err
} }
// in order to get the updated values, read updated config from the // in order to get the updated values, read updated config from the
...@@ -105,9 +103,9 @@ func (c *configComponent) SetConfigKey(key string, value interface{}) error { ...@@ -105,9 +103,9 @@ func (c *configComponent) SetConfigKey(key string, value interface{}) error {
return c.setConfigUnsynced(conf) // TODO roll this into this method return c.setConfigUnsynced(conf) // TODO roll this into this method
} }
// configComponentIsInitialized returns true if the repo is initialized at // ConfigComponentIsInitialized returns true if the repo is initialized at
// provided |path|. // provided |path|.
func configComponentIsInitialized(path string) bool { func ConfigComponentIsInitialized(path string) bool {
configFilename, err := config.Filename(path) configFilename, err := config.Filename(path)
if err != nil { if err != nil {
return false return false
...@@ -119,8 +117,8 @@ func configComponentIsInitialized(path string) bool { ...@@ -119,8 +117,8 @@ func configComponentIsInitialized(path string) bool {
} }
// setConfigUnsynced is for private use. // setConfigUnsynced is for private use.
func (r *configComponent) setConfigUnsynced(updated *config.Config) error { func (r *ConfigComponent) setConfigUnsynced(updated *config.Config) error {
configFilename, err := config.Filename(r.path) configFilename, err := config.Filename(r.Path)
if err != nil { if err != nil {
return err return err
} }
...@@ -128,7 +126,7 @@ func (r *configComponent) setConfigUnsynced(updated *config.Config) error { ...@@ -128,7 +126,7 @@ func (r *configComponent) setConfigUnsynced(updated *config.Config) error {
// as a map, write the updated struct values to the map and write the map // as a map, write the updated struct values to the map and write the map
// to disk. // to disk.
var mapconf map[string]interface{} var mapconf map[string]interface{}
if err := readConfigFile(configFilename, &mapconf); err != nil { if err := serialize.ReadConfigFile(configFilename, &mapconf); err != nil {
return err return err
} }
m, err := config.ToMap(updated) m, err := config.ToMap(updated)
...@@ -138,7 +136,7 @@ func (r *configComponent) setConfigUnsynced(updated *config.Config) error { ...@@ -138,7 +136,7 @@ func (r *configComponent) setConfigUnsynced(updated *config.Config) error {
for k, v := range m { for k, v := range m {
mapconf[k] = v mapconf[k] = v
} }
if err := writeConfigFile(configFilename, mapconf); err != nil { if err := serialize.WriteConfigFile(configFilename, mapconf); err != nil {
return err return err
} }
*r.config = *updated // copy so caller cannot modify this private config *r.config = *updated // copy so caller cannot modify this private config
......
...@@ -11,8 +11,10 @@ import ( ...@@ -11,8 +11,10 @@ import (
repo "github.com/jbenet/go-ipfs/repo" repo "github.com/jbenet/go-ipfs/repo"
config "github.com/jbenet/go-ipfs/repo/config" config "github.com/jbenet/go-ipfs/repo/config"
component "github.com/jbenet/go-ipfs/repo/fsrepo/component"
lockfile "github.com/jbenet/go-ipfs/repo/fsrepo/lock" lockfile "github.com/jbenet/go-ipfs/repo/fsrepo/lock"
opener "github.com/jbenet/go-ipfs/repo/fsrepo/opener" opener "github.com/jbenet/go-ipfs/repo/fsrepo/opener"
serialize "github.com/jbenet/go-ipfs/repo/fsrepo/serialize"
debugerror "github.com/jbenet/go-ipfs/util/debugerror" debugerror "github.com/jbenet/go-ipfs/util/debugerror"
) )
...@@ -49,22 +51,21 @@ type FSRepo struct { ...@@ -49,22 +51,21 @@ type FSRepo struct {
// config is loaded when FSRepo is opened and kept up to date when the // config is loaded when FSRepo is opened and kept up to date when the
// FSRepo is modified. // FSRepo is modified.
// TODO test // TODO test
configComponent configComponent configComponent component.ConfigComponent
} }
type component interface { type componentBuilder struct {
Open() error Init component.Initializer
io.Closer IsInitialized component.InitializationChecker
OpenHandler func(*FSRepo) error
} }
type componentInitializationChecker func(path string) bool
// At returns a handle to an FSRepo at the provided |path|. // At returns a handle to an FSRepo at the provided |path|.
func At(repoPath string) *FSRepo { func At(repoPath string) *FSRepo {
// This method must not have side-effects. // This method must not have side-effects.
return &FSRepo{ return &FSRepo{
path: path.Clean(repoPath), path: path.Clean(repoPath),
configComponent: makeConfigComponent(repoPath), state: unopened, // explicitly set for clarity
state: unopened, // explicitly set for clarity
} }
} }
...@@ -78,7 +79,7 @@ func ConfigAt(repoPath string) (*config.Config, error) { ...@@ -78,7 +79,7 @@ func ConfigAt(repoPath string) (*config.Config, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
return load(configFilename) return serialize.Load(configFilename)
} }
// Init initializes a new FSRepo at the given path with the provided config. // Init initializes a new FSRepo at the given path with the provided config.
...@@ -93,10 +94,11 @@ func Init(path string, conf *config.Config) error { ...@@ -93,10 +94,11 @@ func Init(path string, conf *config.Config) error {
if isInitializedUnsynced(path) { if isInitializedUnsynced(path) {
return nil return nil
} }
if err := initConfigComponent(path, conf); err != nil { for _, b := range componentBuilders() {
return err if err := b.Init(path, conf); err != nil {
return err
}
} }
return nil return nil
} }
...@@ -150,21 +152,12 @@ func (r *FSRepo) Open() error { ...@@ -150,21 +152,12 @@ func (r *FSRepo) Open() error {
return err return err
} }
for _, opener := range r.components() { for _, b := range componentBuilders() {
if err := opener.Open(); err != nil { if err := b.OpenHandler(r); err != nil {
return err return err
} }
} }
// datastore
dspath, err := config.DataStorePath("")
if err != nil {
return err
}
if err := initCheckDir(dspath); err != nil {
return debugerror.Errorf("datastore: %s", err)
}
logpath, err := config.LogsPath("") logpath, err := config.LogsPath("")
if err != nil { if err != nil {
return debugerror.Wrap(err) return debugerror.Wrap(err)
...@@ -255,18 +248,7 @@ func IsInitialized(path string) bool { ...@@ -255,18 +248,7 @@ func IsInitialized(path string) bool {
packageLock.Lock() packageLock.Lock()
defer packageLock.Unlock() defer packageLock.Unlock()
// componentInitCheckers are functions that indicate whether the component return isInitializedUnsynced(path)
// is isInitialized
var componentInitCheckers = []componentInitializationChecker{
configComponentIsInitialized,
// TODO add datastore component initialization checker
}
for _, isInitialized := range componentInitCheckers {
if !isInitialized(path) {
return false
}
}
return true
} }
// private methods below this point. NB: packageLock must held by caller. // private methods below this point. NB: packageLock must held by caller.
...@@ -274,7 +256,12 @@ func IsInitialized(path string) bool { ...@@ -274,7 +256,12 @@ func IsInitialized(path string) bool {
// isInitializedUnsynced reports whether the repo is initialized. Caller must // isInitializedUnsynced reports whether the repo is initialized. Caller must
// hold openerCounter lock. // hold openerCounter lock.
func isInitializedUnsynced(path string) bool { func isInitializedUnsynced(path string) bool {
return configComponentIsInitialized(path) for _, b := range componentBuilders() {
if !b.IsInitialized(path) {
return false
}
}
return true
} }
// initCheckDir ensures the directory exists and is writable // initCheckDir ensures the directory exists and is writable
...@@ -327,9 +314,30 @@ func transitionToClosed(r *FSRepo) error { ...@@ -327,9 +314,30 @@ func transitionToClosed(r *FSRepo) error {
} }
// components returns the FSRepo's constituent components // components returns the FSRepo's constituent components
func (r *FSRepo) components() []component { func (r *FSRepo) components() []component.Component {
return []component{ return []component.Component{
&r.configComponent, &r.configComponent,
// TODO add datastore // TODO add datastore
} }
} }
func componentBuilders() []componentBuilder {
return []componentBuilder{
// ConfigComponent
componentBuilder{
Init: component.InitConfigComponent,
IsInitialized: component.ConfigComponentIsInitialized,
OpenHandler: func(r *FSRepo) error {
cc := component.ConfigComponent{Path: r.path}
if err := cc.Open(); err != nil {
return err
}
r.configComponent = cc
return nil
},
},
// TODO add datastore builder
}
}
...@@ -15,8 +15,8 @@ import ( ...@@ -15,8 +15,8 @@ import (
var log = util.Logger("fsrepo") var log = util.Logger("fsrepo")
// readConfigFile reads the config from `filename` into `cfg`. // ReadConfigFile reads the config from `filename` into `cfg`.
func readConfigFile(filename string, cfg interface{}) error { func ReadConfigFile(filename string, cfg interface{}) error {
f, err := os.Open(filename) f, err := os.Open(filename)
if err != nil { if err != nil {
return err return err
...@@ -28,8 +28,8 @@ func readConfigFile(filename string, cfg interface{}) error { ...@@ -28,8 +28,8 @@ func readConfigFile(filename string, cfg interface{}) error {
return nil return nil
} }
// writeConfigFile writes the config from `cfg` into `filename`. // WriteConfigFile writes the config from `cfg` into `filename`.
func writeConfigFile(filename string, cfg interface{}) error { func WriteConfigFile(filename string, cfg interface{}) error {
err := os.MkdirAll(filepath.Dir(filename), 0775) err := os.MkdirAll(filepath.Dir(filename), 0775)
if err != nil { if err != nil {
return err return err
...@@ -55,15 +55,15 @@ func encode(w io.Writer, value interface{}) error { ...@@ -55,15 +55,15 @@ func encode(w io.Writer, value interface{}) error {
return err return err
} }
// load reads given file and returns the read config, or error. // Load reads given file and returns the read config, or error.
func load(filename string) (*config.Config, error) { func Load(filename string) (*config.Config, error) {
// if nothing is there, fail. User must run 'ipfs init' // if nothing is there, fail. User must run 'ipfs init'
if !util.FileExists(filename) { if !util.FileExists(filename) {
return nil, debugerror.New("ipfs not initialized, please run 'ipfs init'") return nil, debugerror.New("ipfs not initialized, please run 'ipfs init'")
} }
var cfg config.Config var cfg config.Config
err := readConfigFile(filename, &cfg) err := ReadConfigFile(filename, &cfg)
if err != nil { if err != nil {
return nil, err return nil, err
} }
......
...@@ -11,11 +11,11 @@ func TestConfig(t *testing.T) { ...@@ -11,11 +11,11 @@ func TestConfig(t *testing.T) {
const dsPath = "/path/to/datastore" const dsPath = "/path/to/datastore"
cfgWritten := new(config.Config) cfgWritten := new(config.Config)
cfgWritten.Datastore.Path = dsPath cfgWritten.Datastore.Path = dsPath
err := writeConfigFile(filename, cfgWritten) err := WriteConfigFile(filename, cfgWritten)
if err != nil { if err != nil {
t.Error(err) t.Error(err)
} }
cfgRead, err := load(filename) cfgRead, err := Load(filename)
if err != nil { if err != nil {
t.Error(err) t.Error(err)
return return
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment