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 (
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
......
......@@ -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,21 +51,20 @@ 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
}
}
......@@ -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 {
for _, b := range componentBuilders() {
if err := b.Init(path, conf); err != nil {
return err
}
}
return nil
}
......@@ -150,19 +152,10 @@ func (r *FSRepo) Open() error {
return err
}
for _, opener := range r.components() {
if err := opener.Open(); err != nil {
return err
}
}
// datastore
dspath, err := config.DataStorePath("")
if err != nil {
for _, b := range componentBuilders() {
if err := b.OpenHandler(r); err != nil {
return err
}
if err := initCheckDir(dspath); err != nil {
return debugerror.Errorf("datastore: %s", err)
}
logpath, err := config.LogsPath("")
......@@ -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
}
}
......@@ -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
}
......
......@@ -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
......
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