diff --git a/CHANGELOG.zh.md b/CHANGELOG.zh.md index d1e5a8033..6f99e7d10 100644 --- a/CHANGELOG.zh.md +++ b/CHANGELOG.zh.md @@ -1,6 +1,8 @@ # Changelog -## 0.10.0-dev +## v0.9.0-rc6 + - damocles-manager + - 简化扇区存储配置 [#1010](https://github.com/ipfs-force-community/damocles/pull/1010) ## v0.9.0-rc5 - damocles-manager diff --git a/damocles-manager/cmd/damocles-manager/internal/util_storage.go b/damocles-manager/cmd/damocles-manager/internal/util_storage.go index 122703285..31dd3610a 100644 --- a/damocles-manager/cmd/damocles-manager/internal/util_storage.go +++ b/damocles-manager/cmd/damocles-manager/internal/util_storage.go @@ -3,6 +3,7 @@ package internal import ( "bytes" "context" + "encoding/json" "errors" "fmt" "os" @@ -17,13 +18,16 @@ import ( "github.com/filecoin-project/go-address" "github.com/filecoin-project/go-state-types/abi" "github.com/filecoin-project/venus/venus-shared/types" + "github.com/google/uuid" "github.com/ipfs-force-community/damocles/damocles-manager/core" "github.com/ipfs-force-community/damocles/damocles-manager/dep" + "github.com/ipfs-force-community/damocles/damocles-manager/modules" "github.com/ipfs-force-community/damocles/damocles-manager/modules/util" "github.com/ipfs-force-community/damocles/damocles-manager/pkg/chain" "github.com/ipfs-force-community/damocles/damocles-manager/pkg/logging" "github.com/ipfs-force-community/damocles/damocles-manager/pkg/objstore" "github.com/ipfs-force-community/damocles/damocles-manager/pkg/objstore/filestore" + "github.com/ipfs-force-community/damocles/damocles-manager/pkg/slices" "github.com/urfave/cli/v2" ) @@ -31,6 +35,7 @@ var utilStorageCmd = &cli.Command{ Name: "storage", Usage: "Manage persistent storage for sealed sectors", Subcommands: []*cli.Command{ + utilStorageGenSectorStoreJSONCmd, utilStorageAttachCmd, utilStorageFindCmd, utilStorageListCmd, @@ -38,6 +43,70 @@ var utilStorageCmd = &cli.Command{ }, } +var utilStorageGenSectorStoreJSONCmd = &cli.Command{ + Name: "gen-sectorstore-json", + Flags: []cli.Flag{ + &cli.BoolFlag{ + Name: "strict", + Value: true, + }, + &cli.BoolFlag{ + Name: "readonly", + Aliases: []string{"read-only"}, + Value: false, + }, + &cli.UintFlag{ + Name: "weight", + Value: 1, + }, + &cli.StringFlag{ + Name: "plugin-name", + Aliases: []string{"plugin"}, + }, + &cli.Uint64SliceFlag{ + Name: "allow-miners", + }, + &cli.Uint64SliceFlag{ + Name: "deny-miners", + }, + }, + Usage: "generate the `sectorstore.json` file", + ArgsUsage: "[sectorstore.json path]", + Action: func(cctx *cli.Context) error { + cfg := modules.SectorStoreJSON{ + ID: uuid.New().String(), + PersistStoreConfig: modules.PersistStoreConfig{ + Config: objstore.Config{ + Strict: cctx.Bool("strict"), + ReadOnly: cctx.Bool("readonly"), + Weight: cctx.Uint("weight"), + }, + StoreSelectPolicy: objstore.StoreSelectPolicy{ + AllowMiners: slices.Map(cctx.Uint64Slice("allow-miners"), func(x uint64) abi.ActorID { return abi.ActorID(x) }), + DenyMiners: slices.Map(cctx.Uint64Slice("deny-miners"), func(x uint64) abi.ActorID { return abi.ActorID(x) }), + }, + PluginName: cctx.String("plugin-name"), + }, + } + + b, err := json.MarshalIndent(cfg, "", "\t") + if err != nil { + return fmt.Errorf("failed to marshal config: %w", err) + } + + targetPath := cctx.Args().First() + if targetPath == "" { + fmt.Println(string(b)) + return nil + } + + if filepath.Base(targetPath) != modules.FilenameSectorStoreJSON { + targetPath = filepath.Join(targetPath, modules.FilenameSectorStoreJSON) + } + return os.WriteFile(targetPath, b, 0644) + }, +} + var utilStorageAttachCmd = &cli.Command{ Name: "attach", Usage: "Attach local storage path and import sectors in this path", @@ -88,7 +157,7 @@ var utilStorageAttachCmd = &cli.Command{ scfg := objstore.DefaultConfig(abs, readOnly) scfg.Name = name - scfg.Strict = &strict + scfg.Strict = strict store, err := filestore.Open(scfg, false) if err != nil { diff --git a/damocles-manager/dep/sealer_constructor.go b/damocles-manager/dep/sealer_constructor.go index 33a93a93e..eba3811da 100644 --- a/damocles-manager/dep/sealer_constructor.go +++ b/damocles-manager/dep/sealer_constructor.go @@ -466,11 +466,20 @@ func openObjStore(cfg objstore.Config, pluginName string, loadedPlugins *manager } func BuildPersistedFileStoreMgr(scfg *modules.SafeConfig, globalStore CommonMetaStore, loadedPlugins *managerplugin.LoadedPlugins) (PersistedObjectStoreManager, error) { - persistCfg := scfg.MustCommonConfig().GetPersistStores() + persistCfg, err := scfg.MustCommonConfig().GetPersistStores() + if err != nil { + return nil, fmt.Errorf("get persist store config: %w", err) + } stores := make([]objstore.Store, 0, len(persistCfg)) policy := map[string]objstore.StoreSelectPolicy{} + checkName := make(map[string]struct{}) for pi := range persistCfg { + if _, ok := checkName[persistCfg[pi].Name]; ok { + return nil, fmt.Errorf("duplicate persist store name %s", persistCfg[pi].Name) + } + checkName[persistCfg[pi].Name] = struct{}{} + // For compatibility with v0.5 if persistCfg[pi].PluginName == "" && persistCfg[pi].Plugin != "" { persistCfg[pi].PluginName = persistCfg[pi].Plugin @@ -575,7 +584,7 @@ func BuildMarketAPIRelated(gctx GlobalContext, lc fx.Lifecycle, scfg *modules.Sa Name: pcfg.Name, Path: pcfg.Path, Meta: pcfg.Meta, - ReadOnly: &pcfg.ReadOnly, + ReadOnly: pcfg.ReadOnly, } // For compatibility with v0.5 if pcfg.PluginName == "" && pcfg.Plugin != "" { diff --git a/damocles-manager/go.mod b/damocles-manager/go.mod index 8a8cd2cbb..99c2f241a 100644 --- a/damocles-manager/go.mod +++ b/damocles-manager/go.mod @@ -24,9 +24,10 @@ require ( github.com/filecoin-project/specs-storage v0.4.1 github.com/filecoin-project/venus v1.14.0-rc4 github.com/golang/mock v1.6.0 + github.com/google/uuid v1.3.0 github.com/hako/durafmt v0.0.0-20200710122514-c0fb7b4da026 github.com/hashicorp/go-multierror v1.1.1 - github.com/ipfs-force-community/damocles/manager-plugin v0.0.0-20230830062024-608c68ada10e + github.com/ipfs-force-community/damocles/manager-plugin v0.0.0-20231108073455-ac8eebc7d237 github.com/ipfs-force-community/venus-cluster-assets v0.1.0 github.com/ipfs/boxo v0.10.1 github.com/ipfs/go-cid v0.4.1 @@ -114,7 +115,6 @@ require ( github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/protobuf v1.5.3 // indirect github.com/golang/snappy v0.0.4 // indirect - github.com/google/uuid v1.3.0 // indirect github.com/gorilla/mux v1.8.0 // indirect github.com/gorilla/websocket v1.5.0 // indirect github.com/hannahhoward/cbor-gen-for v0.0.0-20230214144701-5d17c9d5243c // indirect diff --git a/damocles-manager/go.sum b/damocles-manager/go.sum index 103a67b17..856ef702e 100644 --- a/damocles-manager/go.sum +++ b/damocles-manager/go.sum @@ -573,8 +573,8 @@ github.com/icza/mighty v0.0.0-20180919140131-cfd07d671de6 h1:8UsGZ2rr2ksmEru6lTo github.com/icza/mighty v0.0.0-20180919140131-cfd07d671de6/go.mod h1:xQig96I1VNBDIWGCdTt54nHt6EeI639SmHycLYL7FkA= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo= -github.com/ipfs-force-community/damocles/manager-plugin v0.0.0-20230830062024-608c68ada10e h1:SEmUD7xCpHWlnTrZdyj++RExTy0T6bqX4yS/iXBHVAg= -github.com/ipfs-force-community/damocles/manager-plugin v0.0.0-20230830062024-608c68ada10e/go.mod h1:me1u2cl7qdxBCZiVL0laDop8uBHDdUwlUNnQ7KkHF64= +github.com/ipfs-force-community/damocles/manager-plugin v0.0.0-20231108073455-ac8eebc7d237 h1:yNvF1C/Qgt9p38wQfhJQ7PRnbwEBs8vSpNFxnWDlhu8= +github.com/ipfs-force-community/damocles/manager-plugin v0.0.0-20231108073455-ac8eebc7d237/go.mod h1:EpGeK7b251iv7L5TnHl1PJGFH4KbliE03ctYt5thy6c= github.com/ipfs-force-community/go-jsonrpc v0.1.8 h1:w7CWlLveL+aXD3gLg8Z7I1RcktCiMY0sp8dgJG37uWE= github.com/ipfs-force-community/go-jsonrpc v0.1.8/go.mod h1:jBSvPTl8V1N7gSTuCR4bis8wnQnIjHbRPpROol6iQKM= github.com/ipfs-force-community/venus-cluster-assets v0.1.0 h1:K/0+OV9Jm7HjSa7O9MAtgfLDIudQYZUTymhJsp8rGXg= diff --git a/damocles-manager/modules/config.go b/damocles-manager/modules/config.go index 73365aa36..c98118331 100644 --- a/damocles-manager/modules/config.go +++ b/damocles-manager/modules/config.go @@ -2,9 +2,7 @@ package modules import ( "bytes" - "encoding/json" "fmt" - "os" "sync" "time" @@ -159,28 +157,6 @@ type PersistStoreConfig struct { PluginName string } -type PieceStorePreset struct { - Meta map[string]string - Strict *bool - ReadOnly *bool - Weight *uint - - AllowMiners []abi.ActorID - DenyMiners []abi.ActorID - - // compatibility for storage.json with lotus - StorageConfigPath *string -} - -type StoragePathConfig struct { - StoragePaths []LocalPath -} - -type LocalPath struct { - Name string - Path string -} - type ProvingConfig struct { // Maximum number of sector checks to run in parallel. (0 = unlimited) // @@ -239,133 +215,44 @@ func DefaultWorkerProverConfig() *WorkerProverConfig { } type CommonConfig struct { - API CommonAPIConfig - Plugins *PluginConfig - PieceStores []PieceStoreConfig - PieceStorePreset PieceStorePreset + API CommonAPIConfig + Plugins *PluginConfig + PieceStores []PieceStoreConfig // PersistStores should not be used directly, use GetPersistStores instead PersistStores []PersistStoreConfig - MongoKVStore *KVStoreMongoDBConfig // For compatibility with v0.5 - DB *DBConfig - Proving ProvingConfig -} + // Configure the storage directory for scanning the directory that contains `sectorstore.json` file, supporting `glob` mode. + ScanPersistStores []string -func (c CommonConfig) GetPersistStores() []PersistStoreConfig { - // apply preset - preset := c.PieceStorePreset - ret := make([]PersistStoreConfig, 0, len(c.PersistStores)) + MongoKVStore *KVStoreMongoDBConfig // For compatibility with v0.5 + DB *DBConfig + Proving ProvingConfig +} - // fill preset with default values if not set - if preset.Strict == nil { - preset.Strict = new(bool) - *preset.Strict = false - } - if preset.ReadOnly == nil { - preset.ReadOnly = new(bool) - *preset.ReadOnly = false - } - if preset.Weight == nil { - preset.Weight = new(uint) - *preset.Weight = 1 - } - if preset.Meta == nil { - preset.Meta = make(map[string]string) - } - if preset.AllowMiners == nil { - preset.AllowMiners = make([]abi.ActorID, 0) - } - if preset.DenyMiners == nil { - preset.DenyMiners = make([]abi.ActorID, 0) - } +func (c CommonConfig) GetPersistStores() (cfgs []PersistStoreConfig, err error) { + cfgs = make([]PersistStoreConfig, len(c.PersistStores)) + copy(cfgs, c.PersistStores) - for i := range c.PersistStores { - ps := c.PersistStores[i] - if ps.Strict == nil { - ps.Strict = preset.Strict - } - if ps.ReadOnly == nil { - ps.ReadOnly = preset.ReadOnly - } - if ps.Weight == nil { - ps.Weight = preset.Weight - } - mergeMapInto[string, string](preset.Meta, ps.Meta) - mergeSliceInto[abi.ActorID](preset.AllowMiners, ps.AllowMiners) - mergeSliceInto[abi.ActorID](preset.DenyMiners, ps.DenyMiners) - - ret = append(ret, ps) - } - - if preset.StorageConfigPath != nil { - p := *preset.StorageConfigPath - if p != "" { - cfg := StoragePathConfig{} - file, err := os.Open(p) - if err != nil { - log.Errorf("open storage config file %s failed: %s", p, err) - } else { - defer file.Close() - err := json.NewDecoder(file).Decode(&cfg) - if err != nil { - log.Errorf("decode storage config file %s failed: %s", p, err) - } else { - for _, lp := range cfg.StoragePaths { - psc := PersistStoreConfig{ - Config: objstore.Config{ - Path: lp.Path, - Meta: preset.Meta, - Strict: preset.Strict, - ReadOnly: preset.ReadOnly, - Weight: preset.Weight, - }, - StoreSelectPolicy: objstore.StoreSelectPolicy{ - AllowMiners: preset.AllowMiners, - DenyMiners: preset.DenyMiners, - }, - } - if lp.Name != "" { - psc.Name = lp.Name - } - ret = append(ret, psc) - } - log.Infof("load storage config file %s success", p) - } - } + if len(c.ScanPersistStores) != 0 { + var scanned []PersistStoreConfig + scanned, err = ScanPersistStores(c.ScanPersistStores) + if err != nil { + return } + cfgs = append(cfgs, scanned...) } - return ret -} -func mergeSliceInto[T comparable](from, into []T) []T { - if len(from) == 0 { - return into - } - has := make(map[T]struct{}) - for _, m := range into { - has[m] = struct{}{} - } - for _, m := range from { - if _, ok := has[m]; !ok { - into = append(into, m) - } - } - return into + return } -func mergeMapInto[T comparable, V any](from, into map[T]V) map[T]V { - if len(from) == 0 { - return into - } - if into == nil { - into = make(map[T]V) - } - for k, v := range from { - if _, ok := into[k]; !ok { - into[k] = v - } - } - return into +// [path]/sectorstore.json +type SectorStoreJSON struct { + // `ID` is the name of the storage + // Equivalent to `Name` + // `ID` is for compatibility with lotus + ID string + + PersistStoreConfig } func exampleFilestoreConfig() objstore.Config { @@ -377,12 +264,13 @@ func exampleFilestoreConfig() objstore.Config { func defaultCommonConfig(example bool) CommonConfig { cfg := CommonConfig{ - API: defaultCommonAPIConfig(example), - PieceStores: []PieceStoreConfig{}, - PersistStores: []PersistStoreConfig{}, - MongoKVStore: nil, - DB: DefaultDBConfig(), - Proving: defaultProvingConfig(), + API: defaultCommonAPIConfig(example), + PieceStores: []PieceStoreConfig{}, + PersistStores: []PersistStoreConfig{}, + ScanPersistStores: []string{}, + MongoKVStore: nil, + DB: DefaultDBConfig(), + Proving: defaultProvingConfig(), } if example { @@ -394,20 +282,6 @@ func defaultCommonConfig(example bool) CommonConfig { PluginName: "s3store", }) - cfg.PieceStorePreset = PieceStorePreset{ - Meta: map[string]string{"SomeKey": "SomeValue"}, - Strict: new(bool), - ReadOnly: new(bool), - Weight: new(uint), - - AllowMiners: []abi.ActorID{1, 2}, - DenyMiners: []abi.ActorID{3, 4}, - - StorageConfigPath: new(string), - } - *cfg.PieceStorePreset.Weight = 1 - *cfg.PieceStorePreset.StorageConfigPath = "/optional/path/to/your/storage.json" - cfg.PersistStores = append(cfg.PersistStores, PersistStoreConfig{ Config: objstore.Config{ Name: exampleCfg.Name, diff --git a/damocles-manager/modules/config_util.go b/damocles-manager/modules/config_util.go index ca4f915b4..5646643db 100644 --- a/damocles-manager/modules/config_util.go +++ b/damocles-manager/modules/config_util.go @@ -3,9 +3,12 @@ package modules import ( "bytes" "encoding" + "encoding/json" "fmt" "math" mbig "math/big" + "os" + "path/filepath" "strconv" "strings" "time" @@ -220,3 +223,42 @@ func (f *FIL) UnmarshalJSON(text []byte) error { } return f.UnmarshalText(text) } + +const FilenameSectorStoreJSON = "sectorstore.json" + +func ScanPersistStores(patterns []string) ([]PersistStoreConfig, error) { + var stores []PersistStoreConfig + for _, pattern := range patterns { + + matches, err := filepath.Glob(pattern) + if err != nil { + return nil, fmt.Errorf("glob pattern `%s`: %w", pattern, err) + } + + for _, path := range matches { + sectorStoreJSONFile := filepath.Join(path, FilenameSectorStoreJSON) + b, err := os.ReadFile(sectorStoreJSONFile) + if os.IsNotExist(err) { + continue + } + if err != nil { + return nil, fmt.Errorf("read `%s`: %w", sectorStoreJSONFile, err) + } + + var conf SectorStoreJSON + if err := json.Unmarshal(b, &conf); err != nil { + return nil, fmt.Errorf("unmarshal json file `%s`: %w", sectorStoreJSONFile, err) + } + + if conf.Name == "" { + conf.Name = conf.ID + } + + conf.Path = path + log.Infof("scanned persist store: %s, path: %s", conf.Name, conf.Path) + stores = append(stores, conf.PersistStoreConfig) + } + } + + return stores, nil +} diff --git a/damocles-manager/modules/config_util_test.go b/damocles-manager/modules/config_util_test.go index 81678f19f..58c40205b 100644 --- a/damocles-manager/modules/config_util_test.go +++ b/damocles-manager/modules/config_util_test.go @@ -3,6 +3,8 @@ package modules_test import ( "bytes" "fmt" + "os" + "path/filepath" "testing" "github.com/BurntSushi/toml" @@ -74,7 +76,7 @@ func TestStructWithNilField(t *testing.T) { enc.Indent = "" err := enc.Encode(a) require.NoError(t, err) - fmt.Println(string(buf.Bytes())) + fmt.Println(buf.String()) } @@ -180,3 +182,44 @@ func TestParseFIL(t *testing.T) { require.False(t, modules.OneFIL.IsZero()) }) } + +func TestScanPersistStores(t *testing.T) { + tmpDir := t.TempDir() + + require.NoError(t, os.Mkdir(filepath.Join(tmpDir, "1"), 0755)) + require.NoError(t, os.Mkdir(filepath.Join(tmpDir, "2"), 0755)) + require.NoError(t, os.Mkdir(filepath.Join(tmpDir, "3"), 0755)) + + jsonConf1 := `{ + "ID": "123", + "Name": "", + "Strict": false, + "ReadOnly": false, + "Weight": 0, + "AllowMiners": [1], + "DenyMiners": [2], + "PluginName": "2234234", + "Meta": {}, + "CanSeal": true + }` + require.NoError(t, os.WriteFile(filepath.Join(tmpDir, "1", modules.FilenameSectorStoreJSON), []byte(jsonConf1), 0644)) + + jsonConf2 := `{ + "Name": "456", + "Meta": {}, + "Strict": true, + "ReadOnly": false, + "AllowMiners": [1], + "DenyMiners": [2], + "PluginName": "2234234", + "CanSeal": true + }` + require.NoError(t, os.WriteFile(filepath.Join(tmpDir, "2", modules.FilenameSectorStoreJSON), []byte(jsonConf2), 0644)) + + cfgs, err := modules.ScanPersistStores([]string{filepath.Join(tmpDir, "*")}) + require.NoError(t, err) + require.Len(t, cfgs, 2) + + require.Equal(t, "123", cfgs[0].Name) + require.Equal(t, "456", cfgs[1].Name) +} diff --git a/damocles-manager/modules/sealer/sealer_cli.go b/damocles-manager/modules/sealer/sealer_cli.go index 14bd2b6c7..1c1ef2f61 100644 --- a/damocles-manager/modules/sealer/sealer_cli.go +++ b/damocles-manager/modules/sealer/sealer_cli.go @@ -360,9 +360,9 @@ func storeConfig2StoreBasic(ocfg *objstore.Config) core.StoreBasicInfo { Name: ocfg.Name, Path: ocfg.Path, Meta: ocfg.Meta, - Strict: ocfg.GetStrict(), - ReadOnly: ocfg.GetReadOnly(), - Weight: ocfg.GetWeight(), + Strict: ocfg.Strict, + ReadOnly: ocfg.ReadOnly, + Weight: ocfg.Weight, } } diff --git a/damocles-manager/pkg/objstore/filestore/store.go b/damocles-manager/pkg/objstore/filestore/store.go index 733769514..daada9418 100644 --- a/damocles-manager/pkg/objstore/filestore/store.go +++ b/damocles-manager/pkg/objstore/filestore/store.go @@ -137,7 +137,7 @@ func (s *Store) open(p string, r *readRange) (io.ReadCloser, error) { } }() - if s.cfg.GetStrict() { + if s.cfg.Strict { stat, err := file.Stat() if err != nil { return nil, fmt.Errorf("obj %s: get stat: %w", p, err) @@ -211,7 +211,7 @@ func (s *Store) Get(ctx context.Context, p string) (io.ReadCloser, error) { } func (s *Store) Del(_ context.Context, p string) error { - if s.cfg.GetReadOnly() { + if s.cfg.ReadOnly { return objstore.ErrReadOnlyStore } @@ -268,7 +268,7 @@ func (s *Store) getAbsPath(p string) (string, error) { } func (s *Store) Put(_ context.Context, p string, r io.Reader) (int64, error) { - if s.cfg.GetReadOnly() { + if s.cfg.ReadOnly { return 0, objstore.ErrReadOnlyStore } diff --git a/damocles-manager/pkg/objstore/mgr.go b/damocles-manager/pkg/objstore/mgr.go index 887ae9a1c..8cf6362fc 100644 --- a/damocles-manager/pkg/objstore/mgr.go +++ b/damocles-manager/pkg/objstore/mgr.go @@ -208,11 +208,11 @@ func (m *StoreManager) ReserveSpace(ctx context.Context, sid abi.SectorID, size } // readonly, or not enough space - if info.Config.GetReadOnly() || info.Free < resSize+size { + if info.Config.ReadOnly || info.Free < resSize+size { continue } - weight := info.Config.GetWeight() + weight := info.Config.Weight if weight == 0 { weight = 1 } diff --git a/damocles-manager/pkg/objstore/mgr_test.go b/damocles-manager/pkg/objstore/mgr_test.go index 897d400a1..a269e8be1 100644 --- a/damocles-manager/pkg/objstore/mgr_test.go +++ b/damocles-manager/pkg/objstore/mgr_test.go @@ -10,11 +10,6 @@ import ( "github.com/stretchr/testify/require" ) -var ( - TRUE = true - THOUNSAND = uint(1000) -) - func (m *StoreManager) resetReserved(ctx context.Context) error { err := m.metadb.Del(ctx, storeReserveSummaryKey) if err != nil { @@ -43,7 +38,7 @@ func TestStoreManagerReserverSpace(t *testing.T) { storeRO, err := NewMockStore(Config{ Name: storeNameReadOnly, - ReadOnly: &TRUE, + ReadOnly: true, }, 1<<30) require.NoError(t, err, "construct store-RO") @@ -199,13 +194,13 @@ func TestStoreManagerReserverSpaceWeighed(t *testing.T) { store1K, err := NewMockStore(Config{ Name: storeName1K, - Weight: &THOUNSAND, + Weight: 1000, }, 1<<20) require.NoError(t, err, "construct store-1K") storeRO, err := NewMockStore(Config{ Name: storeNameReadOnly, - ReadOnly: &TRUE, + ReadOnly: true, }, 1<<30) require.NoError(t, err, "construct store-RO") diff --git a/damocles-manager/pkg/objstore/mock_store.go b/damocles-manager/pkg/objstore/mock_store.go index ef1d486bb..b0607213f 100644 --- a/damocles-manager/pkg/objstore/mock_store.go +++ b/damocles-manager/pkg/objstore/mock_store.go @@ -81,7 +81,7 @@ func (ms *MockStore) Get(_ context.Context, p string) (io.ReadCloser, error) { } func (ms *MockStore) Del(_ context.Context, p string) error { - if ms.cfg.GetReadOnly() { + if ms.cfg.ReadOnly { return ErrReadOnlyStore } @@ -114,7 +114,7 @@ func (ms *MockStore) Stat(_ context.Context, p string) (Stat, error) { } func (ms *MockStore) Put(_ context.Context, p string, r io.Reader) (int64, error) { - if ms.cfg.GetReadOnly() { + if ms.cfg.ReadOnly { return 0, ErrReadOnlyStore } diff --git a/damocles-manager/pkg/piecestore/proxy.go b/damocles-manager/pkg/piecestore/proxy.go index d0c40d712..c0374806c 100644 --- a/damocles-manager/pkg/piecestore/proxy.go +++ b/damocles-manager/pkg/piecestore/proxy.go @@ -91,7 +91,7 @@ func (p *Proxy) handlePut(rw http.ResponseWriter, req *http.Request) { continue } - if storeInfo.Config.GetReadOnly() { + if storeInfo.Config.ReadOnly { continue } @@ -131,7 +131,7 @@ func (p *Proxy) Put(ctx context.Context, pieceCid cid.Cid, data io.Reader) (int6 continue } - if storeInfo.Config.GetReadOnly() { + if storeInfo.Config.ReadOnly { continue } diff --git a/damocles-manager/ver/ver.go b/damocles-manager/ver/ver.go index a90870494..664f4c2d2 100644 --- a/damocles-manager/ver/ver.go +++ b/damocles-manager/ver/ver.go @@ -2,7 +2,7 @@ package ver import "fmt" -const Version = "0.9.0-rc5" +const Version = "0.9.0-rc6" var Commit string diff --git a/damocles-worker/Cargo.lock b/damocles-worker/Cargo.lock index edb5b78ce..73bcc13c3 100644 --- a/damocles-worker/Cargo.lock +++ b/damocles-worker/Cargo.lock @@ -1002,7 +1002,7 @@ dependencies = [ [[package]] name = "damocles-worker" -version = "0.9.0-rc5" +version = "0.9.0-rc6" dependencies = [ "anyhow", "base64 0.13.1", diff --git a/damocles-worker/Cargo.toml b/damocles-worker/Cargo.toml index 1808abc06..b09922b46 100644 --- a/damocles-worker/Cargo.toml +++ b/damocles-worker/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "damocles-worker" -version = "0.9.0-rc5" +version = "0.9.0-rc6" authors = ["dtynn "] edition = "2021" exclude = [".github"] diff --git a/damocles-worker/assets/damocles-worker.mock.toml b/damocles-worker/assets/damocles-worker.mock.toml index 206d1a76b..e0db9e0dc 100644 --- a/damocles-worker/assets/damocles-worker.mock.toml +++ b/damocles-worker/assets/damocles-worker.mock.toml @@ -2,6 +2,7 @@ # name = "worker-#1" # rpc_server.host = "192.168.1.100" # rpc_server.port = 17891 +scan_persist_stores = [] [metrics] enable = true diff --git a/damocles-worker/src/config.rs b/damocles-worker/src/config.rs index 022abc0ca..1dbc7cd85 100644 --- a/damocles-worker/src/config.rs +++ b/damocles-worker/src/config.rs @@ -1,16 +1,18 @@ //! config for damocles-worker -use std::collections::HashMap; -use std::fs; +use std::collections::{HashMap, HashSet}; +use std::fs::{self, File}; +use std::io; use std::net::SocketAddr; use std::path::{Path, PathBuf}; use std::str; use std::time::Duration; -use anyhow::{Context, Result}; +use anyhow::{anyhow, Context, Result}; use byte_unit::Byte; use serde::{Deserialize, Serialize}; +use crate::objstore::attached; use crate::sealing::processor::external::config::Ext; /// Default worker server port @@ -20,6 +22,8 @@ pub const DEFAULT_WORKER_SERVER_HOST: &str = "0.0.0.0"; pub const LOCAL_HOST: &str = "127.0.0.1"; pub const DEFAULT_WORKER_PING_INTERVAL: Duration = Duration::from_secs(30); +pub const FILENAME_SECTORSTORE_JSON: &str = "sectorstore.json"; + /// configurations for sealing sectors #[derive(Debug, Clone, PartialEq, Eq)] pub struct Sealing { @@ -126,7 +130,7 @@ pub struct SealingOptional { } /// configuration for remote store -#[derive(Debug, Default, Serialize, Deserialize)] +#[derive(Debug, Default, Serialize, Deserialize, Clone)] pub struct Attached { pub name: Option, /// store path, if we are using fs based store @@ -135,6 +139,12 @@ pub struct Attached { pub readonly: Option, } +impl Attached { + pub fn name(&self) -> &String { + self.name.as_ref().unwrap_or(&self.location) + } +} + /// configurations for local sealing store #[derive(Debug, Default, Serialize, Deserialize)] pub struct SealingThread { @@ -245,6 +255,11 @@ pub struct WorkerInstanceConfig { /// otherwise it will load the remote piece file from damocles-manager pub local_pieces_dir: Option, // For compatibility pub local_pieces_dirs: Option>, + + /// Configure directories for scanning persistence store + #[serde(default)] + #[serde(alias = "ScanPersistStores")] + pub scan_persist_stores: Vec, } #[derive(Debug, Default, Serialize, Deserialize)] @@ -280,9 +295,6 @@ pub struct Config { /// section for list of local sealing stores pub sealing_thread: Vec, - /// section for remote store, deprecated - pub remote_store: Option, - /// section for attached store pub attached: Option>, @@ -385,4 +397,152 @@ impl Config { writeln!(&mut buf, "{:#?}", self)?; Ok(String::from_utf8(buf)?) } + + /// attached returns all attached persist stores + pub fn attached(&self) -> Result> { + let mut attached = self.attached.clone().unwrap_or(Vec::new()); + if let Some(worker) = &self.worker { + if !worker.scan_persist_stores.is_empty() { + attached.extend( + scan_persist_stores(&worker.scan_persist_stores) + .context("scan persist stores")?, + ); + } + } + + let mut unique_names = HashSet::new(); + for x in &attached { + let name = x.name(); + if unique_names.contains(name) { + return Err(anyhow!("duplicate persist name: {}", name)); + } + unique_names.insert(name.clone()); + } + Ok(attached) + } +} + +#[derive(Debug, Default, Serialize, Deserialize)] +#[serde(rename_all = "PascalCase")] +struct SectorStoreJson { + #[serde(rename = "ID")] + pub id: Option, + pub name: Option, + pub read_only: Option, +} + +pub fn scan_persist_stores(patterns: &[String]) -> Result> { + let mut attached = Vec::new(); + for pattern in patterns { + for path in glob::glob(pattern) + .with_context(|| format!("invalid glob pattern: {}", pattern))? + .filter_map(Result::ok) + { + let cfg_path = path.join(FILENAME_SECTORSTORE_JSON); + match fs::metadata(&cfg_path) { + Ok(m) => { + if !m.is_file() { + continue; + } + } + Err(e) if e.kind() == io::ErrorKind::NotFound => { + continue; + } + Err(e) => { + return Err(anyhow!( + "open file {}: {:?}", + path.display(), + e + )); + } + } + + let f = File::open(&cfg_path).with_context(|| { + format!("open file: {}", cfg_path.display()) + })?; + let cfg: SectorStoreJson = serde_json::from_reader(f) + .with_context(|| { + format!("deserialize json: {}", cfg_path.display()) + })?; + + let name = match (cfg.name, cfg.id) { + (Some(name), _) if !name.is_empty() => Some(name), + (_, Some(id)) if !id.is_empty() => Some(id), + _ => None, + }; + + tracing::info!( + "scanned persist store: {}, path: {}", + name.as_deref().unwrap_or("None"), + path.display() + ); + + attached.push(Attached { + name, + location: path.display().to_string(), + readonly: cfg.read_only, + }) + } + } + Ok(attached) +} + +#[cfg(test)] +mod tests { + use std::fs; + + use pretty_assertions::assert_eq; + + use super::FILENAME_SECTORSTORE_JSON; + + #[test] + fn test_scan_persist_stores() { + let tmpdir = tempfile::tempdir().unwrap(); + + fs::create_dir(tmpdir.path().join("1")).unwrap(); + fs::create_dir(tmpdir.path().join("2")).unwrap(); + fs::create_dir(tmpdir.path().join("3")).unwrap(); + + fs::write( + tmpdir.path().join("1").join(FILENAME_SECTORSTORE_JSON), + r#"{ + "ID": "123", + "Name": "", + "Strict": false, + "ReadOnly": false, + "Weight": 0, + "AllowMiners": [1], + "DenyMiners": [2], + "PluginName": "2234234", + "Meta": {}, + "CanSeal": true + }"#, + ) + .unwrap(); + + fs::write( + tmpdir.path().join("2").join(FILENAME_SECTORSTORE_JSON), + r#"{ + "Name": "456", + "Meta": {}, + "Strict": true, + "ReadOnly": false, + "AllowMiners": [1], + "DenyMiners": [2], + "PluginName": "2234234", + "CanSeal": true + }"#, + ) + .unwrap(); + + let attached = super::scan_persist_stores(&[tmpdir + .path() + .join("*") + .display() + .to_string()]) + .unwrap(); + assert_eq!(attached.len(), 2); + assert_eq!(attached[0].name, Some("123".to_string())); + assert_eq!(attached[1].name, Some("456".to_string())); + } } diff --git a/damocles-worker/src/run.rs b/damocles-worker/src/run.rs index adddd91ba..acd916c8b 100644 --- a/damocles-worker/src/run.rs +++ b/damocles-worker/src/run.rs @@ -65,55 +65,24 @@ pub fn start_daemon(cfg_path: impl AsRef) -> Result<()> { .map_err(|e| anyhow!("jsonrpc connect to {}: {:?}", &dial_addr, e))?; let mut attached: Vec> = Vec::new(); - let mut attached_writable = 0; - if let Some(remote_cfg) = cfg.remote_store.as_ref() { - let remote_store = Box::new( + + for (sidx, scfg) in cfg.attached()?.into_iter().enumerate() { + let attached_store = Box::new( FileStore::open( - remote_cfg.location.clone(), - remote_cfg.name.clone(), - remote_cfg.readonly.unwrap_or(false), + scfg.location, + scfg.name, + scfg.readonly.unwrap_or(false), ) - .with_context(|| { - format!("open remote filestore {}", remote_cfg.location) - })?, + .with_context(|| format!("open attached filestore #{}", sidx))?, ); - if !remote_store.readonly() { - attached_writable += 1; - } - - attached.push(remote_store); - } - - if let Some(attach_cfgs) = cfg.attached.as_ref() { - for (sidx, scfg) in attach_cfgs.iter().enumerate() { - let attached_store = Box::new( - FileStore::open( - scfg.location.clone(), - scfg.name.clone(), - scfg.readonly.unwrap_or(false), - ) - .with_context(|| { - format!("open attached filestore #{}", sidx) - })?, - ); - - if !attached_store.readonly() { - attached_writable += 1; - } - - attached.push(attached_store); - } + attached.push(attached_store); } if attached.is_empty() { return Err(anyhow!("no attached store available")); } - if attached_writable == 0 { - return Err(anyhow!("no attached store available for writing")); - } - // check all persist store exist in damocles-manager for st in attached.iter() { let ins_name = st.instance(); @@ -135,11 +104,7 @@ pub fn start_daemon(cfg_path: impl AsRef) -> Result<()> { } } - info!( - "{} stores attached, {} writable", - attached.len(), - attached_writable - ); + info!("{} stores attached", attached.len(),); let attached_mgr = AttachedManager::init(attached).context("init attached manager")?; diff --git a/docs/en/03.damocles-worker-config.md b/docs/en/03.damocles-worker-config.md index 2610eb320..2101fe8f2 100644 --- a/docs/en/03.damocles-worker-config.md +++ b/docs/en/03.damocles-worker-config.md @@ -11,6 +11,7 @@ Taking a mock instance as an example, a basic configuration might look like this # name = "worker-#1" # rpc_server.host = "192.168.1.100" # rpc_server.port = 17891 +# scan_persist_stores = [] [metrics] #enable = false @@ -151,6 +152,12 @@ The `worker` configuration item is used to configure some basic information of t # otherwise it will load the remote piece file from damocles-manager # If the file "/path/to/{your_local_pieces_dir01, your_local_pieces_dir02, ...}/piece_file_name" does not exist, the worker will also load it from the damocles-manager # local_pieces_dirs = ["/path/to/your_local_pieces_dir01", "/path/to/your_local_pieces_dir02"] + + +# The paths to scan persistent store, string array type, optional +# Default is empty array +# Support glob mode, ScanPersistStores is equivalent to the scan_persist_stores. +# ScanPersistStores = ["/filecoin/*", "/store1"] ``` In most cases, each field in this configuration item does not need to be manually configured. diff --git a/docs/en/04.damocles-manager-config.md b/docs/en/04.damocles-manager-config.md index a1f7cab7e..73fe2ff00 100644 --- a/docs/en/04.damocles-manager-config.md +++ b/docs/en/04.damocles-manager-config.md @@ -7,6 +7,7 @@ After initialization, we can get a copy of the default configuration: ```toml # Default config: [Common] +#ScanPersistStores = [] [Common.API] #Gateway = ["/ip4/{api_host}/tcp/{api_port}"] #Token = "{some token}" @@ -152,9 +153,7 @@ We will break down each configurable item one by one. ## [Common] -`Common` section includes common configuration, which is further divided into four sub-configuration items: - - +`Common` section includes common configuration. ### [Common.API] @@ -245,7 +244,6 @@ Path = "/mnt/mass/piece1" ``` - ### [[Common.PersistStores]] `Common.PersistStores` is used to configure sector persistent data stores. It corresponds to the `attached` concept in `damocles-worker`. @@ -318,6 +316,34 @@ Path = "/mnt/remote/10.0.0.14/store" # ``` + +### ScanPersistStores +```toml +# The paths to scan persistent store, string array type, optional +# Default is empty array +# Support glob mode +#ScanPersistStores = ["/filecoin/*", "/store1"] +``` + +#### sectorsector.json +The format of `sectorsector.json` is as follows, and the field meanings are equivalent to the fields in the `[[Common.PersistStores]]` configuration. + +```json +{ + "ID": "6df11a94-4f04-4070-9ccd-54618e5e390d", + "Strict": false, + "ReadOnly": false, + "Weight": 0, + "AllowMiners": [1001], + "DenyMiners": [], + "PluginName": "", + "Meta": {}, +} +``` + +For persist stores related configuration, please refer to the document [damocles 扇区存储配置](../zh/19.damocles-扇区存储配置.md) + + ### [Common.MongoKVStore] `Deprecated` `Common.MongoKVStore` is used to configure whether `damocles-manager` use MongoDB as KV database during sealing. diff --git "a/docs/zh/03.damocles-worker\347\232\204\351\205\215\347\275\256\350\247\243\346\236\220.md" "b/docs/zh/03.damocles-worker\347\232\204\351\205\215\347\275\256\350\247\243\346\236\220.md" index dea6649f1..abc8e9682 100644 --- "a/docs/zh/03.damocles-worker\347\232\204\351\205\215\347\275\256\350\247\243\346\236\220.md" +++ "b/docs/zh/03.damocles-worker\347\232\204\351\205\215\347\275\256\350\247\243\346\236\220.md" @@ -11,6 +11,7 @@ damocles-worker 是数据封装的执行主体,我们来了解一下它的配 # name = "worker-#1" # rpc_server.host = "192.168.1.100" # rpc_server.port = 17891 +# scan_persist_stores = [] [metrics] #enable = false @@ -132,7 +133,7 @@ cgroup.cpuset = "32-47" ### 基础配置范例 -``` +```toml [worker] # 实例名,选填项,字符串类型 # 默认以连接 `damocles-manager` 所使用的网卡 IP 地址作为实例名 @@ -151,6 +152,12 @@ cgroup.cpuset = "32-47" # 否则将会从 damocles-manager 加载远程 piece 文件 # 如果 "/path/to/{your_local_pieces_dir01, your_local_pieces_dir02, ...}/piece_file_name" 文件不存在, worker 也会从 damocles-manager 加载 # local_pieces_dirs = ["/path/to/your_local_pieces_dir01", "/path/to/your_local_pieces_dir02"] + + +# 扫描持久化存储的路径,字符串数组类型,可选项 +# 默认为空数组 +# 支持 glob 格式, ScanPersistStores 与 scan_persist_stores 字段名称等价 +# ScanPersistStores = ["/filecoin/*", "/store1"] ``` 绝大多数情况下,本配置项内的各个字段无需手工配置。 diff --git "a/docs/zh/04.damocles-manager\347\232\204\351\205\215\347\275\256\350\247\243\346\236\220.md" "b/docs/zh/04.damocles-manager\347\232\204\351\205\215\347\275\256\350\247\243\346\236\220.md" index e645b098c..f822a3f1d 100644 --- "a/docs/zh/04.damocles-manager\347\232\204\351\205\215\347\275\256\350\247\243\346\236\220.md" +++ "b/docs/zh/04.damocles-manager\347\232\204\351\205\215\347\275\256\350\247\243\346\236\220.md" @@ -7,6 +7,7 @@ ```toml # Default config: [Common] +#ScanPersistStores = [] [Common.API] #Gateway = ["/ip4/{api_host}/tcp/{api_port}"] #Token = "{some token}" @@ -44,6 +45,7 @@ #DenyMiners = [3, 4] #Plugin = "" #PluginName = "s3store" + [Common.PersistStores.Meta] #SomeKey = "SomeValue" [Common.DB] @@ -159,8 +161,7 @@ JobLifetime = "25h0m0s" 我们将逐一分析其中的可配置项。 ## [Common] - -`Common` 是公共配置,又分成四个子配置项: +`Common` 是公共配置, 其内容包含: ### [Common.API] @@ -246,84 +247,6 @@ Path = "/mnt/mass/piece1" # ``` -### [Common.PieceStorePreset] - - -`PersistStore` 指的是扇区持久化数据存储。与之对应的是 `damocles-worker` 中的 `attached` 概念。 - -而 `PieceStorePreset` 为全局的所有 `PersistStore` 配置,提供了一套可自定义的预设值,与后一章节中的 `[[Common.PersistStores]]` 配置项相呼应。合理利用 `PieceStorePreset` 可以大大简化 `[[Common.PersistStores]]` 的配置。 - -同时,`Damocles-Manager` 还支持 `lotus` 风格的存储配置文件(例如 `lotus-miner` repo 下默认生成的 `storage.json` 文件)。`Damocles-Manager` 会根据 `PieceStorePreset` 为该配置文件下的每一个路径在内存中生成一个对应的 `PersistStore` 配置项。 - - -#### 基础配置范例 - -```toml -[Common.PieceStorePreset] - -# 只读,选填项,布尔类型 -# 默认值为 false -# 自 v0.4.0 起,持久化存储分配逻辑转到 damocles-manager 上 -# 可通过此配置设置存储是否可以继续写入 -ReadOnly = false - -# 可选项,布尔类型 -# 默认 false -# 是否验证`Path`路径是否为通常文件,true 时,`Path`为软连接等非通常文件时会报错 -Strict = false - -# 权重,选填项,数字类型 -# 默认值为 1 -# 当填写值为 0 时,等效于 1 -# 自 v0.4.0 起,持久化存储分配逻辑转到 damocles-manager 上 -# 可通过此配置设置多个持久化存储之间的权重配比 -# 每个持久化存储被选中的概率为 `weight / sum`, `sum` 是所有可用的持久化存储权重的和 -# 例:配置 3 个 持久化存储,weight 分别为 2, 1, 1。则被选中的概率分别为 50%, 25%, 25% -Weight = 1 - - -# 允许进行分配的矿工号列表,选填项,数字数组类型 -# 默认为 null -# 当不设置时,视为允许全部矿工号;当设置时,则相当于白名单,仅允许分配给列出的矿工号 -# 如果一个矿工号同时出现在 AllowMiners 和 DenyMiners 中时,DenyMiners 优先生效,即视为拒绝 -AllowMiners = [1, 2] - -# 拒绝进行分配的矿工号列表,选填项,数字数组类型 -# 默认为 null -# 当不设置时,视为不拒绝任何矿工号;当设置时,则相当于黑名单,将拒绝为列出的矿工号分配 -# 如果一个矿工号同时出现在 AllowMiners 和 DenyMiners 中时,DenyMiners 优先生效,即视为拒绝 -DenyMiners = [3, 4] - -# lotus 风格的存储配置文件的路径,选填项,字符串类型 -# 默认为空字符串 -# 如果只是希望简单地沿用 lotus 的存储路径配置,可以选择填写此项 -# 但是如果希望更加深入地对每个存储路径进行细致的设置,建议使用 `[[Common.PersistStores]]` 配置项 -StorageConfigPath = "/optional/path/to/your/storage.json" - -# 元信息,选填项,字典类型 -# 内部值为 Key = "Value" 的格式 -# 默认值为 null -# 用于支持不同类型存储方案的预备,目前没有任何作用 -[Common.PersistStores.Meta] -#SomeKey = "SomeValue" -# -``` - -- storage.json - -```json -{ - "StoragePaths": [ - { - "Name": "persist", - "Path": "/root/persist" - } - ] -} -``` - -其中 `Name` 属性可以省略,省略时默认使用 `Path` 属性的值作为 `Name`。 - ### [[Common.PersistStores]] `Common.PersistStores` 用于配置扇区持久化数据存储。与之对应的是 `damocles-worker` 中的 `attached` 概念。 @@ -394,6 +317,30 @@ Path = "/mnt/remote/10.0.0.14/store" # ``` +### ScanPersistStores +```toml +# 扫描持久化存储的路径,字符串数组类型,可选项 +# 默认为空数组 +# 支持 glob 格式 +#ScanPersistStores = ["/filecoin/*", "/store1"] +``` + +#### sectorsector.json +`sectorsector.json` 格式如下, 字段含义等价于 [[Common.PersistStores]] 配置内的字段。 +```json +{ + "ID": "6df11a94-4f04-4070-9ccd-54618e5e390d", + "Strict": false, + "ReadOnly": false, + "Weight": 0, + "AllowMiners": [1001], + "DenyMiners": [], + "PluginName": "", + "Meta": {}, +} +``` +存储相关配置可以参考文档 [damocles 扇区存储配置](./19.damocles-扇区存储配置.md) + ### [Common.MongoKVStore] `已废弃` `Common.MongoKVStore` 用于配置 `damocles-manager` 是否启用 Mongo 作为 sealing 过程中使用的 KV 数据库。 diff --git "a/docs/zh/19.damocles-\346\211\207\345\214\272\345\255\230\345\202\250\351\205\215\347\275\256.md" "b/docs/zh/19.damocles-\346\211\207\345\214\272\345\255\230\345\202\250\351\205\215\347\275\256.md" new file mode 100644 index 000000000..b1387e2e3 --- /dev/null +++ "b/docs/zh/19.damocles-\346\211\207\345\214\272\345\255\230\345\202\250\351\205\215\347\275\256.md" @@ -0,0 +1,115 @@ +# damocles 扇区存储配置 + +damocles 存储配置用于配置存放扇区 sealed/cache 文件的存储目录(通常是 nfs 挂载点)。 damocles 在运行 window_post / winning_post / sealing 任务时都需要访问这些存储中的扇区文件。 + +damocles 支持两种方式配置存储,用户可以根据自己的需求灵活选择。下面将会分别举例介绍两种配置方式。 + +假设我们有三个扇区存储: storage1, storage2, storage3。 它们挂载在 damocles-manager 机器和 damocles-worker 机器上不同的位置: +- damocles-manager 机器: `/filecoin/storage1`, `/filecoin/storage2`, `/storage3` +- damocles-worker 机器: `/fil/storage1`, `/fil/storage2`, `/storage3` + + +其中 storage1 由于存储空间耗尽,我们将其设置为只读,仅提供给 window_post / winning_post 任务使用,storage3 由于性能更好我们希望优先将扇区文件存储到 storage3 中。 另外这三个存储目录要么是空目录要么其中的扇区文件已经存在于 damocles 的扇区索引中([导入已存在的扇区数据](./06.导入已存在的扇区数据.md))。 + +## 直接配置主配置文件方式 +直接配置主配置文件方式是将扇区存储信息全部配置到主配置文件中方式。 + +### 直接配置主配置文件实例 + +#### damocles-manager 配置 + +```toml +# ~/.damocles-manager/sector-manager.cfg +# ... +[[Common.PersistStores]] +Name = "storage1" +Path = "/filecoin/storage1" +ReadOnly = true + +[[Common.PersistStores]] +Name = "storage2" +Path = "/filecoin/storage2" +Weight = 2 + +[[Common.PersistStores]] +Name = "storage3" +Path = "/storage3" +Weight = 1 + +# ... +``` + +#### damocles-worker sealing 配置 + +```toml +# /path/to/damocles-worker.toml +# ... +[[Attached]] +name = "storage2" # 必须与 damocles-manager 配置中的 PersistStore 名称一致 +location = "/fil/storage2" + +[[Attached]] +name = "storage3" +location = "/storage3" +# ... +``` + +至此我们完成了 damocles-manager 和 damocles-worker 的扇区存储配置。**接下来通常会使用 `damocles-manager util sealer proving check` 命令检查存储配置是否正确,以及扇区存储是否可用。(重要)** + +直接配置主配置文件方式的好处是相对灵活,但是对于拥有几十上百个扇区存储的用户来说这种配置方式会显得相对臃肿,下面介绍另一种扇区存储配置方式。 + +## 独立配置文件方式 + +独立配置文件方式是指在存储目录下添加一个名为 `sectorstore.json` 的配置文件,用于存放扇区存储配置信息, damocles 程序在启动时会自动扫描并读取它们。`sectorstore.json` 内容如下: +```json +{ + "ID": "123", + "Name": "123", + "Strict": false, + "ReadOnly": false, + "Weight": 0, + "AllowMiners": [1], + "DenyMiners": [2], + "PluginName": "2234234", + "Meta": {}, +} +``` +`sectorstore.json` 文件中的配置项和主配置文件中 `[[Common.PersistStores]]` 的配置项一一对应。其中的 ID 字段和 Name 字段在 damocles 中是等价的我们只需要填写其中任意一个即可。damocles-manager 提供了一个命令生成 `sectorstore.json` 配置文件。 + +### 独立配置文件实例 + +#### 1. 在 damocles-manager 机器运行命令生成 `sectorstore.json` 配置文件: +``` +damocles-manager util storage gen-sectorstore-json --readonly=true /filecoin/storage1 +damocles-manager util storage gen-sectorstore-json --weight=2 /filecoin/storage2 +damocles-manager util storage gen-sectorstore-json --weight=1 /storage3 +``` + +#### 2. 配置 damocles-manager +```toml +# ~/.damocles-manager/sector-manager.cfg +# ... +[Common] +ScanPersistStores = ["/filecoin/*", "/storage3"] + +# ... +``` + +### 3. 配置 damocles-worker +```toml +# /path/to/damocles-worker.toml +# ... +[worker] +ScanPersistStores = ["/fil/storage2", "/storage3"] + +# ... +``` + +damocles 启动后,会有 log 信息输出 (damocles-manager 和 damocles-worker 日志输出基本一致): +``` +scanned persist store: xxxxx, path: /filecoin/storage1 +scanned persist store: xxxxx, path: /filecoin/storage2 +scanned persist store: xxxxx, path: /storage3 +``` + +至此使用独立配置文件方式配置完毕,对于扇区存储较多的用户来说,这种方式会更加方便。同样的接下来应该使用 `damocles-manager util sealer proving check` 命令检查存储配置是否正确,以及扇区存储是否可用。 diff --git a/manager-plugin/objstore/objstore.go b/manager-plugin/objstore/objstore.go index 578600f1d..c2b11220d 100644 --- a/manager-plugin/objstore/objstore.go +++ b/manager-plugin/objstore/objstore.go @@ -21,42 +21,17 @@ type Config struct { Path string Meta map[string]string - // Strict should never be used directly, use GetStrict() instead - Strict *bool - // ReadOnly should never be used directly, use GetReadOnly() instead - ReadOnly *bool - // Weight should never be used directly, use GetWeight() instead - Weight *uint -} - -func (c Config) GetStrict() bool { - if c.Strict == nil { - return false - } - return *c.Strict -} - -func (c Config) GetReadOnly() bool { - if c.ReadOnly == nil { - return false - } - return *c.ReadOnly -} - -func (c Config) GetWeight() uint { - if c.Weight == nil { - return 1 - } - return *c.Weight + Strict bool + ReadOnly bool + Weight uint } func DefaultConfig(path string, readonly bool) Config { - one := uint(1) return Config{ Path: path, Meta: map[string]string{}, - ReadOnly: &readonly, - Weight: &one, + ReadOnly: readonly, + Weight: 1, } }