mirror of
https://github.com/ipfs/kubo.git
synced 2025-09-11 15:15:58 +08:00
refactor(fsrepo) extract component.Component
This commit is contained in:
14
repo/fsrepo/component/component.go
Normal file
14
repo/fsrepo/component/component.go
Normal file
@ -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
|
@ -1,29 +1,27 @@
|
|||||||
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 {
|
|||||||
// 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 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 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 {
|
|||||||
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 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 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 {
|
|||||||
// 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 {
|
|||||||
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 (
|
|||||||
|
|
||||||
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,21 +51,20 @@ 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) {
|
|||||||
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 {
|
|||||||
if isInitializedUnsynced(path) {
|
if isInitializedUnsynced(path) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
if err := initConfigComponent(path, conf); err != nil {
|
for _, b := range componentBuilders() {
|
||||||
|
if err := b.Init(path, conf); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -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 {
|
|||||||
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 {
|
|||||||
// 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 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 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 (
|
|||||||
|
|
||||||
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 {
|
|||||||
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 {
|
|||||||
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) {
|
|||||||
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
|
Reference in New Issue
Block a user