From 9f67ede6b22cfd2845b50f8f6a8b76c06d4cbb89 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Wed, 14 Jan 2015 07:54:18 -0800 Subject: [PATCH] refactor(fsrepo) extract component.Component --- repo/fsrepo/component/component.go | 14 ++++ .../config.go} | 66 +++++++-------- repo/fsrepo/fsrepo.go | 84 ++++++++++--------- repo/fsrepo/{ => serialize}/serialize.go | 14 ++-- repo/fsrepo/{ => serialize}/serialize_test.go | 4 +- 5 files changed, 101 insertions(+), 81 deletions(-) create mode 100644 repo/fsrepo/component/component.go rename repo/fsrepo/{config_component.go => component/config.go} (57%) rename repo/fsrepo/{ => serialize}/serialize.go (77%) rename repo/fsrepo/{ => serialize}/serialize_test.go (84%) diff --git a/repo/fsrepo/component/component.go b/repo/fsrepo/component/component.go new file mode 100644 index 000000000..5b8c63a1b --- /dev/null +++ b/repo/fsrepo/component/component.go @@ -0,0 +1,14 @@ +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 diff --git a/repo/fsrepo/config_component.go b/repo/fsrepo/component/config.go similarity index 57% rename from repo/fsrepo/config_component.go rename to repo/fsrepo/component/config.go index 4ab77c8c4..a21287029 100644 --- a/repo/fsrepo/config_component.go +++ b/repo/fsrepo/component/config.go @@ -1,29 +1,27 @@ -package fsrepo +package component import ( common "github.com/jbenet/go-ipfs/repo/common" config "github.com/jbenet/go-ipfs/repo/config" + serialize "github.com/jbenet/go-ipfs/repo/fsrepo/serialize" util "github.com/jbenet/go-ipfs/util" ) -var _ component = &configComponent{} -var _ componentInitializationChecker = configComponentIsInitialized +var _ Component = &ConfigComponent{} +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. -type configComponent struct { - path string // required at instantiation +// NOT THREAD-SAFE +type ConfigComponent struct { + Path string // required at instantiation config *config.Config // assigned on Open() } -// makeConfigComponent instantiates a valid configComponent. -func makeConfigComponent(path string) configComponent { - return configComponent{path: path} -} - -// fsrepoConfigInit initializes the FSRepo's configComponent. -func initConfigComponent(path string, conf *config.Config) error { - if configComponentIsInitialized(path) { +// fsrepoConfigInit initializes the FSRepo's ConfigComponent. +func InitConfigComponent(path string, conf *config.Config) error { + if ConfigComponentIsInitialized(path) { return nil } configFilename, err := config.Filename(path) @@ -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 // without reading the config from disk and merging any user-provided keys // that may exist. - if err := writeConfigFile(configFilename, conf); err != nil { + if err := serialize.WriteConfigFile(configFilename, conf); err != nil { return err } return nil } // Open returns an error if the config file is not present. -func (c *configComponent) Open() error { - configFilename, err := config.Filename(c.path) +func (c *ConfigComponent) Open() error { + configFilename, err := config.Filename(c.Path) if err != nil { return err } - conf, err := load(configFilename) + conf, err := serialize.Load(configFilename) if err != nil { return err } @@ -54,46 +52,46 @@ func (c *configComponent) Open() error { } // Close satisfies the fsrepoComponent interface. -func (c *configComponent) Close() error { +func (c *ConfigComponent) Close() error { return nil // config doesn't need to be closed. } -func (c *configComponent) Config() *config.Config { +func (c *ConfigComponent) Config() *config.Config { return c.config } // 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) } // GetConfigKey retrieves only the value of a particular key. -func (c *configComponent) GetConfigKey(key string) (interface{}, error) { - filename, err := config.Filename(c.path) +func (c *ConfigComponent) GetConfigKey(key string) (interface{}, error) { + filename, err := config.Filename(c.Path) if err != nil { return nil, err } var cfg map[string]interface{} - if err := readConfigFile(filename, &cfg); err != nil { + if err := serialize.ReadConfigFile(filename, &cfg); err != nil { return nil, err } return common.MapGetKV(cfg, key) } // SetConfigKey writes the value of a particular key. -func (c *configComponent) SetConfigKey(key string, value interface{}) error { - filename, err := config.Filename(c.path) +func (c *ConfigComponent) SetConfigKey(key string, value interface{}) error { + filename, err := config.Filename(c.Path) if err != nil { return err } var mapconf map[string]interface{} - if err := readConfigFile(filename, &mapconf); err != nil { + if err := serialize.ReadConfigFile(filename, &mapconf); err != nil { return err } if err := common.MapSetKV(mapconf, key, value); err != nil { return err } - if err := writeConfigFile(filename, mapconf); err != nil { + if err := serialize.WriteConfigFile(filename, mapconf); err != nil { return err } // 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 { 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|. -func configComponentIsInitialized(path string) bool { +func ConfigComponentIsInitialized(path string) bool { configFilename, err := config.Filename(path) if err != nil { return false @@ -119,8 +117,8 @@ func configComponentIsInitialized(path string) bool { } // setConfigUnsynced is for private use. -func (r *configComponent) setConfigUnsynced(updated *config.Config) error { - configFilename, err := config.Filename(r.path) +func (r *ConfigComponent) setConfigUnsynced(updated *config.Config) error { + configFilename, err := config.Filename(r.Path) if err != nil { return err } @@ -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 // to disk. var mapconf map[string]interface{} - if err := readConfigFile(configFilename, &mapconf); err != nil { + if err := serialize.ReadConfigFile(configFilename, &mapconf); err != nil { return err } m, err := config.ToMap(updated) @@ -138,7 +136,7 @@ func (r *configComponent) setConfigUnsynced(updated *config.Config) error { for k, v := range m { mapconf[k] = v } - if err := writeConfigFile(configFilename, mapconf); err != nil { + if err := serialize.WriteConfigFile(configFilename, mapconf); err != nil { return err } *r.config = *updated // copy so caller cannot modify this private config diff --git a/repo/fsrepo/fsrepo.go b/repo/fsrepo/fsrepo.go index c89820410..3741fbdaf 100644 --- a/repo/fsrepo/fsrepo.go +++ b/repo/fsrepo/fsrepo.go @@ -11,8 +11,10 @@ import ( repo "github.com/jbenet/go-ipfs/repo" 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" 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" ) @@ -49,22 +51,21 @@ type FSRepo struct { // config is loaded when FSRepo is opened and kept up to date when the // FSRepo is modified. // TODO test - configComponent configComponent + configComponent component.ConfigComponent } -type component interface { - Open() error - io.Closer +type componentBuilder struct { + Init component.Initializer + IsInitialized component.InitializationChecker + OpenHandler func(*FSRepo) error } -type componentInitializationChecker func(path string) bool // At returns a handle to an FSRepo at the provided |path|. func At(repoPath string) *FSRepo { // This method must not have side-effects. return &FSRepo{ - path: path.Clean(repoPath), - configComponent: makeConfigComponent(repoPath), - state: unopened, // explicitly set for clarity + path: path.Clean(repoPath), + state: unopened, // explicitly set for clarity } } @@ -78,7 +79,7 @@ func ConfigAt(repoPath string) (*config.Config, error) { if err != nil { return nil, err } - return load(configFilename) + return serialize.Load(configFilename) } // 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 { if isInitializedUnsynced(path) { return nil } - if err := initConfigComponent(path, conf); err != nil { - return err + for _, b := range componentBuilders() { + if err := b.Init(path, conf); err != nil { + return err + } } - return nil } @@ -150,21 +152,12 @@ func (r *FSRepo) Open() error { return err } - for _, opener := range r.components() { - if err := opener.Open(); err != nil { + for _, b := range componentBuilders() { + if err := b.OpenHandler(r); err != nil { 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("") if err != nil { return debugerror.Wrap(err) @@ -255,18 +248,7 @@ func IsInitialized(path string) bool { packageLock.Lock() defer packageLock.Unlock() - // componentInitCheckers are functions that indicate whether the component - // is isInitialized - var componentInitCheckers = []componentInitializationChecker{ - configComponentIsInitialized, - // TODO add datastore component initialization checker - } - for _, isInitialized := range componentInitCheckers { - if !isInitialized(path) { - return false - } - } - return true + return isInitializedUnsynced(path) } // private methods below this point. NB: packageLock must held by caller. @@ -274,7 +256,12 @@ func IsInitialized(path string) bool { // isInitializedUnsynced reports whether the repo is initialized. Caller must // hold openerCounter lock. 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 @@ -327,9 +314,30 @@ func transitionToClosed(r *FSRepo) error { } // components returns the FSRepo's constituent components -func (r *FSRepo) components() []component { - return []component{ +func (r *FSRepo) components() []component.Component { + return []component.Component{ &r.configComponent, // 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 + } +} diff --git a/repo/fsrepo/serialize.go b/repo/fsrepo/serialize/serialize.go similarity index 77% rename from repo/fsrepo/serialize.go rename to repo/fsrepo/serialize/serialize.go index 559c04804..864eb4fc1 100644 --- a/repo/fsrepo/serialize.go +++ b/repo/fsrepo/serialize/serialize.go @@ -15,8 +15,8 @@ import ( var log = util.Logger("fsrepo") -// readConfigFile reads the config from `filename` into `cfg`. -func readConfigFile(filename string, cfg interface{}) error { +// ReadConfigFile reads the config from `filename` into `cfg`. +func ReadConfigFile(filename string, cfg interface{}) error { f, err := os.Open(filename) if err != nil { return err @@ -28,8 +28,8 @@ func readConfigFile(filename string, cfg interface{}) error { return nil } -// writeConfigFile writes the config from `cfg` into `filename`. -func writeConfigFile(filename string, cfg interface{}) error { +// WriteConfigFile writes the config from `cfg` into `filename`. +func WriteConfigFile(filename string, cfg interface{}) error { err := os.MkdirAll(filepath.Dir(filename), 0775) if err != nil { return err @@ -55,15 +55,15 @@ func encode(w io.Writer, value interface{}) error { return err } -// load reads given file and returns the read config, or error. -func load(filename string) (*config.Config, error) { +// Load reads given file and returns the read config, or error. +func Load(filename string) (*config.Config, error) { // if nothing is there, fail. User must run 'ipfs init' if !util.FileExists(filename) { return nil, debugerror.New("ipfs not initialized, please run 'ipfs init'") } var cfg config.Config - err := readConfigFile(filename, &cfg) + err := ReadConfigFile(filename, &cfg) if err != nil { return nil, err } diff --git a/repo/fsrepo/serialize_test.go b/repo/fsrepo/serialize/serialize_test.go similarity index 84% rename from repo/fsrepo/serialize_test.go rename to repo/fsrepo/serialize/serialize_test.go index 9188b54ec..5de9674af 100644 --- a/repo/fsrepo/serialize_test.go +++ b/repo/fsrepo/serialize/serialize_test.go @@ -11,11 +11,11 @@ func TestConfig(t *testing.T) { const dsPath = "/path/to/datastore" cfgWritten := new(config.Config) cfgWritten.Datastore.Path = dsPath - err := writeConfigFile(filename, cfgWritten) + err := WriteConfigFile(filename, cfgWritten) if err != nil { t.Error(err) } - cfgRead, err := load(filename) + cfgRead, err := Load(filename) if err != nil { t.Error(err) return