diff --git a/cmd/frostfs-adm/internal/modules/morph/ape/ape_util.go b/cmd/frostfs-adm/internal/modules/morph/ape/ape_util.go index b3610e7f7..e82c93e2d 100644 --- a/cmd/frostfs-adm/internal/modules/morph/ape/ape_util.go +++ b/cmd/frostfs-adm/internal/modules/morph/ape/ape_util.go @@ -94,6 +94,16 @@ func parseChainName(cmd *cobra.Command) apechain.Name { return apeChainName } +// invokerAdapter adapats invoker.Invoker to ContractStorageInvoker interface. +type invokerAdapter struct { + *invoker.Invoker + rpcActor invoker.RPCInvoke +} + +func (n *invokerAdapter) GetRPCInvoker() invoker.RPCInvoke { + return n.rpcActor +} + func newPolicyContractReaderInterface(cmd *cobra.Command) (*morph.ContractStorageReader, *invoker.Invoker) { c, err := helper.GetN3Client(viper.GetViper()) commonCmd.ExitOnErr(cmd, "unable to create NEO rpc client: %w", err) @@ -107,7 +117,12 @@ func newPolicyContractReaderInterface(cmd *cobra.Command) (*morph.ContractStorag ch, err = helper.NNSResolveHash(inv, nnsCs.Hash, helper.DomainOf(constants.PolicyContract)) commonCmd.ExitOnErr(cmd, "unable to resolve policy contract hash: %w", err) - return morph.NewContractStorageReader(inv, ch), inv + invokerAdapter := &invokerAdapter{ + Invoker: inv, + rpcActor: c, + } + + return morph.NewContractStorageReader(invokerAdapter, ch), inv } func newPolicyContractInterface(cmd *cobra.Command) (*morph.ContractStorage, *helper.LocalActor) { diff --git a/cmd/frostfs-adm/internal/modules/morph/helper/actor.go b/cmd/frostfs-adm/internal/modules/morph/helper/actor.go index f920aa5ba..1ca246f9f 100644 --- a/cmd/frostfs-adm/internal/modules/morph/helper/actor.go +++ b/cmd/frostfs-adm/internal/modules/morph/helper/actor.go @@ -23,9 +23,10 @@ import ( // LocalActor is a kludge, do not use it outside of the morph commands. type LocalActor struct { - neoActor *actor.Actor - accounts []*wallet.Account - Invoker *invoker.Invoker + neoActor *actor.Actor + accounts []*wallet.Account + Invoker *invoker.Invoker + rpcInvoker invoker.RPCInvoke } // NewLocalActor create LocalActor with accounts form provided wallets. @@ -68,9 +69,10 @@ func NewLocalActor(cmd *cobra.Command, c actor.RPCActor) (*LocalActor, error) { } } return &LocalActor{ - neoActor: act, - accounts: accounts, - Invoker: &act.Invoker, + neoActor: act, + accounts: accounts, + Invoker: &act.Invoker, + rpcInvoker: c, }, nil } @@ -167,3 +169,7 @@ func (a *LocalActor) MakeUnsignedRun(_ []byte, _ []transaction.Attribute) (*tran func (a *LocalActor) MakeCall(_ util.Uint160, _ string, _ ...any) (*transaction.Transaction, error) { panic("unimplemented") } + +func (a *LocalActor) GetRPCInvoker() invoker.RPCInvoke { + return a.rpcInvoker +} diff --git a/cmd/frostfs-cli/modules/control/list_rules.go b/cmd/frostfs-cli/modules/control/list_rules.go index b345ecbfb..f5fc27bda 100644 --- a/cmd/frostfs-cli/modules/control/list_rules.go +++ b/cmd/frostfs-cli/modules/control/list_rules.go @@ -27,6 +27,8 @@ const ( defaultNamespace = "root" namespaceTarget = "namespace" containerTarget = "container" + userTarget = "user" + groupTarget = "group" ) const ( @@ -66,6 +68,16 @@ func parseTarget(cmd *cobra.Command) *control.ChainTarget { Name: name, Type: control.ChainTarget_CONTAINER, } + case userTarget: + return &control.ChainTarget{ + Name: name, + Type: control.ChainTarget_USER, + } + case groupTarget: + return &control.ChainTarget{ + Name: name, + Type: control.ChainTarget_GROUP, + } default: commonCmd.ExitOnErr(cmd, "read target type error: %w", errUnknownTargetType) } diff --git a/cmd/frostfs-ir/config.go b/cmd/frostfs-ir/config.go index be870052c..c73b68eb5 100644 --- a/cmd/frostfs-ir/config.go +++ b/cmd/frostfs-ir/config.go @@ -34,6 +34,7 @@ func reloadConfig() error { if err != nil { return err } + cmode.Store(cfg.GetBool("node.kludge_compatibility_mode")) err = logPrm.SetLevelString(cfg.GetString("logger.level")) if err != nil { return err diff --git a/cmd/frostfs-ir/defaults.go b/cmd/frostfs-ir/defaults.go index 127a68b29..23a475591 100644 --- a/cmd/frostfs-ir/defaults.go +++ b/cmd/frostfs-ir/defaults.go @@ -43,6 +43,8 @@ func defaultConfiguration(cfg *viper.Viper) { setControlDefaults(cfg) cfg.SetDefault("governance.disable", false) + + cfg.SetDefault("node.kludge_compatibility_mode", false) } func setControlDefaults(cfg *viper.Viper) { diff --git a/cmd/frostfs-ir/main.go b/cmd/frostfs-ir/main.go index 9879342b7..31390dd74 100644 --- a/cmd/frostfs-ir/main.go +++ b/cmd/frostfs-ir/main.go @@ -6,6 +6,7 @@ import ( "fmt" "os" "sync" + "sync/atomic" "git.frostfs.info/TrueCloudLab/frostfs-node/internal/logs" "git.frostfs.info/TrueCloudLab/frostfs-node/misc" @@ -37,6 +38,7 @@ var ( cfg *viper.Viper configFile *string configDir *string + cmode = &atomic.Bool{} ) func exitErr(err error) { @@ -62,6 +64,8 @@ func main() { cfg, err = newConfig() exitErr(err) + cmode.Store(cfg.GetBool("node.kludge_compatibility_mode")) + metrics := irMetrics.NewInnerRingMetrics() err = logPrm.SetLevelString( @@ -84,7 +88,7 @@ func main() { metricsCmp = newMetricsComponent() metricsCmp.init() - innerRing, err = innerring.New(ctx, log, cfg, intErr, metrics) + innerRing, err = innerring.New(ctx, log, cfg, intErr, metrics, cmode) exitErr(err) pprofCmp.start() diff --git a/cmd/frostfs-node/config.go b/cmd/frostfs-node/config.go index 45ef771f3..b66a565df 100644 --- a/cmd/frostfs-node/config.go +++ b/cmd/frostfs-node/config.go @@ -109,6 +109,9 @@ type applicationConfiguration struct { lowMem bool rebuildWorkers uint32 } + + // if need to run node in compatibility with other versions mode + cmode *atomic.Bool } type shardCfg struct { @@ -204,10 +207,13 @@ func (a *applicationConfiguration) readConfig(c *config.Config) error { } // clear if it is rereading + cmode := a.cmode *a = applicationConfiguration{} + a.cmode = cmode } a._read = true + a.cmode.Store(nodeconfig.CompatibilityMode(c)) // Logger @@ -375,8 +381,9 @@ func (c *cfg) startMaintenance() { // stops node's maintenance. func (c *internals) stopMaintenance() { - c.isMaintenance.Store(false) - c.log.Info(logs.FrostFSNodeStoppedLocalNodesMaintenance) + if c.isMaintenance.CompareAndSwap(true, false) { + c.log.Info(logs.FrostFSNodeStoppedLocalNodesMaintenance) + } } // IsMaintenance checks if storage node is under maintenance. @@ -648,7 +655,11 @@ type cfgControlService struct { var persistateSideChainLastBlockKey = []byte("side_chain_last_processed_block") func initCfg(appCfg *config.Config) *cfg { - c := &cfg{} + c := &cfg{ + applicationConfiguration: applicationConfiguration{ + cmode: &atomic.Bool{}, + }, + } err := c.readConfig(appCfg) if err != nil { @@ -1135,13 +1146,25 @@ func (c *cfg) LocalNodeInfo() (*netmapV2.NodeInfo, error) { return &res, nil } -// handleLocalNodeInfo rewrites local node info from the FrostFS network map. +// setContractNodeInfo rewrites local node info from the FrostFS network map. // Called with nil when storage node is outside the FrostFS network map // (before entering the network and after leaving it). -func (c *cfg) handleLocalNodeInfo(ni *netmap.NodeInfo) { +func (c *cfg) setContractNodeInfo(ni *netmap.NodeInfo) { c.cfgNetmap.state.setNodeInfo(ni) } +func (c *cfg) updateContractNodeInfo(epoch uint64) { + ni, err := c.netmapLocalNodeState(epoch) + if err != nil { + c.log.Error(logs.FrostFSNodeCouldNotUpdateNodeStateOnNewEpoch, + zap.Uint64("epoch", epoch), + zap.String("error", err.Error())) + return + } + + c.setContractNodeInfo(ni) +} + // bootstrapWithState calls "addPeer" method of the Sidechain Netmap contract // with the binary-encoded information from the current node's configuration. // The state is set using the provided setter which MUST NOT be nil. diff --git a/cmd/frostfs-node/config/node/config.go b/cmd/frostfs-node/config/node/config.go index ac76ad47e..90338556e 100644 --- a/cmd/frostfs-node/config/node/config.go +++ b/cmd/frostfs-node/config/node/config.go @@ -292,3 +292,8 @@ func (l PersistentPolicyRulesConfig) Perm() fs.FileMode { func (l PersistentPolicyRulesConfig) NoSync() bool { return config.BoolSafe((*config.Config)(l.cfg), "no_sync") } + +// CompatibilityMode returns true if need to run node in compatibility with previous versions mode. +func CompatibilityMode(c *config.Config) bool { + return config.BoolSafe(c.Sub(subsection), "kludge_compatibility_mode") +} diff --git a/cmd/frostfs-node/morph.go b/cmd/frostfs-node/morph.go index 1b148095b..b025f63ef 100644 --- a/cmd/frostfs-node/morph.go +++ b/cmd/frostfs-node/morph.go @@ -48,6 +48,7 @@ func initMorphComponents(ctx context.Context, c *cfg) { }), client.WithSwitchInterval(morphconfig.SwitchInterval(c.appCfg)), client.WithMorphCacheMetrics(c.metricsCollector.MorphCacheMetrics()), + client.WithCompatibilityMode(c.cmode), ) if err != nil { c.log.Info(logs.FrostFSNodeFailedToCreateNeoRPCClient, diff --git a/cmd/frostfs-node/netmap.go b/cmd/frostfs-node/netmap.go index b1d854da4..56f2ca98f 100644 --- a/cmd/frostfs-node/netmap.go +++ b/cmd/frostfs-node/netmap.go @@ -31,7 +31,7 @@ type networkState struct { controlNetStatus atomic.Int32 // control.NetmapStatus - nodeInfo atomic.Value // *netmapSDK.NodeInfo + nodeInfo atomic.Value // netmapSDK.NodeInfo metrics *metrics.NodeMetrics } @@ -176,7 +176,11 @@ func addNewEpochNotificationHandlers(c *cfg) { c.cfgNetmap.state.setCurrentEpoch(ev.(netmapEvent.NewEpoch).EpochNumber()) }) - addNewEpochAsyncNotificationHandler(c, func(_ event.Event) { + addNewEpochAsyncNotificationHandler(c, func(ev event.Event) { + e := ev.(netmapEvent.NewEpoch).EpochNumber() + + c.updateContractNodeInfo(e) + if !c.needBootstrap() || c.cfgNetmap.reBoostrapTurnedOff.Load() { // fixes #470 return } @@ -186,22 +190,6 @@ func addNewEpochNotificationHandlers(c *cfg) { } }) - addNewEpochAsyncNotificationHandler(c, func(ev event.Event) { - e := ev.(netmapEvent.NewEpoch).EpochNumber() - - ni, err := c.netmapLocalNodeState(e) - if err != nil { - c.log.Error(logs.FrostFSNodeCouldNotUpdateNodeStateOnNewEpoch, - zap.Uint64("epoch", e), - zap.String("error", err.Error()), - ) - - return - } - - c.handleLocalNodeInfo(ni) - }) - if c.cfgMorph.notaryEnabled { addNewEpochAsyncNotificationHandler(c, func(_ event.Event) { _, err := makeNotaryDeposit(c) @@ -270,7 +258,7 @@ func initNetmapState(c *cfg) { c.cfgNetmap.state.setCurrentEpoch(epoch) c.cfgNetmap.startEpoch = epoch - c.handleLocalNodeInfo(ni) + c.setContractNodeInfo(ni) } func nodeState(ni *netmapSDK.NodeInfo) string { diff --git a/cmd/frostfs-node/tree.go b/cmd/frostfs-node/tree.go index dced05bc2..ff00b6fac 100644 --- a/cmd/frostfs-node/tree.go +++ b/cmd/frostfs-node/tree.go @@ -63,7 +63,9 @@ func initTreeService(c *cfg) { tree.WithReplicationChannelCapacity(treeConfig.ReplicationChannelCapacity()), tree.WithReplicationWorkerCount(treeConfig.ReplicationWorkerCount()), tree.WithAuthorizedKeys(treeConfig.AuthorizedKeys()), - tree.WithMetrics(c.metricsCollector.TreeService())) + tree.WithMetrics(c.metricsCollector.TreeService()), + tree.WithAPERouter(c.cfgObject.cfgAccessPolicyEngine.accessPolicyEngine), + ) c.cfgGRPC.performAndSave(func(_ string, _ net.Listener, s *grpc.Server) { tree.RegisterTreeServiceServer(s, c.treeService) diff --git a/go.mod b/go.mod index 1d18bb9d3..1c4bba466 100644 --- a/go.mod +++ b/go.mod @@ -5,16 +5,15 @@ go 1.20 require ( code.gitea.io/sdk/gitea v0.17.1 git.frostfs.info/TrueCloudLab/frostfs-api-go/v2 v2.16.1-0.20240215124401-634e24aba715 - git.frostfs.info/TrueCloudLab/frostfs-contract v0.19.0 + git.frostfs.info/TrueCloudLab/frostfs-contract v0.19.3-0.20240409111539-e7a05a49ff45 git.frostfs.info/TrueCloudLab/frostfs-observability v0.0.0-20231101111734-b3ad3335ff65 git.frostfs.info/TrueCloudLab/frostfs-sdk-go v0.0.0-20240301150205-6fe4e2541d0b git.frostfs.info/TrueCloudLab/hrw v1.2.1 - git.frostfs.info/TrueCloudLab/policy-engine v0.0.0-20240307151106-2ec958cbfdfd + git.frostfs.info/TrueCloudLab/policy-engine v0.0.0-20240426062043-c5397286410f git.frostfs.info/TrueCloudLab/tzhash v1.8.0 git.frostfs.info/TrueCloudLab/zapjournald v0.0.0-20240124114243-cb2e66427d02 github.com/cheggaaa/pb v1.0.29 github.com/chzyer/readline v1.5.1 - github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc github.com/flynn-archive/go-shlex v0.0.0-20150515145356-3f9db97f8568 github.com/google/uuid v1.6.0 github.com/hashicorp/golang-lru/v2 v2.0.7 @@ -40,8 +39,8 @@ require ( go.uber.org/zap v1.26.0 golang.org/x/exp v0.0.0-20240119083558-1b970713d09a golang.org/x/sync v0.6.0 - golang.org/x/sys v0.16.0 - golang.org/x/term v0.16.0 + golang.org/x/sys v0.18.0 + golang.org/x/term v0.18.0 google.golang.org/grpc v1.61.0 google.golang.org/protobuf v1.33.0 gopkg.in/yaml.v3 v3.0.1 @@ -65,6 +64,7 @@ require ( github.com/consensys/bavard v0.1.13 // indirect github.com/consensys/gnark-crypto v0.12.2-0.20231222162921-eb75782795d2 // indirect github.com/cpuguy83/go-md2man/v2 v2.0.3 // indirect + github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/davidmz/go-pageant v1.0.2 // indirect github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 // indirect github.com/fsnotify/fsnotify v1.7.0 // indirect @@ -121,8 +121,8 @@ require ( go.opentelemetry.io/otel/sdk v1.22.0 // indirect go.opentelemetry.io/proto/otlp v1.1.0 // indirect go.uber.org/multierr v1.11.0 // indirect - golang.org/x/crypto v0.18.0 // indirect - golang.org/x/net v0.20.0 // indirect + golang.org/x/crypto v0.21.0 // indirect + golang.org/x/net v0.23.0 // indirect golang.org/x/text v0.14.0 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20240123012728-ef4313101c80 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20240123012728-ef4313101c80 // indirect diff --git a/go.sum b/go.sum index d90bf0f6a..51385bc70 100644 --- a/go.sum +++ b/go.sum @@ -2,8 +2,8 @@ code.gitea.io/sdk/gitea v0.17.1 h1:3jCPOG2ojbl8AcfaUCRYLT5MUcBMFwS0OSK2mA5Zok8= code.gitea.io/sdk/gitea v0.17.1/go.mod h1:aCnBqhHpoEWA180gMbaCtdX9Pl6BWBAuuP2miadoTNM= git.frostfs.info/TrueCloudLab/frostfs-api-go/v2 v2.16.1-0.20240215124401-634e24aba715 h1:EDtL9OJcdeevV/jmNMtHugulAQprdOnknNPvLB3LRgE= git.frostfs.info/TrueCloudLab/frostfs-api-go/v2 v2.16.1-0.20240215124401-634e24aba715/go.mod h1:uY0AYmCznjZdghDnAk7THFIe1Vlg531IxUcus7ZfUJI= -git.frostfs.info/TrueCloudLab/frostfs-contract v0.19.0 h1:FzurjElUwC7InY9v5rzXReKbfBL5yRJKSWJPq6BKhH0= -git.frostfs.info/TrueCloudLab/frostfs-contract v0.19.0/go.mod h1:F/fe1OoIDKr5Bz99q4sriuHDuf3aZefZy9ZsCqEtgxc= +git.frostfs.info/TrueCloudLab/frostfs-contract v0.19.3-0.20240409111539-e7a05a49ff45 h1:Tp4I+XOLp3VCJORfxSamQtj3RZNISbaLM4WD5iIzXxg= +git.frostfs.info/TrueCloudLab/frostfs-contract v0.19.3-0.20240409111539-e7a05a49ff45/go.mod h1:F/fe1OoIDKr5Bz99q4sriuHDuf3aZefZy9ZsCqEtgxc= git.frostfs.info/TrueCloudLab/frostfs-crypto v0.6.0 h1:FxqFDhQYYgpe41qsIHVOcdzSVCB8JNSfPG7Uk4r2oSk= git.frostfs.info/TrueCloudLab/frostfs-crypto v0.6.0/go.mod h1:RUIKZATQLJ+TaYQa60X2fTDwfuhMfm8Ar60bQ5fr+vU= git.frostfs.info/TrueCloudLab/frostfs-observability v0.0.0-20231101111734-b3ad3335ff65 h1:PaZ8GpnUoXxUoNsc1qp36bT2u7FU+neU4Jn9cl8AWqI= @@ -12,8 +12,8 @@ git.frostfs.info/TrueCloudLab/frostfs-sdk-go v0.0.0-20240301150205-6fe4e2541d0b git.frostfs.info/TrueCloudLab/frostfs-sdk-go v0.0.0-20240301150205-6fe4e2541d0b/go.mod h1:XcgrbZ88XfvhAMxmZCQJ0dv6FyRSq6Mg2J7nN8uuO0k= git.frostfs.info/TrueCloudLab/hrw v1.2.1 h1:ccBRK21rFvY5R1WotI6LNoPlizk7qSvdfD8lNIRudVc= git.frostfs.info/TrueCloudLab/hrw v1.2.1/go.mod h1:C1Ygde2n843yTZEQ0FP69jYiuaYV0kriLvP4zm8JuvM= -git.frostfs.info/TrueCloudLab/policy-engine v0.0.0-20240307151106-2ec958cbfdfd h1:pyIl9f4nIr7ekJ73W9keLIQ5dpoKb8o6xNmodsXY5+o= -git.frostfs.info/TrueCloudLab/policy-engine v0.0.0-20240307151106-2ec958cbfdfd/go.mod h1:H/AW85RtYxVTbcgwHW76DqXeKlsiCIOeNXHPqyDBrfQ= +git.frostfs.info/TrueCloudLab/policy-engine v0.0.0-20240426062043-c5397286410f h1:z+AqVpjWIZVh91eIt+lBTK1AwWtj2EBv+YE2PJKvvuk= +git.frostfs.info/TrueCloudLab/policy-engine v0.0.0-20240426062043-c5397286410f/go.mod h1:SgioiGhQNWqiV5qpFAXRDJF81SEFRBhtwGEiU0FViyA= git.frostfs.info/TrueCloudLab/rfc6979 v0.4.0 h1:M2KR3iBj7WpY3hP10IevfIB9MURr4O9mwVfJ+SjT3HA= git.frostfs.info/TrueCloudLab/rfc6979 v0.4.0/go.mod h1:okpbKfVYf/BpejtfFTfhZqFP+sZ8rsHrP8Rr/jYPNRc= git.frostfs.info/TrueCloudLab/tzhash v1.8.0 h1:UFMnUIk0Zh17m8rjGHJMqku2hCgaXDqjqZzS4gsb4UA= @@ -316,8 +316,8 @@ golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5 golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= -golang.org/x/crypto v0.18.0 h1:PGVlW0xEltQnzFZ55hkuX5+KLyrMYhHld1YHO4AKcdc= -golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg= +golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA= +golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs= golang.org/x/exp v0.0.0-20240119083558-1b970713d09a h1:Q8/wZp0KX97QFTc2ywcOE0YRjZPVIx+MXInMzdvQqcA= golang.org/x/exp v0.0.0-20240119083558-1b970713d09a/go.mod h1:idGWGoKP1toJGkd5/ig9ZLuPcZBC3ewk7SzmH0uou08= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= @@ -339,8 +339,8 @@ golang.org/x/net v0.0.0-20220607020251-c690dde0001d/go.mod h1:XRhObCWvk6IyKnWLug golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= -golang.org/x/net v0.20.0 h1:aCL9BSgETF1k+blQaYUBx9hJ9LOGP3gAVemcZlf1Kpo= -golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY= +golang.org/x/net v0.23.0 h1:7EYJ93RZ9vYSZAIb2x3lnuvqO5zneoD6IvWjuhfxjTs= +golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -375,15 +375,15 @@ golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU= -golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4= +golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0= -golang.org/x/term v0.16.0 h1:m+B6fahuftsE9qjo0VWp2FW0mB3MTJvR0BaMQrq0pmE= -golang.org/x/term v0.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY= +golang.org/x/term v0.18.0 h1:FcHjZXDMxI8mM3nwhX9HlKop4C0YQvCVCdwYl2wOtE8= +golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= diff --git a/pkg/ape/converter/converter.go b/pkg/ape/converter/converter.go new file mode 100644 index 000000000..9032680af --- /dev/null +++ b/pkg/ape/converter/converter.go @@ -0,0 +1,44 @@ +package converter + +import ( + "fmt" + + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/acl" + nativeschema "git.frostfs.info/TrueCloudLab/policy-engine/schema/native" +) + +func SchemaRoleFromACLRole(role acl.Role) (string, error) { + switch role { + case acl.RoleOwner: + return nativeschema.PropertyValueContainerRoleOwner, nil + case acl.RoleContainer: + return nativeschema.PropertyValueContainerRoleContainer, nil + case acl.RoleInnerRing: + return nativeschema.PropertyValueContainerRoleIR, nil + case acl.RoleOthers: + return nativeschema.PropertyValueContainerRoleOthers, nil + default: + return "", fmt.Errorf("failed to convert %s", role.String()) + } +} + +func SchemaMethodFromACLOperation(op acl.Op) (string, error) { + switch op { + case acl.OpObjectGet: + return nativeschema.MethodGetObject, nil + case acl.OpObjectHead: + return nativeschema.MethodHeadObject, nil + case acl.OpObjectPut: + return nativeschema.MethodPutObject, nil + case acl.OpObjectDelete: + return nativeschema.MethodDeleteObject, nil + case acl.OpObjectSearch: + return nativeschema.MethodSearchObject, nil + case acl.OpObjectRange: + return nativeschema.MethodRangeObject, nil + case acl.OpObjectHash: + return nativeschema.MethodHashObject, nil + default: + return "", fmt.Errorf("operation cannot be converted: %d", op) + } +} diff --git a/pkg/ape/request/request.go b/pkg/ape/request/request.go new file mode 100644 index 000000000..6d62ef3d5 --- /dev/null +++ b/pkg/ape/request/request.go @@ -0,0 +1,55 @@ +package ape + +import ( + aperesource "git.frostfs.info/TrueCloudLab/policy-engine/pkg/resource" +) + +type Request struct { + operation string + resource Resource + properties map[string]string +} + +func NewRequest(operation string, resource Resource, properties map[string]string) Request { + return Request{ + operation: operation, + resource: resource, + properties: properties, + } +} + +var _ aperesource.Request = Request{} + +func (r Request) Operation() string { + return r.operation +} + +func (r Request) Property(key string) string { + return r.properties[key] +} + +func (r Request) Resource() aperesource.Resource { + return r.resource +} + +type Resource struct { + name string + properties map[string]string +} + +var _ aperesource.Resource = Resource{} + +func NewResource(name string, properties map[string]string) Resource { + return Resource{ + name: name, + properties: properties, + } +} + +func (r Resource) Name() string { + return r.name +} + +func (r Resource) Property(key string) string { + return r.properties[key] +} diff --git a/pkg/innerring/initialization.go b/pkg/innerring/initialization.go index 1a4174289..783888b3d 100644 --- a/pkg/innerring/initialization.go +++ b/pkg/innerring/initialization.go @@ -462,6 +462,7 @@ func (s *Server) initMorph(ctx context.Context, cfg *viper.Viper, errChan chan<- name: morphPrefix, from: fromSideChainBlock, morphCacheMetric: s.irMetrics.MorphCacheMetrics(), + cmode: s.cmode, } // create morph client diff --git a/pkg/innerring/innerring.go b/pkg/innerring/innerring.go index 5d7dc5a52..3cc203558 100644 --- a/pkg/innerring/innerring.go +++ b/pkg/innerring/innerring.go @@ -103,6 +103,8 @@ type ( // should report start errors // to the application. runners []func(chan<- error) error + + cmode *atomic.Bool } chainParams struct { @@ -113,6 +115,7 @@ type ( sgn *transaction.Signer from uint32 // block height morphCacheMetric metrics.MorphCacheMetrics + cmode *atomic.Bool } ) @@ -330,12 +333,13 @@ func (s *Server) registerStarter(f func() error) { // New creates instance of inner ring sever structure. func New(ctx context.Context, log *logger.Logger, cfg *viper.Viper, errChan chan<- error, - metrics *metrics.InnerRingServiceMetrics, + metrics *metrics.InnerRingServiceMetrics, cmode *atomic.Bool, ) (*Server, error) { var err error server := &Server{ log: log, irMetrics: metrics, + cmode: cmode, } server.sdNotify, err = server.initSdNotify(cfg) @@ -485,6 +489,7 @@ func createClient(ctx context.Context, p *chainParams, errChan chan<- error) (*c }), client.WithSwitchInterval(p.cfg.GetDuration(p.name+".switch_interval")), client.WithMorphCacheMetrics(p.morphCacheMetric), + client.WithCompatibilityMode(p.cmode), ) } diff --git a/pkg/local_object_storage/engine/engine.go b/pkg/local_object_storage/engine/engine.go index 558fe92ed..b87d77e6c 100644 --- a/pkg/local_object_storage/engine/engine.go +++ b/pkg/local_object_storage/engine/engine.go @@ -8,6 +8,7 @@ import ( "git.frostfs.info/TrueCloudLab/frostfs-node/internal/logs" "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/core/container" + "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/internal/metaerr" "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/shard" "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/shard/mode" "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/util/logicerr" @@ -49,6 +50,7 @@ type shardWrapper struct { type setModeRequest struct { sh *shard.Shard + isMeta bool errorCount uint32 } @@ -74,7 +76,7 @@ func (e *StorageEngine) setModeLoop() { if !ok { inProgress[sid] = struct{}{} go func() { - e.moveToDegraded(r.sh, r.errorCount) + e.moveToDegraded(r.sh, r.errorCount, r.isMeta) mtx.Lock() delete(inProgress, sid) @@ -86,7 +88,7 @@ func (e *StorageEngine) setModeLoop() { } } -func (e *StorageEngine) moveToDegraded(sh *shard.Shard, errCount uint32) { +func (e *StorageEngine) moveToDegraded(sh *shard.Shard, errCount uint32, isMeta bool) { sid := sh.ID() log := e.log.With( zap.Stringer("shard_id", sid), @@ -95,21 +97,23 @@ func (e *StorageEngine) moveToDegraded(sh *shard.Shard, errCount uint32) { e.mtx.RLock() defer e.mtx.RUnlock() - err := sh.SetMode(mode.DegradedReadOnly) - if err != nil { + if isMeta { + err := sh.SetMode(mode.DegradedReadOnly) + if err == nil { + log.Info(logs.EngineShardIsMovedInDegradedModeDueToErrorThreshold) + return + } log.Error(logs.EngineFailedToMoveShardInDegradedreadonlyModeMovingToReadonly, zap.Error(err)) - - err = sh.SetMode(mode.ReadOnly) - if err != nil { - log.Error(logs.EngineFailedToMoveShardInReadonlyMode, - zap.Error(err)) - } else { - log.Info(logs.EngineShardIsMovedInReadonlyModeDueToErrorThreshold) - } - } else { - log.Info(logs.EngineShardIsMovedInDegradedModeDueToErrorThreshold) } + + err := sh.SetMode(mode.ReadOnly) + if err != nil { + log.Error(logs.EngineFailedToMoveShardInReadonlyMode, zap.Error(err)) + return + } + + log.Info(logs.EngineShardIsMovedInReadonlyModeDueToErrorThreshold) } // reportShardErrorBackground increases shard error counter and logs an error. @@ -133,7 +137,7 @@ func (e *StorageEngine) reportShardErrorBackground(id string, msg string, err er errCount := sh.errorCount.Add(1) sh.Shard.IncErrorCounter() - e.reportShardErrorWithFlags(sh.Shard, errCount, false, msg, err) + e.reportShardErrorWithFlags(sh.Shard, errCount, msg, err) } // reportShardError checks that the amount of errors doesn't exceed the configured threshold. @@ -153,13 +157,12 @@ func (e *StorageEngine) reportShardError( errCount := sh.errorCount.Add(1) sh.Shard.IncErrorCounter() - e.reportShardErrorWithFlags(sh.Shard, errCount, true, msg, err, fields...) + e.reportShardErrorWithFlags(sh.Shard, errCount, msg, err, fields...) } func (e *StorageEngine) reportShardErrorWithFlags( sh *shard.Shard, errCount uint32, - block bool, msg string, err error, fields ...zap.Field, @@ -175,23 +178,20 @@ func (e *StorageEngine) reportShardErrorWithFlags( return } - if block { - e.moveToDegraded(sh, errCount) - } else { - req := setModeRequest{ - errorCount: errCount, - sh: sh, - } + req := setModeRequest{ + errorCount: errCount, + sh: sh, + isMeta: errors.As(err, new(metaerr.Error)), + } - select { - case e.setModeCh <- req: - default: - // For background workers we can have a lot of such errors, - // thus logging is done with DEBUG level. - e.log.Debug(logs.EngineModeChangeIsInProgressIgnoringSetmodeRequest, - zap.Stringer("shard_id", sid), - zap.Uint32("error_count", errCount)) - } + select { + case e.setModeCh <- req: + default: + // For background workers we can have a lot of such errors, + // thus logging is done with DEBUG level. + e.log.Debug(logs.EngineModeChangeIsInProgressIgnoringSetmodeRequest, + zap.Stringer("shard_id", sid), + zap.Uint32("error_count", errCount)) } } diff --git a/pkg/local_object_storage/engine/error_test.go b/pkg/local_object_storage/engine/error_test.go index ec4287bdd..535435ceb 100644 --- a/pkg/local_object_storage/engine/error_test.go +++ b/pkg/local_object_storage/engine/error_test.go @@ -7,6 +7,7 @@ import ( "path/filepath" "strconv" "testing" + "time" "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/core/object" "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/blobstor" @@ -153,7 +154,7 @@ func TestErrorReporting(t *testing.T) { for i := uint32(0); i < 2; i++ { _, err = te.ng.Get(context.Background(), GetPrm{addr: object.AddressOf(obj)}) require.Error(t, err) - checkShardState(t, te.ng, te.shards[0].id, errThreshold+i, mode.DegradedReadOnly) + checkShardState(t, te.ng, te.shards[0].id, errThreshold+i, mode.ReadOnly) checkShardState(t, te.ng, te.shards[1].id, 0, mode.ReadWrite) } @@ -229,6 +230,8 @@ func checkShardState(t *testing.T, e *StorageEngine, id *shard.ID, errCount uint sh := e.shards[id.String()] e.mtx.RUnlock() - require.Equal(t, errCount, sh.errorCount.Load()) - require.Equal(t, mode, sh.GetMode()) + require.Eventually(t, func() bool { + return errCount == sh.errorCount.Load() && + mode == sh.GetMode() + }, 10*time.Second, 10*time.Millisecond, "shard mode doesn't changed to expected state in 10 seconds") } diff --git a/pkg/local_object_storage/engine/head.go b/pkg/local_object_storage/engine/head.go index ba5e7cc1d..d9754dc87 100644 --- a/pkg/local_object_storage/engine/head.go +++ b/pkg/local_object_storage/engine/head.go @@ -85,6 +85,7 @@ func (e *StorageEngine) head(ctx context.Context, prm HeadPrm) (HeadRes, error) shPrm.SetRaw(prm.raw) e.iterateOverSortedShards(prm.addr, func(_ int, sh hashedShard) (stop bool) { + shPrm.ShardLooksBad = sh.errorCount.Load() >= e.errorsThreshold res, err := sh.Head(ctx, shPrm) if err != nil { switch { diff --git a/pkg/local_object_storage/engine/tree.go b/pkg/local_object_storage/engine/tree.go index 52ea399fe..8bacdba76 100644 --- a/pkg/local_object_storage/engine/tree.go +++ b/pkg/local_object_storage/engine/tree.go @@ -210,7 +210,7 @@ func (e *StorageEngine) TreeGetChildren(ctx context.Context, cid cidSDK.ID, tree } // TreeSortedByFilename implements the pilorama.Forest interface. -func (e *StorageEngine) TreeSortedByFilename(ctx context.Context, cid cidSDK.ID, treeID string, nodeID pilorama.Node, last string, count int) ([]pilorama.NodeInfo, string, error) { +func (e *StorageEngine) TreeSortedByFilename(ctx context.Context, cid cidSDK.ID, treeID string, nodeID pilorama.Node, last *string, count int) ([]pilorama.NodeInfo, *string, error) { ctx, span := tracing.StartSpanFromContext(ctx, "StorageEngine.TreeSortedByFilename", trace.WithAttributes( attribute.String("container_id", cid.EncodeToString()), @@ -222,7 +222,7 @@ func (e *StorageEngine) TreeSortedByFilename(ctx context.Context, cid cidSDK.ID, var err error var nodes []pilorama.NodeInfo - var cursor string + var cursor *string for _, sh := range e.sortShards(cid) { nodes, cursor, err = sh.TreeSortedByFilename(ctx, cid, treeID, nodeID, last, count) if err != nil { diff --git a/pkg/local_object_storage/metabase/counter.go b/pkg/local_object_storage/metabase/counter.go index dc85b4697..4d860261c 100644 --- a/pkg/local_object_storage/metabase/counter.go +++ b/pkg/local_object_storage/metabase/counter.go @@ -232,14 +232,19 @@ func (db *DB) ContainerCount(ctx context.Context, id cid.ID) (ObjectCounters, er } func (db *DB) incCounters(tx *bbolt.Tx, cnrID cid.ID, isUserObject bool) error { - if err := db.updateShardObjectCounter(tx, phy, 1, true); err != nil { + b := tx.Bucket(shardInfoBucket) + if b == nil { + return db.incContainerObjectCounter(tx, cnrID, isUserObject) + } + + if err := db.updateShardObjectCounterBucket(b, phy, 1, true); err != nil { return fmt.Errorf("could not increase phy object counter: %w", err) } - if err := db.updateShardObjectCounter(tx, logical, 1, true); err != nil { + if err := db.updateShardObjectCounterBucket(b, logical, 1, true); err != nil { return fmt.Errorf("could not increase logical object counter: %w", err) } if isUserObject { - if err := db.updateShardObjectCounter(tx, user, 1, true); err != nil { + if err := db.updateShardObjectCounterBucket(b, user, 1, true); err != nil { return fmt.Errorf("could not increase user object counter: %w", err) } } @@ -252,6 +257,10 @@ func (db *DB) updateShardObjectCounter(tx *bbolt.Tx, typ objectType, delta uint6 return nil } + return db.updateShardObjectCounterBucket(b, typ, delta, inc) +} + +func (*DB) updateShardObjectCounterBucket(b *bbolt.Bucket, typ objectType, delta uint64, inc bool) error { var counter uint64 var counterKey []byte diff --git a/pkg/local_object_storage/metabase/storage_id.go b/pkg/local_object_storage/metabase/storage_id.go index f9767935c..6d620b41a 100644 --- a/pkg/local_object_storage/metabase/storage_id.go +++ b/pkg/local_object_storage/metabase/storage_id.go @@ -36,6 +36,14 @@ func (r StorageIDRes) StorageID() []byte { // StorageID returns storage descriptor for objects from the blobstor. // It is put together with the object can makes get/delete operation faster. func (db *DB) StorageID(ctx context.Context, prm StorageIDPrm) (res StorageIDRes, err error) { + var ( + startedAt = time.Now() + success = false + ) + defer func() { + db.metrics.AddMethodDuration("StorageID", time.Since(startedAt), success) + }() + _, span := tracing.StartSpanFromContext(ctx, "metabase.StorageID", trace.WithAttributes( attribute.String("address", prm.addr.EncodeToString()), @@ -54,7 +62,7 @@ func (db *DB) StorageID(ctx context.Context, prm StorageIDPrm) (res StorageIDRes return err }) - + success = err == nil return res, metaerr.Wrap(err) } diff --git a/pkg/local_object_storage/pilorama/boltdb.go b/pkg/local_object_storage/pilorama/boltdb.go index 29a9306b6..972b949b5 100644 --- a/pkg/local_object_storage/pilorama/boltdb.go +++ b/pkg/local_object_storage/pilorama/boltdb.go @@ -1003,7 +1003,7 @@ func (t *boltForest) hasFewChildren(b *bbolt.Bucket, nodeID Node, threshold int) } // TreeSortedByFilename implements the Forest interface. -func (t *boltForest) TreeSortedByFilename(ctx context.Context, cid cidSDK.ID, treeID string, nodeID Node, last string, count int) ([]NodeInfo, string, error) { +func (t *boltForest) TreeSortedByFilename(ctx context.Context, cid cidSDK.ID, treeID string, nodeID Node, last *string, count int) ([]NodeInfo, *string, error) { var ( startedAt = time.Now() success = false @@ -1025,7 +1025,7 @@ func (t *boltForest) TreeSortedByFilename(ctx context.Context, cid cidSDK.ID, tr defer t.modeMtx.RUnlock() if t.mode.NoMetabase() { - return nil, "", ErrDegradedMode + return nil, last, ErrDegradedMode } h := newHeap(last, count) @@ -1069,20 +1069,25 @@ func (t *boltForest) TreeSortedByFilename(ctx context.Context, cid cidSDK.ID, tr } if fewChildren { - result = sortAndCut(result, []byte(last)) + result = sortAndCut(result, last) } if len(result) != 0 { - last = string(result[len(result)-1].Meta.GetAttr(AttributeFilename)) + s := string(result[len(result)-1].Meta.GetAttr(AttributeFilename)) + last = &s } return result, last, metaerr.Wrap(err) } -func sortAndCut(result []NodeInfo, last []byte) []NodeInfo { +func sortAndCut(result []NodeInfo, last *string) []NodeInfo { + var lastBytes []byte + if last != nil { + lastBytes = []byte(*last) + } sort.Slice(result, func(i, j int) bool { return bytes.Compare(result[i].Meta.GetAttr(AttributeFilename), result[j].Meta.GetAttr(AttributeFilename)) == -1 }) for i := range result { - if bytes.Compare(last, result[i].Meta.GetAttr(AttributeFilename)) == -1 { + if lastBytes == nil || bytes.Compare(lastBytes, result[i].Meta.GetAttr(AttributeFilename)) == -1 { return result[i:] } } diff --git a/pkg/local_object_storage/pilorama/forest.go b/pkg/local_object_storage/pilorama/forest.go index fedd3f90d..b45a77b99 100644 --- a/pkg/local_object_storage/pilorama/forest.go +++ b/pkg/local_object_storage/pilorama/forest.go @@ -156,11 +156,11 @@ func (f *memoryForest) TreeGetMeta(_ context.Context, cid cid.ID, treeID string, } // TreeSortedByFilename implements the Forest interface. -func (f *memoryForest) TreeSortedByFilename(_ context.Context, cid cid.ID, treeID string, nodeID Node, start string, count int) ([]NodeInfo, string, error) { +func (f *memoryForest) TreeSortedByFilename(_ context.Context, cid cid.ID, treeID string, nodeID Node, start *string, count int) ([]NodeInfo, *string, error) { fullID := cid.String() + "/" + treeID s, ok := f.treeMap[fullID] if !ok { - return nil, "", ErrTreeNotFound + return nil, start, ErrTreeNotFound } if count == 0 { return nil, start, nil @@ -169,7 +169,14 @@ func (f *memoryForest) TreeSortedByFilename(_ context.Context, cid cid.ID, treeI children := s.tree.getChildren(nodeID) res := make([]NodeInfo, 0, len(children)) for _, childID := range children { - if len(s.infoMap[childID].Meta.GetAttr(AttributeFilename)) == 0 { + var found bool + for _, kv := range s.infoMap[childID].Meta.Items { + if kv.Key == AttributeFilename { + found = true + break + } + } + if !found { continue } res = append(res, NodeInfo{ @@ -179,22 +186,24 @@ func (f *memoryForest) TreeSortedByFilename(_ context.Context, cid cid.ID, treeI }) } if len(res) == 0 { - return res, "", nil + return res, start, nil } sort.Slice(res, func(i, j int) bool { return bytes.Compare(res[i].Meta.GetAttr(AttributeFilename), res[j].Meta.GetAttr(AttributeFilename)) == -1 }) for i := range res { - if string(res[i].Meta.GetAttr(AttributeFilename)) > start { + if start == nil || string(res[i].Meta.GetAttr(AttributeFilename)) > *start { finish := i + count if len(res) < finish { finish = len(res) } - return res[i:finish], string(res[finish-1].Meta.GetAttr(AttributeFilename)), nil + last := string(res[finish-1].Meta.GetAttr(AttributeFilename)) + return res[i:finish], &last, nil } } - return nil, string(res[len(res)-1].Meta.GetAttr(AttributeFilename)), nil + last := string(res[len(res)-1].Meta.GetAttr(AttributeFilename)) + return nil, &last, nil } // TreeGetChildren implements the Forest interface. diff --git a/pkg/local_object_storage/pilorama/forest_test.go b/pkg/local_object_storage/pilorama/forest_test.go index 6c1bc343f..9da0177cc 100644 --- a/pkg/local_object_storage/pilorama/forest_test.go +++ b/pkg/local_object_storage/pilorama/forest_test.go @@ -16,7 +16,6 @@ import ( cidSDK "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id" cidtest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id/test" objectSDK "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object" - "github.com/davecgh/go-spew/spew" "github.com/google/uuid" "github.com/stretchr/testify/require" "golang.org/x/sync/errgroup" @@ -216,7 +215,7 @@ func BenchmarkForestSortedIteration(b *testing.B) { b.Run(providers[i].name+",root", func(b *testing.B) { for i := 0; i < b.N; i++ { - res, _, err := f.TreeSortedByFilename(context.Background(), cnr, treeID, RootID, "", 100) + res, _, err := f.TreeSortedByFilename(context.Background(), cnr, treeID, RootID, nil, 100) if err != nil || len(res) != 100 { b.Fatalf("err %v, count %d", err, len(res)) } @@ -224,7 +223,7 @@ func BenchmarkForestSortedIteration(b *testing.B) { }) b.Run(providers[i].name+",leaf", func(b *testing.B) { for i := 0; i < b.N; i++ { - res, _, err := f.TreeSortedByFilename(context.Background(), cnr, treeID, 1, "", 100) + res, _, err := f.TreeSortedByFilename(context.Background(), cnr, treeID, 1, nil, 100) if err != nil || len(res) != 0 { b.FailNow() } @@ -247,14 +246,14 @@ func testForestTreeSortedIteration(t *testing.T, s ForestStorage) { cid := cidtest.ID() d := CIDDescriptor{cid, 0, 1} treeID := "version" - treeAdd := func(t *testing.T, ts int) { + treeAdd := func(t *testing.T, ts int, filename string) { _, err := s.TreeMove(context.Background(), d, treeID, &Move{ Child: RootID + uint64(ts), Parent: RootID, Meta: Meta{ Time: Timestamp(ts), Items: []KeyValue{ - {Key: AttributeFilename, Value: []byte(strconv.Itoa(ts))}, + {Key: AttributeFilename, Value: []byte(filename)}, }, }, }) @@ -262,20 +261,20 @@ func testForestTreeSortedIteration(t *testing.T, s ForestStorage) { } const count = 9 - for i := 0; i < count; i++ { - treeAdd(t, i+1) + treeAdd(t, 1, "") + for i := 1; i < count; i++ { + treeAdd(t, i+1, strconv.Itoa(i+1)) } var result []NodeInfo - treeAppend := func(t *testing.T, last string, count int) string { + treeAppend := func(t *testing.T, last *string, count int) *string { res, cursor, err := s.TreeSortedByFilename(context.Background(), d.CID, treeID, RootID, last, count) require.NoError(t, err) result = append(result, res...) - spew.Dump(last, res) return cursor } - last := treeAppend(t, "", 2) + last := treeAppend(t, nil, 2) last = treeAppend(t, last, 3) last = treeAppend(t, last, 0) last = treeAppend(t, last, 1) @@ -284,7 +283,11 @@ func testForestTreeSortedIteration(t *testing.T, s ForestStorage) { require.Len(t, result, count) for i := range result { require.Equal(t, RootID+uint64(i+1), result[i].ID) - require.Equal(t, strconv.Itoa(RootID+i+1), string(result[i].Meta.GetAttr(AttributeFilename))) + if i == 0 { + require.Equal(t, "", string(result[i].Meta.GetAttr(AttributeFilename))) + } else { + require.Equal(t, strconv.Itoa(RootID+i+1), string(result[i].Meta.GetAttr(AttributeFilename))) + } } } @@ -343,7 +346,7 @@ func testForestTreeSortedByFilename(t *testing.T, s ForestStorage) { } getChildren := func(t *testing.T, id Node) []NodeInfo { - res, _, err := s.TreeSortedByFilename(context.Background(), d.CID, treeID, id, "", len(items)) + res, _, err := s.TreeSortedByFilename(context.Background(), d.CID, treeID, id, nil, len(items)) require.NoError(t, err) return res } diff --git a/pkg/local_object_storage/pilorama/heap.go b/pkg/local_object_storage/pilorama/heap.go index 667283c2a..b40fb0849 100644 --- a/pkg/local_object_storage/pilorama/heap.go +++ b/pkg/local_object_storage/pilorama/heap.go @@ -17,6 +17,7 @@ func (h filenameHeap) Swap(i, j int) { h[i], h[j] = h[j], h[i] } func (h *filenameHeap) Push(x any) { *h = append(*h, x.(heapInfo)) } + func (h *filenameHeap) Pop() any { old := *h n := len(old) @@ -27,13 +28,13 @@ func (h *filenameHeap) Pop() any { // fixedHeap maintains a fixed number of smallest elements started at some point. type fixedHeap struct { - start string + start *string max string count int h *filenameHeap } -func newHeap(start string, count int) *fixedHeap { +func newHeap(start *string, count int) *fixedHeap { h := new(filenameHeap) heap.Init(h) @@ -46,7 +47,7 @@ func newHeap(start string, count int) *fixedHeap { } func (h *fixedHeap) push(id Node, filename string) bool { - if filename == "" || filename <= h.start { + if h.start != nil && filename <= *h.start { return false } heap.Push(h.h, heapInfo{id: id, filename: filename}) diff --git a/pkg/local_object_storage/pilorama/interface.go b/pkg/local_object_storage/pilorama/interface.go index 79449e080..f7f5a85b1 100644 --- a/pkg/local_object_storage/pilorama/interface.go +++ b/pkg/local_object_storage/pilorama/interface.go @@ -35,7 +35,7 @@ type Forest interface { TreeGetChildren(ctx context.Context, cid cidSDK.ID, treeID string, nodeID Node) ([]NodeInfo, error) // TreeSortedByFilename returns children of the node with the specified ID. The nodes are sorted by the filename attribute.. // Should return ErrTreeNotFound if the tree is not found, and empty result if the node is not in the tree. - TreeSortedByFilename(ctx context.Context, cid cidSDK.ID, treeID string, nodeID Node, last string, count int) ([]NodeInfo, string, error) + TreeSortedByFilename(ctx context.Context, cid cidSDK.ID, treeID string, nodeID Node, last *string, count int) ([]NodeInfo, *string, error) // TreeGetOpLog returns first log operation stored at or above the height. // In case no such operation is found, empty Move and nil error should be returned. TreeGetOpLog(ctx context.Context, cid cidSDK.ID, treeID string, height uint64) (Move, error) diff --git a/pkg/local_object_storage/shard/head.go b/pkg/local_object_storage/shard/head.go index a0ec231af..9d5d31260 100644 --- a/pkg/local_object_storage/shard/head.go +++ b/pkg/local_object_storage/shard/head.go @@ -13,8 +13,9 @@ import ( // HeadPrm groups the parameters of Head operation. type HeadPrm struct { - addr oid.Address - raw bool + addr oid.Address + raw bool + ShardLooksBad bool } // HeadRes groups the resulting values of Head operation. @@ -59,7 +60,8 @@ func (s *Shard) Head(ctx context.Context, prm HeadPrm) (HeadRes, error) { var obj *objectSDK.Object var err error - if s.GetMode().NoMetabase() { + mode := s.GetMode() + if mode.NoMetabase() || (mode.ReadOnly() && prm.ShardLooksBad) { var getPrm GetPrm getPrm.SetAddress(prm.addr) getPrm.SetIgnoreMeta(true) diff --git a/pkg/local_object_storage/shard/shard.go b/pkg/local_object_storage/shard/shard.go index 8368f6db4..266f360a6 100644 --- a/pkg/local_object_storage/shard/shard.go +++ b/pkg/local_object_storage/shard/shard.go @@ -469,6 +469,7 @@ func (s *Shard) updateMetrics(ctx context.Context) { s.setContainerObjectsCount(contID.EncodeToString(), logical, count.Logic) s.setContainerObjectsCount(contID.EncodeToString(), user, count.User) } + s.cfg.metricsWriter.SetMode(s.info.Mode) } // incObjectCounter increment both physical and logical object diff --git a/pkg/local_object_storage/shard/tree.go b/pkg/local_object_storage/shard/tree.go index 9a78f99ea..e92a61e5b 100644 --- a/pkg/local_object_storage/shard/tree.go +++ b/pkg/local_object_storage/shard/tree.go @@ -184,7 +184,7 @@ func (s *Shard) TreeGetChildren(ctx context.Context, cid cidSDK.ID, treeID strin } // TreeSortedByFilename implements the pilorama.Forest interface. -func (s *Shard) TreeSortedByFilename(ctx context.Context, cid cidSDK.ID, treeID string, nodeID pilorama.Node, last string, count int) ([]pilorama.NodeInfo, string, error) { +func (s *Shard) TreeSortedByFilename(ctx context.Context, cid cidSDK.ID, treeID string, nodeID pilorama.Node, last *string, count int) ([]pilorama.NodeInfo, *string, error) { ctx, span := tracing.StartSpanFromContext(ctx, "Shard.TreeSortedByFilename", trace.WithAttributes( attribute.String("shard_id", s.ID().String()), @@ -196,14 +196,14 @@ func (s *Shard) TreeSortedByFilename(ctx context.Context, cid cidSDK.ID, treeID defer span.End() if s.pilorama == nil { - return nil, "", ErrPiloramaDisabled + return nil, last, ErrPiloramaDisabled } s.m.RLock() defer s.m.RUnlock() if s.info.Mode.NoMetabase() { - return nil, "", ErrDegradedMode + return nil, last, ErrDegradedMode } return s.pilorama.TreeSortedByFilename(ctx, cid, treeID, nodeID, last, count) } diff --git a/pkg/morph/client/actor.go b/pkg/morph/client/actor.go index 8c283a672..b6718dea5 100644 --- a/pkg/morph/client/actor.go +++ b/pkg/morph/client/actor.go @@ -6,12 +6,14 @@ import ( "github.com/nspcc-dev/neo-go/pkg/core/transaction" "github.com/nspcc-dev/neo-go/pkg/neorpc/result" "github.com/nspcc-dev/neo-go/pkg/rpcclient/actor" + "github.com/nspcc-dev/neo-go/pkg/rpcclient/invoker" "github.com/nspcc-dev/neo-go/pkg/util" "github.com/nspcc-dev/neo-go/pkg/vm/stackitem" ) type actorProvider interface { GetActor() *actor.Actor + GetRPCActor() actor.RPCActor } // Client switches an established connection with neo-go if it is broken. @@ -132,3 +134,11 @@ func (a *SwitchRPCGuardedActor) TerminateSession(sessionID uuid.UUID) error { func (a *SwitchRPCGuardedActor) TraverseIterator(sessionID uuid.UUID, iterator *result.Iterator, num int) ([]stackitem.Item, error) { return a.actorProvider.GetActor().TraverseIterator(sessionID, iterator, num) } + +func (a *SwitchRPCGuardedActor) GetRPCActor() actor.RPCActor { + return a.actorProvider.GetRPCActor() +} + +func (a *SwitchRPCGuardedActor) GetRPCInvoker() invoker.RPCInvoke { + return a.actorProvider.GetRPCActor() +} diff --git a/pkg/morph/client/client.go b/pkg/morph/client/client.go index 2570bc2c9..c9f819f04 100644 --- a/pkg/morph/client/client.go +++ b/pkg/morph/client/client.go @@ -579,3 +579,10 @@ func (c *Client) GetActor() *actor.Actor { return c.rpcActor } + +func (c *Client) GetRPCActor() actor.RPCActor { + c.switchLock.RLock() + defer c.switchLock.RUnlock() + + return c.client +} diff --git a/pkg/morph/client/constructor.go b/pkg/morph/client/constructor.go index 50a9572d4..956ebfb31 100644 --- a/pkg/morph/client/constructor.go +++ b/pkg/morph/client/constructor.go @@ -4,6 +4,7 @@ import ( "context" "errors" "fmt" + "sync/atomic" "time" "git.frostfs.info/TrueCloudLab/frostfs-node/internal/logs" @@ -48,6 +49,8 @@ type cfg struct { switchInterval time.Duration morphCacheMetrics metrics.MorphCacheMetrics + + cmode *atomic.Bool } const ( @@ -311,3 +314,11 @@ func WithMorphCacheMetrics(morphCacheMetrics metrics.MorphCacheMetrics) Option { c.morphCacheMetrics = morphCacheMetrics } } + +// WithCompatibilityMode indicates that Client is working in compatibility mode +// in this mode we need to keep backward compatibility with services with previous version. +func WithCompatibilityMode(cmode *atomic.Bool) Option { + return func(c *cfg) { + c.cmode = cmode + } +} diff --git a/pkg/morph/client/notary.go b/pkg/morph/client/notary.go index 1665fec1d..564384a3d 100644 --- a/pkg/morph/client/notary.go +++ b/pkg/morph/client/notary.go @@ -566,14 +566,19 @@ func (c *Client) notaryCosigners(invokedByAlpha bool, ir []*keys.PublicKey, comm } s := make([]actor.SignerAccount, 2, 3) // Proxy contract that will pay for the execution. + // Do not change this: + // We must be able to call NNS contract indirectly from the Container contract. + // Thus, CalledByEntry is not sufficient. + // In future we may restrict this to all the usecases we have. + scopes := transaction.Global + if c.cfg.cmode != nil && c.cfg.cmode.Load() { + // Set it to None to keep ability to send notary requests during upgrade + scopes = transaction.None + } s[0] = actor.SignerAccount{ Signer: transaction.Signer{ Account: c.notary.proxy, - // Do not change this: - // We must be able to call NNS contract indirectly from the Container contract. - // Thus, CalledByEntry is not sufficient. - // In future we may restrict this to all the usecases we have. - Scopes: transaction.Global, + Scopes: scopes, }, Account: notary.FakeContractAccount(c.notary.proxy), } diff --git a/pkg/services/container/ape.go b/pkg/services/container/ape.go index 83361257a..02549bded 100644 --- a/pkg/services/container/ape.go +++ b/pkg/services/container/ape.go @@ -15,6 +15,7 @@ import ( "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/refs" session "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/session" "git.frostfs.info/TrueCloudLab/frostfs-contract/frostfsid/client" + aperequest "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/ape/request" containercore "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/core/container" "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/core/netmap" "git.frostfs.info/TrueCloudLab/frostfs-observability/tracing" @@ -26,7 +27,6 @@ import ( "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/user" apechain "git.frostfs.info/TrueCloudLab/policy-engine/pkg/chain" policyengine "git.frostfs.info/TrueCloudLab/policy-engine/pkg/engine" - "git.frostfs.info/TrueCloudLab/policy-engine/pkg/resource" nativeschema "git.frostfs.info/TrueCloudLab/policy-engine/schema/native" "github.com/nspcc-dev/neo-go/pkg/crypto/keys" "github.com/nspcc-dev/neo-go/pkg/util" @@ -148,18 +148,21 @@ func (ac *apeChecker) List(ctx context.Context, req *container.ListRequest) (*co return nil, err } - request := &apeRequest{ - resource: &apeResource{ - name: resourceName(namespace, ""), - props: make(map[string]string), - }, - op: nativeschema.MethodListContainers, - props: reqProps, - } + request := aperequest.NewRequest( + nativeschema.MethodListContainers, + aperequest.NewResource( + resourceName(namespace, ""), + make(map[string]string), + ), + reqProps, + ) - s, found, err := ac.router.IsAllowed(apechain.Ingress, - policyengine.NewRequestTargetWithNamespace(namespace), - request) + rt := policyengine.NewRequestTargetWithNamespace(namespace) + rt.User = &policyengine.Target{ + Type: policyengine.User, + Name: fmt.Sprintf("%s:%s", namespace, pk.Address()), + } + s, found, err := ac.router.IsAllowed(apechain.Ingress, rt, request) if err != nil { return nil, err } @@ -193,18 +196,21 @@ func (ac *apeChecker) Put(ctx context.Context, req *container.PutRequest) (*cont return nil, err } - request := &apeRequest{ - resource: &apeResource{ - name: resourceName(namespace, ""), - props: make(map[string]string), - }, - op: nativeschema.MethodPutContainer, - props: reqProps, - } + request := aperequest.NewRequest( + nativeschema.MethodPutContainer, + aperequest.NewResource( + resourceName(namespace, ""), + make(map[string]string), + ), + reqProps, + ) - s, found, err := ac.router.IsAllowed(apechain.Ingress, - policyengine.NewRequestTargetWithNamespace(namespace), - request) + rt := policyengine.NewRequestTargetWithNamespace(namespace) + rt.User = &policyengine.Target{ + Type: policyengine.User, + Name: fmt.Sprintf("%s:%s", namespace, pk.Address()), + } + s, found, err := ac.router.IsAllowed(apechain.Ingress, rt, request) if err != nil { return nil, err } @@ -277,7 +283,7 @@ func (ac *apeChecker) validateContainerBoundedOperation(containerID *refs.Contai return err } - reqProps, err := ac.getRequestProps(mh, vh, cont, id) + reqProps, pk, err := ac.getRequestProps(mh, vh, cont, id) if err != nil { return err } @@ -288,17 +294,17 @@ func (ac *apeChecker) validateContainerBoundedOperation(containerID *refs.Contai namespace = cntNamespace } - request := &apeRequest{ - resource: &apeResource{ - name: resourceName(namespace, id.EncodeToString()), - props: ac.getContainerProps(cont), - }, - op: op, - props: reqProps, - } + request := aperequest.NewRequest( + op, + aperequest.NewResource( + resourceName(namespace, id.EncodeToString()), + ac.getContainerProps(cont), + ), + reqProps, + ) s, found, err := ac.router.IsAllowed(apechain.Ingress, - policyengine.NewRequestTarget(namespace, id.EncodeToString()), + policyengine.NewRequestTargetExtended(namespace, id.EncodeToString(), fmt.Sprintf("%s:%s", namespace, pk.Address()), nil), request) if err != nil { return err @@ -329,40 +335,6 @@ func getContainerID(reqContID *refs.ContainerID) (cid.ID, error) { return id, nil } -type apeRequest struct { - resource *apeResource - op string - props map[string]string -} - -// Operation implements resource.Request. -func (r *apeRequest) Operation() string { - return r.op -} - -// Property implements resource.Request. -func (r *apeRequest) Property(key string) string { - return r.props[key] -} - -// Resource implements resource.Request. -func (r *apeRequest) Resource() resource.Resource { - return r.resource -} - -type apeResource struct { - name string - props map[string]string -} - -func (r *apeResource) Name() string { - return r.name -} - -func (r *apeResource) Property(key string) string { - return r.props[key] -} - func resourceName(namespace string, container string) string { if namespace == "" && container == "" { return nativeschema.ResourceFormatRootContainers @@ -384,19 +356,19 @@ func (ac *apeChecker) getContainerProps(c *containercore.Container) map[string]s func (ac *apeChecker) getRequestProps(mh *session.RequestMetaHeader, vh *session.RequestVerificationHeader, cont *containercore.Container, cnrID cid.ID, -) (map[string]string, error) { +) (map[string]string, *keys.PublicKey, error) { actor, pk, err := ac.getActorAndPublicKey(mh, vh, cnrID) if err != nil { - return nil, err + return nil, nil, err } role, err := ac.getRole(actor, pk, cont, cnrID) if err != nil { - return nil, err + return nil, nil, err } return map[string]string{ nativeschema.PropertyKeyActorPublicKey: hex.EncodeToString(pk.Bytes()), nativeschema.PropertyKeyActorRole: role, - }, nil + }, pk, nil } func (ac *apeChecker) getRole(actor *user.ID, pk *keys.PublicKey, cont *containercore.Container, cnrID cid.ID) (string, error) { diff --git a/pkg/services/control/server/policy_engine.go b/pkg/services/control/server/policy_engine.go index 16b365b21..7ec3d58ac 100644 --- a/pkg/services/control/server/policy_engine.go +++ b/pkg/services/control/server/policy_engine.go @@ -19,6 +19,10 @@ func apeTarget(chainTarget *control.ChainTarget) (engine.Target, error) { return engine.ContainerTarget(chainTarget.GetName()), nil case control.ChainTarget_NAMESPACE: return engine.NamespaceTarget(chainTarget.GetName()), nil + case control.ChainTarget_USER: + return engine.UserTarget(chainTarget.GetName()), nil + case control.ChainTarget_GROUP: + return engine.GroupTarget(chainTarget.GetName()), nil default: } return engine.Target{}, status.Error(codes.InvalidArgument, @@ -42,6 +46,16 @@ func controlTarget(chainTarget *engine.Target) (control.ChainTarget, error) { Name: nm, Type: control.ChainTarget_NAMESPACE, }, nil + case engine.User: + return control.ChainTarget{ + Name: chainTarget.Name, + Type: control.ChainTarget_USER, + }, nil + case engine.Group: + return control.ChainTarget{ + Name: chainTarget.Name, + Type: control.ChainTarget_GROUP, + }, nil default: } return control.ChainTarget{}, status.Error(codes.InvalidArgument, diff --git a/pkg/services/control/service.pb.go b/pkg/services/control/service.pb.go index 345110bab..9c597beec 100644 --- a/pkg/services/control/service.pb.go +++ b/pkg/services/control/service.pb.go @@ -3188,7 +3188,8 @@ type FlushCacheRequest_Body struct { // ID of the shard. Shard_ID [][]byte `protobuf:"bytes,1,rep,name=shard_ID,json=shardID,proto3" json:"shard_ID,omitempty"` - // If true, then writecache will be left in read-only mode after flush completed. + // If true, then writecache will be left in read-only mode after flush + // completed. Seal bool `protobuf:"varint,2,opt,name=seal,proto3" json:"seal,omitempty"` } @@ -3525,7 +3526,8 @@ type GetShardEvacuationStatusResponse_Body struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - // Total objects to evacuate count. The value is approximate, so evacuated + failed + skipped == total is not guaranteed after completion. + // Total objects to evacuate count. The value is approximate, so evacuated + + // failed + skipped == total is not guaranteed after completion. TotalObjects uint64 `protobuf:"varint,1,opt,name=total_objects,json=totalObjects,proto3" json:"total_objects,omitempty"` // Evacuated objects count. EvacuatedObjects uint64 `protobuf:"varint,2,opt,name=evacuated_objects,json=evacuatedObjects,proto3" json:"evacuated_objects,omitempty"` diff --git a/pkg/services/control/service.proto b/pkg/services/control/service.proto index 04ea62e0e..94032b346 100644 --- a/pkg/services/control/service.proto +++ b/pkg/services/control/service.proto @@ -6,658 +6,656 @@ import "pkg/services/control/types.proto"; option go_package = "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/services/control"; -// `ControlService` provides an interface for internal work with the storage node. +// `ControlService` provides an interface for internal work with the storage +// node. service ControlService { - // Performs health check of the storage node. - rpc HealthCheck (HealthCheckRequest) returns (HealthCheckResponse); + // Performs health check of the storage node. + rpc HealthCheck(HealthCheckRequest) returns (HealthCheckResponse); - // Sets status of the storage node in FrostFS network map. - rpc SetNetmapStatus (SetNetmapStatusRequest) returns (SetNetmapStatusResponse); + // Sets status of the storage node in FrostFS network map. + rpc SetNetmapStatus(SetNetmapStatusRequest) returns (SetNetmapStatusResponse); - // Mark objects to be removed from node's local object storage. - rpc DropObjects (DropObjectsRequest) returns (DropObjectsResponse); + // Mark objects to be removed from node's local object storage. + rpc DropObjects(DropObjectsRequest) returns (DropObjectsResponse); - // Returns list that contains information about all shards of a node. - rpc ListShards (ListShardsRequest) returns (ListShardsResponse); + // Returns list that contains information about all shards of a node. + rpc ListShards(ListShardsRequest) returns (ListShardsResponse); - // Sets mode of the shard. - rpc SetShardMode (SetShardModeRequest) returns (SetShardModeResponse); + // Sets mode of the shard. + rpc SetShardMode(SetShardModeRequest) returns (SetShardModeResponse); - // Synchronizes all log operations for the specified tree. - rpc SynchronizeTree (SynchronizeTreeRequest) returns (SynchronizeTreeResponse); + // Synchronizes all log operations for the specified tree. + rpc SynchronizeTree(SynchronizeTreeRequest) returns (SynchronizeTreeResponse); - // EvacuateShard moves all data from one shard to the others. - // Deprecated: Use StartShardEvacuation/GetShardEvacuationStatus/StopShardEvacuation - rpc EvacuateShard (EvacuateShardRequest) returns (EvacuateShardResponse); + // EvacuateShard moves all data from one shard to the others. + // Deprecated: Use + // StartShardEvacuation/GetShardEvacuationStatus/StopShardEvacuation + rpc EvacuateShard(EvacuateShardRequest) returns (EvacuateShardResponse); - // StartShardEvacuation starts moving all data from one shard to the others. - rpc StartShardEvacuation (StartShardEvacuationRequest) returns (StartShardEvacuationResponse); + // StartShardEvacuation starts moving all data from one shard to the others. + rpc StartShardEvacuation(StartShardEvacuationRequest) + returns (StartShardEvacuationResponse); - // GetShardEvacuationStatus returns evacuation status. - rpc GetShardEvacuationStatus (GetShardEvacuationStatusRequest) returns (GetShardEvacuationStatusResponse); + // GetShardEvacuationStatus returns evacuation status. + rpc GetShardEvacuationStatus(GetShardEvacuationStatusRequest) + returns (GetShardEvacuationStatusResponse); - // ResetShardEvacuationStatus resets evacuation status if there is no running evacuation process. - rpc ResetShardEvacuationStatus (ResetShardEvacuationStatusRequest) returns (ResetShardEvacuationStatusResponse); + // ResetShardEvacuationStatus resets evacuation status if there is no running + // evacuation process. + rpc ResetShardEvacuationStatus(ResetShardEvacuationStatusRequest) + returns (ResetShardEvacuationStatusResponse); - // StopShardEvacuation stops moving all data from one shard to the others. - rpc StopShardEvacuation (StopShardEvacuationRequest) returns (StopShardEvacuationResponse); + // StopShardEvacuation stops moving all data from one shard to the others. + rpc StopShardEvacuation(StopShardEvacuationRequest) + returns (StopShardEvacuationResponse); - // FlushCache moves all data from one shard to the others. - rpc FlushCache (FlushCacheRequest) returns (FlushCacheResponse); + // FlushCache moves all data from one shard to the others. + rpc FlushCache(FlushCacheRequest) returns (FlushCacheResponse); - // Doctor performs storage restructuring operations on engine. - rpc Doctor (DoctorRequest) returns (DoctorResponse); + // Doctor performs storage restructuring operations on engine. + rpc Doctor(DoctorRequest) returns (DoctorResponse); - // Add local access policy engine overrides to a node. - rpc AddChainLocalOverride (AddChainLocalOverrideRequest) returns (AddChainLocalOverrideResponse); + // Add local access policy engine overrides to a node. + rpc AddChainLocalOverride(AddChainLocalOverrideRequest) + returns (AddChainLocalOverrideResponse); - // Get local access policy engine overrides stored in the node by chain id. - rpc GetChainLocalOverride (GetChainLocalOverrideRequest) returns (GetChainLocalOverrideResponse); + // Get local access policy engine overrides stored in the node by chain id. + rpc GetChainLocalOverride(GetChainLocalOverrideRequest) + returns (GetChainLocalOverrideResponse); - // List local access policy engine overrides stored in the node by container id. - rpc ListChainLocalOverrides (ListChainLocalOverridesRequest) returns (ListChainLocalOverridesResponse); + // List local access policy engine overrides stored in the node by container + // id. + rpc ListChainLocalOverrides(ListChainLocalOverridesRequest) + returns (ListChainLocalOverridesResponse); - // Remove local access policy engine overrides stored in the node by chaind id. - rpc RemoveChainLocalOverride (RemoveChainLocalOverrideRequest) returns (RemoveChainLocalOverrideResponse); + // Remove local access policy engine overrides stored in the node by chaind + // id. + rpc RemoveChainLocalOverride(RemoveChainLocalOverrideRequest) + returns (RemoveChainLocalOverrideResponse); - // Remove local access policy engine overrides stored in the node by chaind id. - rpc RemoveChainLocalOverridesByTarget (RemoveChainLocalOverridesByTargetRequest) returns (RemoveChainLocalOverridesByTargetResponse); + // Remove local access policy engine overrides stored in the node by chaind + // id. + rpc RemoveChainLocalOverridesByTarget( + RemoveChainLocalOverridesByTargetRequest) + returns (RemoveChainLocalOverridesByTargetResponse); - // List targets of the local APE overrides stored in the node. - rpc ListTargetsLocalOverrides (ListTargetsLocalOverridesRequest) returns (ListTargetsLocalOverridesResponse); + // List targets of the local APE overrides stored in the node. + rpc ListTargetsLocalOverrides(ListTargetsLocalOverridesRequest) + returns (ListTargetsLocalOverridesResponse); - // Flush objects from write-cache and move it to degraded read only mode. - rpc SealWriteCache(SealWriteCacheRequest) returns (SealWriteCacheResponse); + // Flush objects from write-cache and move it to degraded read only mode. + rpc SealWriteCache(SealWriteCacheRequest) returns (SealWriteCacheResponse); - // DetachShards detaches and closes shards. - rpc DetachShards(DetachShardsRequest) returns (DetachShardsResponse); + // DetachShards detaches and closes shards. + rpc DetachShards(DetachShardsRequest) returns (DetachShardsResponse); } // Health check request. message HealthCheckRequest { - // Health check request body. - message Body { - } + // Health check request body. + message Body {} - // Body of health check request message. - Body body = 1; + // Body of health check request message. + Body body = 1; - // Body signature. - Signature signature = 2; + // Body signature. + Signature signature = 2; } // Health check request. message HealthCheckResponse { - // Health check response body - message Body { - // Status of the storage node in FrostFS network map. - NetmapStatus netmap_status = 1; + // Health check response body + message Body { + // Status of the storage node in FrostFS network map. + NetmapStatus netmap_status = 1; - // Health status of storage node application. - HealthStatus health_status = 2; - } + // Health status of storage node application. + HealthStatus health_status = 2; + } - // Body of health check response message. - Body body = 1; + // Body of health check response message. + Body body = 1; - // Body signature. - Signature signature = 2; + // Body signature. + Signature signature = 2; } // Set netmap status request. message SetNetmapStatusRequest { - // Set netmap status request body. - message Body { - // New storage node status in FrostFS network map. - // If status is MAINTENANCE, the node checks whether maintenance is - // allowed in the network settings. In case of prohibition, the request - // is denied. Otherwise, node switches to local maintenance state. To - // force local maintenance, use `force_maintenance` flag. - NetmapStatus status = 1; + // Set netmap status request body. + message Body { + // New storage node status in FrostFS network map. + // If status is MAINTENANCE, the node checks whether maintenance is + // allowed in the network settings. In case of prohibition, the request + // is denied. Otherwise, node switches to local maintenance state. To + // force local maintenance, use `force_maintenance` flag. + NetmapStatus status = 1; - // MAINTENANCE status validation skip flag. If set, node starts local - // maintenance regardless of network settings. The flag MUST NOT be - // set for any other status. - bool force_maintenance = 2; - } + // MAINTENANCE status validation skip flag. If set, node starts local + // maintenance regardless of network settings. The flag MUST NOT be + // set for any other status. + bool force_maintenance = 2; + } - // Body of set netmap status request message. - Body body = 1; + // Body of set netmap status request message. + Body body = 1; - // Body signature. - Signature signature = 2; + // Body signature. + Signature signature = 2; } // Set netmap status response. message SetNetmapStatusResponse { - // Set netmap status response body - message Body { - } + // Set netmap status response body + message Body {} - // Body of set netmap status response message. - Body body = 1; + // Body of set netmap status response message. + Body body = 1; - // Body signature. - Signature signature = 2; + // Body signature. + Signature signature = 2; } // Request to drop the objects. message DropObjectsRequest { - // Request body structure. - message Body { - // List of object addresses to be removed. - // in FrostFS API binary format. - repeated bytes address_list = 1; - } + // Request body structure. + message Body { + // List of object addresses to be removed. + // in FrostFS API binary format. + repeated bytes address_list = 1; + } - // Body of the request message. - Body body = 1; + // Body of the request message. + Body body = 1; - // Body signature. - Signature signature = 2; + // Body signature. + Signature signature = 2; } // Response to request to drop the objects. message DropObjectsResponse { - // Response body structure. - message Body { - } + // Response body structure. + message Body {} - // Body of the response message. - Body body = 1; + // Body of the response message. + Body body = 1; - // Body signature. - Signature signature = 2; + // Body signature. + Signature signature = 2; } // Request to list all shards of the node. message ListShardsRequest { - // Request body structure. - message Body { - } + // Request body structure. + message Body {} - // Body of the request message. - Body body = 1; + // Body of the request message. + Body body = 1; - // Body signature. - Signature signature = 2; + // Body signature. + Signature signature = 2; } // ListShards response. message ListShardsResponse { - // Response body structure. - message Body { - // List of the node's shards. - repeated ShardInfo shards = 1; - } + // Response body structure. + message Body { + // List of the node's shards. + repeated ShardInfo shards = 1; + } - // Body of the response message. - Body body = 1; + // Body of the response message. + Body body = 1; - // Body signature. - Signature signature = 2; + // Body signature. + Signature signature = 2; } // Request to set mode of the shard. message SetShardModeRequest { - // Request body structure. - message Body { - // ID of the shard. - repeated bytes shard_ID = 1; + // Request body structure. + message Body { + // ID of the shard. + repeated bytes shard_ID = 1; - // Mode that requested to be set. - ShardMode mode = 2; + // Mode that requested to be set. + ShardMode mode = 2; - // Flag signifying whether error counter should be set to 0. - bool resetErrorCounter = 3; - } + // Flag signifying whether error counter should be set to 0. + bool resetErrorCounter = 3; + } - // Body of set shard mode request message. - Body body = 1; + // Body of set shard mode request message. + Body body = 1; - // Body signature. - Signature signature = 2; + // Body signature. + Signature signature = 2; } // SetShardMode response. message SetShardModeResponse { - // Response body structure. - message Body { - } + // Response body structure. + message Body {} - // Body of set shard mode response message. - Body body = 1; + // Body of set shard mode response message. + Body body = 1; - // Body signature. - Signature signature = 2; + // Body signature. + Signature signature = 2; } // SynchronizeTree request. message SynchronizeTreeRequest { - // Request body structure. - message Body { - bytes container_id = 1; - string tree_id = 2; - // Starting height for the synchronization. Can be omitted. - uint64 height = 3; - } + // Request body structure. + message Body { + bytes container_id = 1; + string tree_id = 2; + // Starting height for the synchronization. Can be omitted. + uint64 height = 3; + } - // Body of restore shard request message. - Body body = 1; + // Body of restore shard request message. + Body body = 1; - // Body signature. - Signature signature = 2; + // Body signature. + Signature signature = 2; } // SynchronizeTree response. message SynchronizeTreeResponse { - // Response body structure. - message Body { - } + // Response body structure. + message Body {} - // Body of restore shard response message. - Body body = 1; + // Body of restore shard response message. + Body body = 1; - // Body signature. - Signature signature = 2; + // Body signature. + Signature signature = 2; } - // EvacuateShard request. message EvacuateShardRequest { - // Request body structure. - message Body { - // ID of the shard. - repeated bytes shard_ID = 1; + // Request body structure. + message Body { + // ID of the shard. + repeated bytes shard_ID = 1; - // Flag indicating whether object read errors should be ignored. - bool ignore_errors = 2; - } + // Flag indicating whether object read errors should be ignored. + bool ignore_errors = 2; + } - Body body = 1; - Signature signature = 2; + Body body = 1; + Signature signature = 2; } // EvacuateShard response. message EvacuateShardResponse { - // Response body structure. - message Body { - uint32 count = 1; - } + // Response body structure. + message Body { uint32 count = 1; } - Body body = 1; - Signature signature = 2; + Body body = 1; + Signature signature = 2; } // FlushCache request. message FlushCacheRequest { - // Request body structure. - message Body { - // ID of the shard. - repeated bytes shard_ID = 1; - // If true, then writecache will be left in read-only mode after flush completed. - bool seal = 2; - } + // Request body structure. + message Body { + // ID of the shard. + repeated bytes shard_ID = 1; + // If true, then writecache will be left in read-only mode after flush + // completed. + bool seal = 2; + } - Body body = 1; - Signature signature = 2; + Body body = 1; + Signature signature = 2; } // FlushCache response. message FlushCacheResponse { - // Response body structure. - message Body { - } + // Response body structure. + message Body {} - Body body = 1; - Signature signature = 2; + Body body = 1; + Signature signature = 2; } - // Doctor request. message DoctorRequest { - // Request body structure. - message Body { - // Number of threads to use for the operation. - uint32 concurrency = 1; - // Flag to search engine for duplicate objects and leave only one copy. - bool remove_duplicates = 2; - } + // Request body structure. + message Body { + // Number of threads to use for the operation. + uint32 concurrency = 1; + // Flag to search engine for duplicate objects and leave only one copy. + bool remove_duplicates = 2; + } - Body body = 1; - Signature signature = 2; + Body body = 1; + Signature signature = 2; } // Doctor response. message DoctorResponse { - // Response body structure. - message Body { - } + // Response body structure. + message Body {} - Body body = 1; - Signature signature = 2; + Body body = 1; + Signature signature = 2; } // StartShardEvacuation request. message StartShardEvacuationRequest { - // Request body structure. - message Body { - enum Scope { - NONE = 0; - OBJECTS = 1; - TREES = 2; - } - - // IDs of the shards. - repeated bytes shard_ID = 1; - // Flag indicating whether object read errors should be ignored. - bool ignore_errors = 2; - // Evacuation scope. - uint32 scope = 3; + // Request body structure. + message Body { + enum Scope { + NONE = 0; + OBJECTS = 1; + TREES = 2; } - Body body = 1; - Signature signature = 2; + // IDs of the shards. + repeated bytes shard_ID = 1; + // Flag indicating whether object read errors should be ignored. + bool ignore_errors = 2; + // Evacuation scope. + uint32 scope = 3; + } + + Body body = 1; + Signature signature = 2; } // StartShardEvacuation response. message StartShardEvacuationResponse { - // Response body structure. - message Body {} + // Response body structure. + message Body {} - Body body = 1; - Signature signature = 2; + Body body = 1; + Signature signature = 2; } // GetShardEvacuationStatus request. message GetShardEvacuationStatusRequest { - // Request body structure. - message Body {} + // Request body structure. + message Body {} - Body body = 1; - Signature signature = 2; + Body body = 1; + Signature signature = 2; } // GetShardEvacuationStatus response. message GetShardEvacuationStatusResponse { - // Response body structure. - message Body { - // Evacuate status enum. - enum Status { - EVACUATE_SHARD_STATUS_UNDEFINED = 0; - RUNNING = 1; - COMPLETED = 2; - } - - // Unix timestamp value. - message UnixTimestamp { - int64 value = 1; - } - - // Duration in seconds. - message Duration { - int64 seconds = 1; - } - - // Total objects to evacuate count. The value is approximate, so evacuated + failed + skipped == total is not guaranteed after completion. - uint64 total_objects = 1; - // Evacuated objects count. - uint64 evacuated_objects = 2; - // Failed objects count. - uint64 failed_objects = 3; - - // Shard IDs. - repeated bytes shard_ID = 4; - // Evacuation process status. - Status status = 5; - // Evacuation process duration. - Duration duration = 6; - // Evacuation process started at timestamp. - UnixTimestamp started_at = 7; - // Error message if evacuation failed. - string error_message = 8; - - // Skipped objects count. - uint64 skipped_objects = 9; - - // Total trees to evacuate count. - uint64 total_trees = 10; - // Evacuated trees count. - uint64 evacuated_trees = 11; - // Failed trees count. - uint64 failed_trees = 12; + // Response body structure. + message Body { + // Evacuate status enum. + enum Status { + EVACUATE_SHARD_STATUS_UNDEFINED = 0; + RUNNING = 1; + COMPLETED = 2; } - Body body = 1; - Signature signature = 2; + // Unix timestamp value. + message UnixTimestamp { int64 value = 1; } + + // Duration in seconds. + message Duration { int64 seconds = 1; } + + // Total objects to evacuate count. The value is approximate, so evacuated + + // failed + skipped == total is not guaranteed after completion. + uint64 total_objects = 1; + // Evacuated objects count. + uint64 evacuated_objects = 2; + // Failed objects count. + uint64 failed_objects = 3; + + // Shard IDs. + repeated bytes shard_ID = 4; + // Evacuation process status. + Status status = 5; + // Evacuation process duration. + Duration duration = 6; + // Evacuation process started at timestamp. + UnixTimestamp started_at = 7; + // Error message if evacuation failed. + string error_message = 8; + + // Skipped objects count. + uint64 skipped_objects = 9; + + // Total trees to evacuate count. + uint64 total_trees = 10; + // Evacuated trees count. + uint64 evacuated_trees = 11; + // Failed trees count. + uint64 failed_trees = 12; + } + + Body body = 1; + Signature signature = 2; } // ResetShardEvacuationStatus request. message ResetShardEvacuationStatusRequest { - message Body {} + message Body {} - Body body = 1; - Signature signature = 2; + Body body = 1; + Signature signature = 2; } // ResetShardEvacuationStatus response. message ResetShardEvacuationStatusResponse { - message Body {} + message Body {} - Body body = 1; - Signature signature = 2; + Body body = 1; + Signature signature = 2; } // StopShardEvacuation request. message StopShardEvacuationRequest { - // Request body structure. - message Body {} + // Request body structure. + message Body {} - Body body = 1; - Signature signature = 2; + Body body = 1; + Signature signature = 2; } // StopShardEvacuation response. message StopShardEvacuationResponse { - // Response body structure. - message Body {} + // Response body structure. + message Body {} - Body body = 1; - Signature signature = 2; + Body body = 1; + Signature signature = 2; } // AddChainLocalOverride request. message AddChainLocalOverrideRequest { - message Body { - // Target for which the overrides are applied. - ChainTarget target = 1; + message Body { + // Target for which the overrides are applied. + ChainTarget target = 1; - // Serialized rule chain. If chain ID is left empty - // in the chain, then it will be generated and returned - // in the response. - bytes chain = 2; - } + // Serialized rule chain. If chain ID is left empty + // in the chain, then it will be generated and returned + // in the response. + bytes chain = 2; + } - Body body = 1; + Body body = 1; - Signature signature = 2; + Signature signature = 2; } // AddChainLocalOverride response. message AddChainLocalOverrideResponse { - message Body { - // Chain ID assigned for the added rule chain. - // If chain ID is left empty in the request, then - // it will be generated. - bytes chain_id = 1; - } + message Body { + // Chain ID assigned for the added rule chain. + // If chain ID is left empty in the request, then + // it will be generated. + bytes chain_id = 1; + } - Body body = 1; + Body body = 1; - Signature signature = 2; + Signature signature = 2; } // GetChainLocalOverride request. message GetChainLocalOverrideRequest { - message Body { - // Target for which the overrides are applied. - ChainTarget target = 1; + message Body { + // Target for which the overrides are applied. + ChainTarget target = 1; - // Chain ID assigned for the added rule chain. - bytes chain_id = 2; - } + // Chain ID assigned for the added rule chain. + bytes chain_id = 2; + } - Body body = 1; + Body body = 1; - Signature signature = 2; + Signature signature = 2; } // GetChainLocalOverride response. message GetChainLocalOverrideResponse { - message Body { - // Serialized rule chain. - bytes chain = 1; - } + message Body { + // Serialized rule chain. + bytes chain = 1; + } - Body body = 1; + Body body = 1; - Signature signature = 2; + Signature signature = 2; } // ListChainLocalOverrides request. message ListChainLocalOverridesRequest { - message Body { - // Target for which the overrides are applied. - ChainTarget target = 1; - } + message Body { + // Target for which the overrides are applied. + ChainTarget target = 1; + } - Body body = 1; + Body body = 1; - Signature signature = 2; + Signature signature = 2; } // ListChainLocalOverrides response. message ListChainLocalOverridesResponse { - message Body { - // The list of serialized rule chain. - repeated bytes chains = 1; - } + message Body { + // The list of serialized rule chain. + repeated bytes chains = 1; + } - Body body = 1; + Body body = 1; - Signature signature = 2; + Signature signature = 2; } // ListTargetsLocalOverrides request. message ListTargetsLocalOverridesRequest { - message Body { - // Target for which the overrides are applied. - string chainName = 1; - } + message Body { + // Target for which the overrides are applied. + string chainName = 1; + } - Body body = 1; + Body body = 1; - Signature signature = 2; + Signature signature = 2; } // ListTargetsLocalOverrides response. message ListTargetsLocalOverridesResponse { - message Body { - // The list of chain targets. - repeated ChainTarget targets = 1; - } + message Body { + // The list of chain targets. + repeated ChainTarget targets = 1; + } - Body body = 1; + Body body = 1; - Signature signature = 2; + Signature signature = 2; } message RemoveChainLocalOverrideRequest { - message Body { - // Target for which the overrides are applied. - ChainTarget target = 1; + message Body { + // Target for which the overrides are applied. + ChainTarget target = 1; - // Chain ID assigned for the added rule chain. - bytes chain_id = 2; - } + // Chain ID assigned for the added rule chain. + bytes chain_id = 2; + } - Body body = 1; + Body body = 1; - Signature signature = 2; + Signature signature = 2; } message RemoveChainLocalOverrideResponse { - message Body { - } + message Body {} - Body body = 1; + Body body = 1; - Signature signature = 2; + Signature signature = 2; } message RemoveChainLocalOverridesByTargetRequest { - message Body { - // Target for which the overrides are applied. - ChainTarget target = 1; - } + message Body { + // Target for which the overrides are applied. + ChainTarget target = 1; + } - Body body = 1; + Body body = 1; - Signature signature = 2; + Signature signature = 2; } -message RemoveChainLocalOverridesByTargetResponse { - message Body { - } +message RemoveChainLocalOverridesByTargetResponse { + message Body {} - Body body = 1; + Body body = 1; - Signature signature = 2; + Signature signature = 2; } message SealWriteCacheRequest { - // Request body structure. - message Body { - // ID of the shard. - repeated bytes shard_ID = 1; + // Request body structure. + message Body { + // ID of the shard. + repeated bytes shard_ID = 1; - // Flag indicating whether object read errors should be ignored. - bool ignore_errors = 2; - } + // Flag indicating whether object read errors should be ignored. + bool ignore_errors = 2; + } - Body body = 1; - Signature signature = 2; + Body body = 1; + Signature signature = 2; } message SealWriteCacheResponse { - message Body { - message Status { - bytes shard_ID = 1; - bool success = 2; - string error = 3; - } - repeated Status results = 1; + message Body { + message Status { + bytes shard_ID = 1; + bool success = 2; + string error = 3; } + repeated Status results = 1; + } - Body body = 1; + Body body = 1; - Signature signature = 2; + Signature signature = 2; } message DetachShardsRequest { - message Body { - repeated bytes shard_ID = 1; - } + message Body { repeated bytes shard_ID = 1; } - Body body = 1; - Signature signature = 2; + Body body = 1; + Signature signature = 2; } message DetachShardsResponse { - message Body { - } + message Body {} - Body body = 1; + Body body = 1; - Signature signature = 2; + Signature signature = 2; } diff --git a/pkg/services/control/service_grpc.pb.go b/pkg/services/control/service_grpc.pb.go index 95264fcd3..feeee0006 100644 --- a/pkg/services/control/service_grpc.pb.go +++ b/pkg/services/control/service_grpc.pb.go @@ -59,13 +59,15 @@ type ControlServiceClient interface { // Synchronizes all log operations for the specified tree. SynchronizeTree(ctx context.Context, in *SynchronizeTreeRequest, opts ...grpc.CallOption) (*SynchronizeTreeResponse, error) // EvacuateShard moves all data from one shard to the others. - // Deprecated: Use StartShardEvacuation/GetShardEvacuationStatus/StopShardEvacuation + // Deprecated: Use + // StartShardEvacuation/GetShardEvacuationStatus/StopShardEvacuation EvacuateShard(ctx context.Context, in *EvacuateShardRequest, opts ...grpc.CallOption) (*EvacuateShardResponse, error) // StartShardEvacuation starts moving all data from one shard to the others. StartShardEvacuation(ctx context.Context, in *StartShardEvacuationRequest, opts ...grpc.CallOption) (*StartShardEvacuationResponse, error) // GetShardEvacuationStatus returns evacuation status. GetShardEvacuationStatus(ctx context.Context, in *GetShardEvacuationStatusRequest, opts ...grpc.CallOption) (*GetShardEvacuationStatusResponse, error) - // ResetShardEvacuationStatus resets evacuation status if there is no running evacuation process. + // ResetShardEvacuationStatus resets evacuation status if there is no running + // evacuation process. ResetShardEvacuationStatus(ctx context.Context, in *ResetShardEvacuationStatusRequest, opts ...grpc.CallOption) (*ResetShardEvacuationStatusResponse, error) // StopShardEvacuation stops moving all data from one shard to the others. StopShardEvacuation(ctx context.Context, in *StopShardEvacuationRequest, opts ...grpc.CallOption) (*StopShardEvacuationResponse, error) @@ -77,11 +79,14 @@ type ControlServiceClient interface { AddChainLocalOverride(ctx context.Context, in *AddChainLocalOverrideRequest, opts ...grpc.CallOption) (*AddChainLocalOverrideResponse, error) // Get local access policy engine overrides stored in the node by chain id. GetChainLocalOverride(ctx context.Context, in *GetChainLocalOverrideRequest, opts ...grpc.CallOption) (*GetChainLocalOverrideResponse, error) - // List local access policy engine overrides stored in the node by container id. + // List local access policy engine overrides stored in the node by container + // id. ListChainLocalOverrides(ctx context.Context, in *ListChainLocalOverridesRequest, opts ...grpc.CallOption) (*ListChainLocalOverridesResponse, error) - // Remove local access policy engine overrides stored in the node by chaind id. + // Remove local access policy engine overrides stored in the node by chaind + // id. RemoveChainLocalOverride(ctx context.Context, in *RemoveChainLocalOverrideRequest, opts ...grpc.CallOption) (*RemoveChainLocalOverrideResponse, error) - // Remove local access policy engine overrides stored in the node by chaind id. + // Remove local access policy engine overrides stored in the node by chaind + // id. RemoveChainLocalOverridesByTarget(ctx context.Context, in *RemoveChainLocalOverridesByTargetRequest, opts ...grpc.CallOption) (*RemoveChainLocalOverridesByTargetResponse, error) // List targets of the local APE overrides stored in the node. ListTargetsLocalOverrides(ctx context.Context, in *ListTargetsLocalOverridesRequest, opts ...grpc.CallOption) (*ListTargetsLocalOverridesResponse, error) @@ -305,13 +310,15 @@ type ControlServiceServer interface { // Synchronizes all log operations for the specified tree. SynchronizeTree(context.Context, *SynchronizeTreeRequest) (*SynchronizeTreeResponse, error) // EvacuateShard moves all data from one shard to the others. - // Deprecated: Use StartShardEvacuation/GetShardEvacuationStatus/StopShardEvacuation + // Deprecated: Use + // StartShardEvacuation/GetShardEvacuationStatus/StopShardEvacuation EvacuateShard(context.Context, *EvacuateShardRequest) (*EvacuateShardResponse, error) // StartShardEvacuation starts moving all data from one shard to the others. StartShardEvacuation(context.Context, *StartShardEvacuationRequest) (*StartShardEvacuationResponse, error) // GetShardEvacuationStatus returns evacuation status. GetShardEvacuationStatus(context.Context, *GetShardEvacuationStatusRequest) (*GetShardEvacuationStatusResponse, error) - // ResetShardEvacuationStatus resets evacuation status if there is no running evacuation process. + // ResetShardEvacuationStatus resets evacuation status if there is no running + // evacuation process. ResetShardEvacuationStatus(context.Context, *ResetShardEvacuationStatusRequest) (*ResetShardEvacuationStatusResponse, error) // StopShardEvacuation stops moving all data from one shard to the others. StopShardEvacuation(context.Context, *StopShardEvacuationRequest) (*StopShardEvacuationResponse, error) @@ -323,11 +330,14 @@ type ControlServiceServer interface { AddChainLocalOverride(context.Context, *AddChainLocalOverrideRequest) (*AddChainLocalOverrideResponse, error) // Get local access policy engine overrides stored in the node by chain id. GetChainLocalOverride(context.Context, *GetChainLocalOverrideRequest) (*GetChainLocalOverrideResponse, error) - // List local access policy engine overrides stored in the node by container id. + // List local access policy engine overrides stored in the node by container + // id. ListChainLocalOverrides(context.Context, *ListChainLocalOverridesRequest) (*ListChainLocalOverridesResponse, error) - // Remove local access policy engine overrides stored in the node by chaind id. + // Remove local access policy engine overrides stored in the node by chaind + // id. RemoveChainLocalOverride(context.Context, *RemoveChainLocalOverrideRequest) (*RemoveChainLocalOverrideResponse, error) - // Remove local access policy engine overrides stored in the node by chaind id. + // Remove local access policy engine overrides stored in the node by chaind + // id. RemoveChainLocalOverridesByTarget(context.Context, *RemoveChainLocalOverridesByTargetRequest) (*RemoveChainLocalOverridesByTargetResponse, error) // List targets of the local APE overrides stored in the node. ListTargetsLocalOverrides(context.Context, *ListTargetsLocalOverridesRequest) (*ListTargetsLocalOverridesResponse, error) diff --git a/pkg/services/control/types.pb.go b/pkg/services/control/types.pb.go index 2fc16a926..858755694 100644 --- a/pkg/services/control/types.pb.go +++ b/pkg/services/control/types.pb.go @@ -205,6 +205,8 @@ const ( ChainTarget_UNDEFINED ChainTarget_TargetType = 0 ChainTarget_NAMESPACE ChainTarget_TargetType = 1 ChainTarget_CONTAINER ChainTarget_TargetType = 2 + ChainTarget_USER ChainTarget_TargetType = 3 + ChainTarget_GROUP ChainTarget_TargetType = 4 ) // Enum value maps for ChainTarget_TargetType. @@ -213,11 +215,15 @@ var ( 0: "UNDEFINED", 1: "NAMESPACE", 2: "CONTAINER", + 3: "USER", + 4: "GROUP", } ChainTarget_TargetType_value = map[string]int32{ "UNDEFINED": 0, "NAMESPACE": 1, "CONTAINER": 2, + "USER": 3, + "GROUP": 4, } ) @@ -814,40 +820,41 @@ var file_pkg_services_control_types_proto_rawDesc = []byte{ 0x6d, 0x61, 0x50, 0x61, 0x74, 0x68, 0x22, 0x36, 0x0a, 0x0c, 0x42, 0x6c, 0x6f, 0x62, 0x73, 0x74, 0x6f, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x61, 0x74, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x70, 0x61, 0x74, 0x68, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, - 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x22, 0x91, + 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x22, 0xa6, 0x01, 0x0a, 0x0b, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x12, 0x33, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1f, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x2e, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x2e, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x54, 0x79, 0x70, 0x65, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x04, 0x4e, 0x61, 0x6d, 0x65, 0x22, 0x39, 0x0a, 0x0a, 0x54, 0x61, 0x72, 0x67, 0x65, + 0x09, 0x52, 0x04, 0x4e, 0x61, 0x6d, 0x65, 0x22, 0x4e, 0x0a, 0x0a, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x0d, 0x0a, 0x09, 0x55, 0x4e, 0x44, 0x45, 0x46, 0x49, 0x4e, 0x45, 0x44, 0x10, 0x00, 0x12, 0x0d, 0x0a, 0x09, 0x4e, 0x41, 0x4d, 0x45, 0x53, 0x50, 0x41, 0x43, 0x45, 0x10, 0x01, 0x12, 0x0d, 0x0a, 0x09, 0x43, 0x4f, 0x4e, 0x54, 0x41, 0x49, 0x4e, 0x45, 0x52, - 0x10, 0x02, 0x2a, 0x4e, 0x0a, 0x0c, 0x4e, 0x65, 0x74, 0x6d, 0x61, 0x70, 0x53, 0x74, 0x61, 0x74, - 0x75, 0x73, 0x12, 0x14, 0x0a, 0x10, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x55, 0x4e, 0x44, - 0x45, 0x46, 0x49, 0x4e, 0x45, 0x44, 0x10, 0x00, 0x12, 0x0a, 0x0a, 0x06, 0x4f, 0x4e, 0x4c, 0x49, - 0x4e, 0x45, 0x10, 0x01, 0x12, 0x0b, 0x0a, 0x07, 0x4f, 0x46, 0x46, 0x4c, 0x49, 0x4e, 0x45, 0x10, - 0x02, 0x12, 0x0f, 0x0a, 0x0b, 0x4d, 0x41, 0x49, 0x4e, 0x54, 0x45, 0x4e, 0x41, 0x4e, 0x43, 0x45, - 0x10, 0x03, 0x2a, 0x6a, 0x0a, 0x0c, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x53, 0x74, 0x61, 0x74, - 0x75, 0x73, 0x12, 0x1b, 0x0a, 0x17, 0x48, 0x45, 0x41, 0x4c, 0x54, 0x48, 0x5f, 0x53, 0x54, 0x41, - 0x54, 0x55, 0x53, 0x5f, 0x55, 0x4e, 0x44, 0x45, 0x46, 0x49, 0x4e, 0x45, 0x44, 0x10, 0x00, 0x12, - 0x0c, 0x0a, 0x08, 0x53, 0x54, 0x41, 0x52, 0x54, 0x49, 0x4e, 0x47, 0x10, 0x01, 0x12, 0x09, 0x0a, - 0x05, 0x52, 0x45, 0x41, 0x44, 0x59, 0x10, 0x02, 0x12, 0x11, 0x0a, 0x0d, 0x53, 0x48, 0x55, 0x54, - 0x54, 0x49, 0x4e, 0x47, 0x5f, 0x44, 0x4f, 0x57, 0x4e, 0x10, 0x03, 0x12, 0x11, 0x0a, 0x0d, 0x52, - 0x45, 0x43, 0x4f, 0x4e, 0x46, 0x49, 0x47, 0x55, 0x52, 0x49, 0x4e, 0x47, 0x10, 0x04, 0x2a, 0x6a, - 0x0a, 0x09, 0x53, 0x68, 0x61, 0x72, 0x64, 0x4d, 0x6f, 0x64, 0x65, 0x12, 0x18, 0x0a, 0x14, 0x53, - 0x48, 0x41, 0x52, 0x44, 0x5f, 0x4d, 0x4f, 0x44, 0x45, 0x5f, 0x55, 0x4e, 0x44, 0x45, 0x46, 0x49, - 0x4e, 0x45, 0x44, 0x10, 0x00, 0x12, 0x0e, 0x0a, 0x0a, 0x52, 0x45, 0x41, 0x44, 0x5f, 0x57, 0x52, - 0x49, 0x54, 0x45, 0x10, 0x01, 0x12, 0x0d, 0x0a, 0x09, 0x52, 0x45, 0x41, 0x44, 0x5f, 0x4f, 0x4e, - 0x4c, 0x59, 0x10, 0x02, 0x12, 0x0c, 0x0a, 0x08, 0x44, 0x45, 0x47, 0x52, 0x41, 0x44, 0x45, 0x44, - 0x10, 0x03, 0x12, 0x16, 0x0a, 0x12, 0x44, 0x45, 0x47, 0x52, 0x41, 0x44, 0x45, 0x44, 0x5f, 0x52, - 0x45, 0x41, 0x44, 0x5f, 0x4f, 0x4e, 0x4c, 0x59, 0x10, 0x04, 0x42, 0x41, 0x5a, 0x3f, 0x67, 0x69, - 0x74, 0x2e, 0x66, 0x72, 0x6f, 0x73, 0x74, 0x66, 0x73, 0x2e, 0x69, 0x6e, 0x66, 0x6f, 0x2f, 0x54, - 0x72, 0x75, 0x65, 0x43, 0x6c, 0x6f, 0x75, 0x64, 0x4c, 0x61, 0x62, 0x2f, 0x66, 0x72, 0x6f, 0x73, - 0x74, 0x66, 0x73, 0x2d, 0x6e, 0x6f, 0x64, 0x65, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x73, 0x65, 0x72, - 0x76, 0x69, 0x63, 0x65, 0x73, 0x2f, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x62, 0x06, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x10, 0x02, 0x12, 0x08, 0x0a, 0x04, 0x55, 0x53, 0x45, 0x52, 0x10, 0x03, 0x12, 0x09, 0x0a, 0x05, + 0x47, 0x52, 0x4f, 0x55, 0x50, 0x10, 0x04, 0x2a, 0x4e, 0x0a, 0x0c, 0x4e, 0x65, 0x74, 0x6d, 0x61, + 0x70, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x14, 0x0a, 0x10, 0x53, 0x54, 0x41, 0x54, 0x55, + 0x53, 0x5f, 0x55, 0x4e, 0x44, 0x45, 0x46, 0x49, 0x4e, 0x45, 0x44, 0x10, 0x00, 0x12, 0x0a, 0x0a, + 0x06, 0x4f, 0x4e, 0x4c, 0x49, 0x4e, 0x45, 0x10, 0x01, 0x12, 0x0b, 0x0a, 0x07, 0x4f, 0x46, 0x46, + 0x4c, 0x49, 0x4e, 0x45, 0x10, 0x02, 0x12, 0x0f, 0x0a, 0x0b, 0x4d, 0x41, 0x49, 0x4e, 0x54, 0x45, + 0x4e, 0x41, 0x4e, 0x43, 0x45, 0x10, 0x03, 0x2a, 0x6a, 0x0a, 0x0c, 0x48, 0x65, 0x61, 0x6c, 0x74, + 0x68, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x1b, 0x0a, 0x17, 0x48, 0x45, 0x41, 0x4c, 0x54, + 0x48, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x55, 0x4e, 0x44, 0x45, 0x46, 0x49, 0x4e, + 0x45, 0x44, 0x10, 0x00, 0x12, 0x0c, 0x0a, 0x08, 0x53, 0x54, 0x41, 0x52, 0x54, 0x49, 0x4e, 0x47, + 0x10, 0x01, 0x12, 0x09, 0x0a, 0x05, 0x52, 0x45, 0x41, 0x44, 0x59, 0x10, 0x02, 0x12, 0x11, 0x0a, + 0x0d, 0x53, 0x48, 0x55, 0x54, 0x54, 0x49, 0x4e, 0x47, 0x5f, 0x44, 0x4f, 0x57, 0x4e, 0x10, 0x03, + 0x12, 0x11, 0x0a, 0x0d, 0x52, 0x45, 0x43, 0x4f, 0x4e, 0x46, 0x49, 0x47, 0x55, 0x52, 0x49, 0x4e, + 0x47, 0x10, 0x04, 0x2a, 0x6a, 0x0a, 0x09, 0x53, 0x68, 0x61, 0x72, 0x64, 0x4d, 0x6f, 0x64, 0x65, + 0x12, 0x18, 0x0a, 0x14, 0x53, 0x48, 0x41, 0x52, 0x44, 0x5f, 0x4d, 0x4f, 0x44, 0x45, 0x5f, 0x55, + 0x4e, 0x44, 0x45, 0x46, 0x49, 0x4e, 0x45, 0x44, 0x10, 0x00, 0x12, 0x0e, 0x0a, 0x0a, 0x52, 0x45, + 0x41, 0x44, 0x5f, 0x57, 0x52, 0x49, 0x54, 0x45, 0x10, 0x01, 0x12, 0x0d, 0x0a, 0x09, 0x52, 0x45, + 0x41, 0x44, 0x5f, 0x4f, 0x4e, 0x4c, 0x59, 0x10, 0x02, 0x12, 0x0c, 0x0a, 0x08, 0x44, 0x45, 0x47, + 0x52, 0x41, 0x44, 0x45, 0x44, 0x10, 0x03, 0x12, 0x16, 0x0a, 0x12, 0x44, 0x45, 0x47, 0x52, 0x41, + 0x44, 0x45, 0x44, 0x5f, 0x52, 0x45, 0x41, 0x44, 0x5f, 0x4f, 0x4e, 0x4c, 0x59, 0x10, 0x04, 0x42, + 0x41, 0x5a, 0x3f, 0x67, 0x69, 0x74, 0x2e, 0x66, 0x72, 0x6f, 0x73, 0x74, 0x66, 0x73, 0x2e, 0x69, + 0x6e, 0x66, 0x6f, 0x2f, 0x54, 0x72, 0x75, 0x65, 0x43, 0x6c, 0x6f, 0x75, 0x64, 0x4c, 0x61, 0x62, + 0x2f, 0x66, 0x72, 0x6f, 0x73, 0x74, 0x66, 0x73, 0x2d, 0x6e, 0x6f, 0x64, 0x65, 0x2f, 0x70, 0x6b, + 0x67, 0x2f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2f, 0x63, 0x6f, 0x6e, 0x74, 0x72, + 0x6f, 0x6c, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( diff --git a/pkg/services/control/types.proto b/pkg/services/control/types.proto index 00fcb98d1..55636d88a 100644 --- a/pkg/services/control/types.proto +++ b/pkg/services/control/types.proto @@ -6,183 +6,186 @@ option go_package = "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/services/con // Signature of some message. message Signature { - // Public key used for signing. - bytes key = 1 [json_name = "key"]; + // Public key used for signing. + bytes key = 1 [ json_name = "key" ]; - // Binary signature. - bytes sign = 2 [json_name = "signature"]; + // Binary signature. + bytes sign = 2 [ json_name = "signature" ]; } // Status of the storage node in the FrostFS network map. enum NetmapStatus { - // Undefined status, default value. - STATUS_UNDEFINED = 0; + // Undefined status, default value. + STATUS_UNDEFINED = 0; - // Node is online. - ONLINE = 1; + // Node is online. + ONLINE = 1; - // Node is offline. - OFFLINE = 2; + // Node is offline. + OFFLINE = 2; - // Node is maintained by the owner. - MAINTENANCE = 3; + // Node is maintained by the owner. + MAINTENANCE = 3; } // FrostFS node description. message NodeInfo { - // Public key of the FrostFS node in a binary format. - bytes public_key = 1 [json_name = "publicKey"]; + // Public key of the FrostFS node in a binary format. + bytes public_key = 1 [ json_name = "publicKey" ]; - // Ways to connect to a node. - repeated string addresses = 2 [json_name = "addresses"]; + // Ways to connect to a node. + repeated string addresses = 2 [ json_name = "addresses" ]; - // Administrator-defined Attributes of the FrostFS Storage Node. - // - // `Attribute` is a Key-Value metadata pair. Key name must be a valid UTF-8 - // string. Value can't be empty. - // - // Node's attributes are mostly used during Storage Policy evaluation to - // calculate object's placement and find a set of nodes satisfying policy - // requirements. There are some "well-known" node attributes common to all the - // Storage Nodes in the network and used implicitly with default values if not - // explicitly set: - // - // * Capacity \ + // Administrator-defined Attributes of the FrostFS Storage Node. + // + // `Attribute` is a Key-Value metadata pair. Key name must be a valid UTF-8 + // string. Value can't be empty. + // + // Node's attributes are mostly used during Storage Policy evaluation to + // calculate object's placement and find a set of nodes satisfying policy + // requirements. There are some "well-known" node attributes common to all the + // Storage Nodes in the network and used implicitly with default values if not + // explicitly set: + // + // * Capacity \ // Total available disk space in Gigabytes. - // * Price \ + // * Price \ // Price in GAS tokens for storing one GB of data during one Epoch. In node - // attributes it's a string presenting floating point number with comma or - // point delimiter for decimal part. In the Network Map it will be saved as - // 64-bit unsigned integer representing number of minimal token fractions. - // * Locode \ + // attributes it's a string presenting floating point number with comma or + // point delimiter for decimal part. In the Network Map it will be saved as + // 64-bit unsigned integer representing number of minimal token fractions. + // * Locode \ // Node's geographic location in - // [UN/LOCODE](https://www.unece.org/cefact/codesfortrade/codes_index.html) - // format approximated to the nearest point defined in standard. - // * Country \ + // [UN/LOCODE](https://www.unece.org/cefact/codesfortrade/codes_index.html) + // format approximated to the nearest point defined in standard. + // * Country \ // Country code in - // [ISO 3166-1_alpha-2](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2) - // format. Calculated automatically from `Locode` attribute - // * Region \ + // [ISO 3166-1_alpha-2](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2) + // format. Calculated automatically from `Locode` attribute + // * Region \ // Country's administative subdivision where node is located. Calculated - // automatically from `Locode` attribute based on `SubDiv` field. Presented - // in [ISO 3166-2](https://en.wikipedia.org/wiki/ISO_3166-2) format. - // * City \ + // automatically from `Locode` attribute based on `SubDiv` field. Presented + // in [ISO 3166-2](https://en.wikipedia.org/wiki/ISO_3166-2) format. + // * City \ // City, town, village or rural area name where node is located written - // without diacritics . Calculated automatically from `Locode` attribute. - // - // For detailed description of each well-known attribute please see the - // corresponding section in FrostFS Technical specification. - message Attribute { - // Key of the node attribute. - string key = 1 [json_name = "key"]; + // without diacritics . Calculated automatically from `Locode` attribute. + // + // For detailed description of each well-known attribute please see the + // corresponding section in FrostFS Technical specification. + message Attribute { + // Key of the node attribute. + string key = 1 [ json_name = "key" ]; - // Value of the node attribute. - string value = 2 [json_name = "value"]; + // Value of the node attribute. + string value = 2 [ json_name = "value" ]; - // Parent keys, if any. For example for `City` it could be `Region` and - // `Country`. - repeated string parents = 3 [json_name = "parents"]; - } - // Carries list of the FrostFS node attributes in a key-value form. Key name - // must be a node-unique valid UTF-8 string. Value can't be empty. NodeInfo - // structures with duplicated attribute names or attributes with empty values - // will be considered invalid. - repeated Attribute attributes = 3 [json_name = "attributes"]; + // Parent keys, if any. For example for `City` it could be `Region` and + // `Country`. + repeated string parents = 3 [ json_name = "parents" ]; + } + // Carries list of the FrostFS node attributes in a key-value form. Key name + // must be a node-unique valid UTF-8 string. Value can't be empty. NodeInfo + // structures with duplicated attribute names or attributes with empty values + // will be considered invalid. + repeated Attribute attributes = 3 [ json_name = "attributes" ]; - // Carries state of the FrostFS node. - NetmapStatus state = 4 [json_name = "state"]; + // Carries state of the FrostFS node. + NetmapStatus state = 4 [ json_name = "state" ]; } // Network map structure. message Netmap { - // Network map revision number. - uint64 epoch = 1 [json_name = "epoch"]; + // Network map revision number. + uint64 epoch = 1 [ json_name = "epoch" ]; - // Nodes presented in network. - repeated NodeInfo nodes = 2 [json_name = "nodes"]; + // Nodes presented in network. + repeated NodeInfo nodes = 2 [ json_name = "nodes" ]; } // Health status of the storage node application. enum HealthStatus { - // Undefined status, default value. - HEALTH_STATUS_UNDEFINED = 0; + // Undefined status, default value. + HEALTH_STATUS_UNDEFINED = 0; - // Storage node application is starting. - STARTING = 1; + // Storage node application is starting. + STARTING = 1; - // Storage node application is started and serves all services. - READY = 2; + // Storage node application is started and serves all services. + READY = 2; - // Storage node application is shutting down. - SHUTTING_DOWN = 3; + // Storage node application is shutting down. + SHUTTING_DOWN = 3; - // Storage node application is reconfiguring. - RECONFIGURING = 4; + // Storage node application is reconfiguring. + RECONFIGURING = 4; } // Shard description. message ShardInfo { - // ID of the shard. - bytes shard_ID = 1 [json_name = "shardID"]; + // ID of the shard. + bytes shard_ID = 1 [ json_name = "shardID" ]; - // Path to shard's metabase. - string metabase_path = 2 [json_name = "metabasePath"]; + // Path to shard's metabase. + string metabase_path = 2 [ json_name = "metabasePath" ]; - // Shard's blobstor info. - repeated BlobstorInfo blobstor = 3 [json_name = "blobstor"]; + // Shard's blobstor info. + repeated BlobstorInfo blobstor = 3 [ json_name = "blobstor" ]; - // Path to shard's write-cache, empty if disabled. - string writecache_path = 4 [json_name = "writecachePath"]; + // Path to shard's write-cache, empty if disabled. + string writecache_path = 4 [ json_name = "writecachePath" ]; - // Work mode of the shard. - ShardMode mode = 5; + // Work mode of the shard. + ShardMode mode = 5; - // Amount of errors occured. - uint32 errorCount = 6; + // Amount of errors occured. + uint32 errorCount = 6; - // Path to shard's pilorama storage. - string pilorama_path = 7 [json_name = "piloramaPath"]; + // Path to shard's pilorama storage. + string pilorama_path = 7 [ json_name = "piloramaPath" ]; } // Blobstor component description. message BlobstorInfo { - // Path to the root. - string path = 1 [json_name = "path"]; - // Component type. - string type = 2 [json_name = "type"]; + // Path to the root. + string path = 1 [ json_name = "path" ]; + // Component type. + string type = 2 [ json_name = "type" ]; } // Work mode of the shard. enum ShardMode { - // Undefined mode, default value. - SHARD_MODE_UNDEFINED = 0; + // Undefined mode, default value. + SHARD_MODE_UNDEFINED = 0; - // Read-write. - READ_WRITE = 1; + // Read-write. + READ_WRITE = 1; - // Read-only. - READ_ONLY = 2; + // Read-only. + READ_ONLY = 2; - // Degraded. - DEGRADED = 3; + // Degraded. + DEGRADED = 3; - // DegradedReadOnly. - DEGRADED_READ_ONLY = 4; + // DegradedReadOnly. + DEGRADED_READ_ONLY = 4; } - // ChainTarget is an object to which local overrides // are applied. message ChainTarget { - enum TargetType { - UNDEFINED = 0; + enum TargetType { + UNDEFINED = 0; - NAMESPACE = 1; + NAMESPACE = 1; - CONTAINER = 2; - } + CONTAINER = 2; - TargetType type = 1; + USER = 3; - string Name = 2; + GROUP = 4; + } + + TargetType type = 1; + + string Name = 2; } diff --git a/pkg/services/object/ape/checker.go b/pkg/services/object/ape/checker.go index 45efce5a7..1063bd901 100644 --- a/pkg/services/object/ape/checker.go +++ b/pkg/services/object/ape/checker.go @@ -12,6 +12,7 @@ import ( apechain "git.frostfs.info/TrueCloudLab/policy-engine/pkg/chain" policyengine "git.frostfs.info/TrueCloudLab/policy-engine/pkg/engine" nativeschema "git.frostfs.info/TrueCloudLab/policy-engine/schema/native" + "github.com/nspcc-dev/neo-go/pkg/crypto/keys" ) type checkerImpl struct { @@ -54,6 +55,9 @@ type Prm struct { // If SoftAPECheck is set to true, then NoRuleFound is interpreted as allow. SoftAPECheck bool + + // If true, object headers will not retrieved from storage engine. + WithoutHeaderRequest bool } var errMissingOID = errors.New("object ID is not set") @@ -81,8 +85,13 @@ func (c *checkerImpl) CheckAPE(ctx context.Context, prm Prm) error { return fmt.Errorf("failed to create ape request: %w", err) } - status, ruleFound, err := c.chainRouter.IsAllowed(apechain.Ingress, - policyengine.NewRequestTarget(prm.Namespace, prm.Container.EncodeToString()), r) + pub, err := keys.NewPublicKeyFromString(prm.SenderKey) + if err != nil { + return err + } + + rt := policyengine.NewRequestTargetExtended(prm.Namespace, prm.Container.EncodeToString(), fmt.Sprintf("%s:%s", prm.Namespace, pub.Address()), nil) + status, ruleFound, err := c.chainRouter.IsAllowed(apechain.Ingress, rt, r) if err != nil { return err } diff --git a/pkg/services/object/ape/checker_test.go b/pkg/services/object/ape/checker_test.go index 443414959..fc915715c 100644 --- a/pkg/services/object/ape/checker_test.go +++ b/pkg/services/object/ape/checker_test.go @@ -16,6 +16,7 @@ import ( policyengine "git.frostfs.info/TrueCloudLab/policy-engine/pkg/engine" "git.frostfs.info/TrueCloudLab/policy-engine/pkg/engine/inmemory" nativeschema "git.frostfs.info/TrueCloudLab/policy-engine/schema/native" + "github.com/nspcc-dev/neo-go/pkg/crypto/keys" "github.com/stretchr/testify/require" ) @@ -147,7 +148,9 @@ var ( role = "Container" - senderKey = hex.EncodeToString([]byte{1, 0, 0, 1}) + senderPrivateKey, _ = keys.NewPrivateKey() + + senderKey = hex.EncodeToString(senderPrivateKey.PublicKey().Bytes()) ) func TestAPECheck(t *testing.T) { diff --git a/pkg/services/object/ape/request.go b/pkg/services/object/ape/request.go index 3689bd178..7bbee31c1 100644 --- a/pkg/services/object/ape/request.go +++ b/pkg/services/object/ape/request.go @@ -6,49 +6,16 @@ import ( "strconv" objectV2 "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/object" + aperequest "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/ape/request" "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/acl" cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id" objectSDK "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object" oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id" "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/user" - aperesource "git.frostfs.info/TrueCloudLab/policy-engine/pkg/resource" nativeschema "git.frostfs.info/TrueCloudLab/policy-engine/schema/native" ) -type request struct { - operation string - resource *resource - properties map[string]string -} - -var _ aperesource.Request = (*request)(nil) - -type resource struct { - name string - properties map[string]string -} - -var _ aperesource.Resource = (*resource)(nil) - -func (r *resource) Name() string { - return r.name -} - -func (r *resource) Property(key string) string { - return r.properties[key] -} - -func (r *request) Operation() string { - return r.operation -} - -func (r *request) Property(key string) string { - return r.properties[key] -} - -func (r *request) Resource() aperesource.Resource { - return r.resource -} +var defaultRequest = aperequest.Request{} func nativeSchemaRole(role acl.Role) string { switch role { @@ -123,7 +90,7 @@ func objectProperties(cnr cid.ID, oid *oid.ID, cnrOwner user.ID, header *objectV // newAPERequest creates an APE request to be passed to a chain router. It collects resource properties from // header provided by headerProvider. If it cannot be found in headerProvider, then properties are // initialized from header given in prm (if it is set). Otherwise, just CID and OID are set to properties. -func (c *checkerImpl) newAPERequest(ctx context.Context, prm Prm) (*request, error) { +func (c *checkerImpl) newAPERequest(ctx context.Context, prm Prm) (aperequest.Request, error) { switch prm.Method { case nativeschema.MethodGetObject, nativeschema.MethodHeadObject, @@ -131,32 +98,32 @@ func (c *checkerImpl) newAPERequest(ctx context.Context, prm Prm) (*request, err nativeschema.MethodHashObject, nativeschema.MethodDeleteObject: if prm.Object == nil { - return nil, fmt.Errorf("method %s: %w", prm.Method, errMissingOID) + return defaultRequest, fmt.Errorf("method %s: %w", prm.Method, errMissingOID) } case nativeschema.MethodSearchObject, nativeschema.MethodPutObject: default: - return nil, fmt.Errorf("unknown method: %s", prm.Method) + return defaultRequest, fmt.Errorf("unknown method: %s", prm.Method) } var header *objectV2.Header if prm.Header != nil { header = prm.Header - } else if prm.Object != nil { + } else if prm.Object != nil && !prm.WithoutHeaderRequest { headerObjSDK, err := c.headerProvider.GetHeader(ctx, prm.Container, *prm.Object) if err == nil { header = headerObjSDK.ToV2().GetHeader() } } - return &request{ - operation: prm.Method, - resource: &resource{ - name: resourceName(prm.Container, prm.Object, prm.Namespace), - properties: objectProperties(prm.Container, prm.Object, prm.ContainerOwner, header), - }, - properties: map[string]string{ + return aperequest.NewRequest( + prm.Method, + aperequest.NewResource( + resourceName(prm.Container, prm.Object, prm.Namespace), + objectProperties(prm.Container, prm.Object, prm.ContainerOwner, header), + ), + map[string]string{ nativeschema.PropertyKeyActorPublicKey: prm.SenderKey, nativeschema.PropertyKeyActorRole: prm.Role, }, - }, nil + ), nil } diff --git a/pkg/services/object/ape/request_test.go b/pkg/services/object/ape/request_test.go index c5b43fa8b..fdb7af219 100644 --- a/pkg/services/object/ape/request_test.go +++ b/pkg/services/object/ape/request_test.go @@ -6,6 +6,7 @@ import ( "testing" objectV2 "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/object" + aperequest "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/ape/request" checksumtest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/checksum/test" objectSDK "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object" "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/user" @@ -256,24 +257,23 @@ func TestNewAPERequest(t *testing.T) { return } - expectedRequest := request{ - operation: method, - resource: &resource{ - name: resourceName(cnr, obj, prm.Namespace), - properties: objectProperties(cnr, obj, testCnrOwner, func() *objectV2.Header { + expectedRequest := aperequest.NewRequest( + method, + aperequest.NewResource( + resourceName(cnr, obj, prm.Namespace), + objectProperties(cnr, obj, testCnrOwner, func() *objectV2.Header { if headerObjSDK != nil { return headerObjSDK.ToV2().GetHeader() } return prm.Header - }()), - }, - properties: map[string]string{ + }())), + map[string]string{ nativeschema.PropertyKeyActorPublicKey: prm.SenderKey, nativeschema.PropertyKeyActorRole: prm.Role, }, - } + ) - require.Equal(t, expectedRequest, *r) + require.Equal(t, expectedRequest, r) }) } }) diff --git a/pkg/services/object/ape/service.go b/pkg/services/object/ape/service.go index 0c203209d..95f36be79 100644 --- a/pkg/services/object/ape/service.go +++ b/pkg/services/object/ape/service.go @@ -125,14 +125,15 @@ func (c *Service) Get(request *objectV2.GetRequest, stream objectSvc.GetObjectSt } err = c.apeChecker.CheckAPE(stream.Context(), Prm{ - Namespace: reqCtx.Namespace, - Container: cnrID, - Object: objID, - Method: nativeschema.MethodGetObject, - Role: nativeSchemaRole(reqCtx.Role), - SenderKey: hex.EncodeToString(reqCtx.SenderKey), - ContainerOwner: reqCtx.ContainerOwner, - SoftAPECheck: reqCtx.SoftAPECheck, + Namespace: reqCtx.Namespace, + Container: cnrID, + Object: objID, + Method: nativeschema.MethodGetObject, + Role: nativeSchemaRole(reqCtx.Role), + SenderKey: hex.EncodeToString(reqCtx.SenderKey), + ContainerOwner: reqCtx.ContainerOwner, + SoftAPECheck: reqCtx.SoftAPECheck, + WithoutHeaderRequest: true, }) if err != nil { return toStatusErr(err) @@ -211,14 +212,15 @@ func (c *Service) Head(ctx context.Context, request *objectV2.HeadRequest) (*obj } err = c.apeChecker.CheckAPE(ctx, Prm{ - Namespace: reqCtx.Namespace, - Container: cnrID, - Object: objID, - Method: nativeschema.MethodHeadObject, - Role: nativeSchemaRole(reqCtx.Role), - SenderKey: hex.EncodeToString(reqCtx.SenderKey), - ContainerOwner: reqCtx.ContainerOwner, - SoftAPECheck: reqCtx.SoftAPECheck, + Namespace: reqCtx.Namespace, + Container: cnrID, + Object: objID, + Method: nativeschema.MethodHeadObject, + Role: nativeSchemaRole(reqCtx.Role), + SenderKey: hex.EncodeToString(reqCtx.SenderKey), + ContainerOwner: reqCtx.ContainerOwner, + SoftAPECheck: reqCtx.SoftAPECheck, + WithoutHeaderRequest: true, }) if err != nil { return nil, toStatusErr(err) diff --git a/pkg/services/object/get/assembler.go b/pkg/services/object/get/assembler.go index f10c67e52..025296ec7 100644 --- a/pkg/services/object/get/assembler.go +++ b/pkg/services/object/get/assembler.go @@ -114,7 +114,7 @@ func (a *assembler) initializeFromSourceObjectID(ctx context.Context, id oid.ID) } to := uint64(0) - if seekOff+seekLen > a.currentOffset+from { + if seekOff+seekLen >= a.currentOffset+from { to = seekOff + seekLen - a.currentOffset } diff --git a/pkg/services/object/get/get_test.go b/pkg/services/object/get/get_test.go index 64614d8d8..988cd6982 100644 --- a/pkg/services/object/get/get_test.go +++ b/pkg/services/object/get/get_test.go @@ -1,6 +1,7 @@ package getsvc import ( + "bytes" "context" "crypto/ecdsa" "crypto/rand" @@ -25,6 +26,9 @@ import ( objectSDK "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object" oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id" oidtest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id/test" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/transformer" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/version" + "github.com/nspcc-dev/neo-go/pkg/crypto/keys" "github.com/stretchr/testify/require" ) @@ -62,6 +66,10 @@ func (e testEpochReceiver) Epoch() (uint64, error) { return uint64(e), nil } +func (e testEpochReceiver) CurrentEpoch() uint64 { + return uint64(e) +} + func newTestStorage() *testStorage { return &testStorage{ inhumed: make(map[string]struct{}), @@ -555,21 +563,6 @@ func TestGetRemoteSmall(t *testing.T) { return p } - newRngPrm := func(raw bool, w ChunkWriter, off, ln uint64) RangePrm { - p := RangePrm{} - p.SetChunkWriter(w) - p.WithRawFlag(raw) - p.common = new(util.CommonPrm).WithLocalOnly(false) - - r := objectSDK.NewRange() - r.SetOffset(off) - r.SetLength(ln) - - p.SetRange(r) - - return p - } - newHeadPrm := func(raw bool, w ObjectWriter) HeadPrm { p := HeadPrm{} p.SetHeaderWriter(w) @@ -1628,6 +1621,203 @@ func TestGetRemoteSmall(t *testing.T) { }) } +type testTarget struct { + objects []*objectSDK.Object +} + +func (tt *testTarget) WriteObject(_ context.Context, obj *objectSDK.Object) error { + tt.objects = append(tt.objects, obj) + return nil +} + +func objectChain(t *testing.T, cnr cid.ID, singleSize, totalSize uint64) (oid.ID, []*objectSDK.Object, *objectSDK.Object, []byte) { + pk, err := keys.NewPrivateKey() + require.NoError(t, err) + + tt := new(testTarget) + p := transformer.NewPayloadSizeLimiter(transformer.Params{ + Key: &pk.PrivateKey, + NextTargetInit: func() transformer.ObjectWriter { return tt }, + NetworkState: testEpochReceiver(1), + MaxSize: singleSize, + }) + + payload := make([]byte, totalSize) + _, err = rand.Read(payload) + require.NoError(t, err) + + ver := version.Current() + hdr := objectSDK.New() + hdr.SetContainerID(cnr) + hdr.SetType(objectSDK.TypeRegular) + hdr.SetVersion(&ver) + + ctx := context.Background() + require.NoError(t, p.WriteHeader(ctx, hdr)) + + _, err = p.Write(ctx, payload) + require.NoError(t, err) + + res, err := p.Close(ctx) + require.NoError(t, err) + + if totalSize <= singleSize { + // Small object, no linking. + require.Len(t, tt.objects, 1) + return res.SelfID, tt.objects, nil, payload + } + + return *res.ParentID, tt.objects[:len(tt.objects)-1], tt.objects[len(tt.objects)-1], bytes.Clone(payload) +} + +func newRngPrm(raw bool, w ChunkWriter, off, ln uint64) RangePrm { + p := RangePrm{} + p.SetChunkWriter(w) + p.WithRawFlag(raw) + p.common = new(util.CommonPrm) + + r := objectSDK.NewRange() + r.SetOffset(off) + r.SetLength(ln) + + p.SetRange(r) + return p +} + +func TestGetRange(t *testing.T) { + var cnr container.Container + cnr.SetPlacementPolicy(netmaptest.PlacementPolicy()) + + var idCnr cid.ID + container.CalculateID(&idCnr, cnr) + + ns, as := testNodeMatrix(t, []int{2}) + + testGetRange := func(t *testing.T, svc *Service, addr oid.Address, from, to uint64, payload []byte) { + w := NewSimpleObjectWriter() + rngPrm := newRngPrm(false, w, from, to-from) + rngPrm.WithAddress(addr) + + err := svc.GetRange(context.Background(), rngPrm) + require.NoError(t, err) + if from == to { + require.Nil(t, w.Object().Payload()) + } else { + require.Equal(t, payload[from:to], w.Object().Payload()) + } + } + + newSvc := func(b *testPlacementBuilder, c *testClientCache) *Service { + const curEpoch = 13 + + return &Service{ + log: test.NewLogger(t), + localStorage: newTestStorage(), + traverserGenerator: &testTraverserGenerator{ + c: cnr, + b: map[uint64]placement.Builder{ + curEpoch: b, + }, + }, + epochSource: testEpochReceiver(curEpoch), + remoteStorageConstructor: c, + keyStore: &testKeyStorage{}, + } + } + + t.Run("small", func(t *testing.T) { + const totalSize = 5 + _, objs, _, payload := objectChain(t, idCnr, totalSize, totalSize) + require.Len(t, objs, 1) + require.Len(t, payload, totalSize) + + obj := objs[0] + addr := object.AddressOf(obj) + builder := &testPlacementBuilder{vectors: map[string][][]netmap.NodeInfo{addr.EncodeToString(): ns}} + + c1 := newTestClient() + c1.addResult(addr, obj, nil) + + svc := newSvc(builder, &testClientCache{ + clients: map[string]*testClient{ + as[0][0]: c1, + as[0][1]: c1, + }, + }) + + for from := 0; from < totalSize-1; from++ { + for to := from; to < totalSize; to++ { + t.Run(fmt.Sprintf("from=%d,to=%d", from, to), func(t *testing.T) { + testGetRange(t, svc, addr, uint64(from), uint64(to), payload) + }) + } + } + }) + t.Run("big", func(t *testing.T) { + const totalSize = 9 + id, objs, link, payload := objectChain(t, idCnr, 3, totalSize) // 3 parts + require.Equal(t, totalSize, len(payload)) + + builder := &testPlacementBuilder{vectors: map[string][][]netmap.NodeInfo{}} + builder.vectors[idCnr.EncodeToString()+"/"+id.EncodeToString()] = ns + builder.vectors[object.AddressOf(link).EncodeToString()] = ns + for i := range objs { + builder.vectors[object.AddressOf(objs[i]).EncodeToString()] = ns + } + + var addr oid.Address + addr.SetContainer(idCnr) + addr.SetObject(id) + + const ( + linkingLast = "splitinfo=last" + linkingChildren = "splitinfo=children" + linkingBoth = "splitinfo=both" + ) + + lastID, _ := objs[len(objs)-1].ID() + linkID, _ := link.ID() + + for _, kind := range []string{linkingLast, linkingChildren, linkingBoth} { + t.Run(kind, func(t *testing.T) { + c1 := newTestClient() + for i := range objs { + c1.addResult(object.AddressOf(objs[i]), objs[i], nil) + } + + c1.addResult(object.AddressOf(link), link, nil) + + si := objectSDK.NewSplitInfo() + switch kind { + case linkingLast: + si.SetLastPart(lastID) + case linkingChildren: + si.SetLink(linkID) + case linkingBoth: + si.SetLastPart(lastID) + si.SetLink(linkID) + } + c1.addResult(addr, nil, objectSDK.NewSplitInfoError(si)) + + svc := newSvc(builder, &testClientCache{ + clients: map[string]*testClient{ + as[0][0]: c1, + as[0][1]: c1, + }, + }) + + for from := 0; from < totalSize-1; from++ { + for to := from; to < totalSize; to++ { + t.Run(fmt.Sprintf("from=%d,to=%d", from, to), func(t *testing.T) { + testGetRange(t, svc, addr, uint64(from), uint64(to), payload) + }) + } + } + }) + } + }) +} + func TestGetFromPastEpoch(t *testing.T) { ctx := context.Background() diff --git a/pkg/services/object/internal/client/client.go b/pkg/services/object/internal/client/client.go index dd2de0fd1..4634c96e1 100644 --- a/pkg/services/object/internal/client/client.go +++ b/pkg/services/object/internal/client/client.go @@ -348,7 +348,7 @@ func PayloadRange(ctx context.Context, prm PayloadRangePrm) (*PayloadRangeRes, e ln = maxInitialBufferSize } - w := bytes.NewBuffer(make([]byte, ln)) + w := bytes.NewBuffer(make([]byte, 0, ln)) _, err = io.CopyN(w, rdr, int64(prm.ln)) if err != nil { return nil, fmt.Errorf("read payload: %w", err) diff --git a/pkg/services/tree/ape.go b/pkg/services/tree/ape.go new file mode 100644 index 000000000..5da49a591 --- /dev/null +++ b/pkg/services/tree/ape.go @@ -0,0 +1,70 @@ +package tree + +import ( + "encoding/hex" + "fmt" + "strings" + + "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/ape/converter" + aperequest "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/ape/request" + core "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/core/container" + apistatus "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client/status" + cnrSDK "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/acl" + cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id" + apechain "git.frostfs.info/TrueCloudLab/policy-engine/pkg/chain" + "git.frostfs.info/TrueCloudLab/policy-engine/pkg/engine" + nativeschema "git.frostfs.info/TrueCloudLab/policy-engine/schema/native" + "github.com/nspcc-dev/neo-go/pkg/crypto/keys" +) + +func (s *Service) checkAPE(container *core.Container, cid cid.ID, operation acl.Op, role acl.Role, publicKey *keys.PublicKey) error { + namespace := "" + cntNamespace, hasNamespace := strings.CutSuffix(cnrSDK.ReadDomain(container.Value).Zone(), ".ns") + if hasNamespace { + namespace = cntNamespace + } + + schemaMethod, err := converter.SchemaMethodFromACLOperation(operation) + if err != nil { + return apeErr(err) + } + schemaRole, err := converter.SchemaRoleFromACLRole(role) + if err != nil { + return apeErr(err) + } + reqProps := map[string]string{ + nativeschema.PropertyKeyActorPublicKey: hex.EncodeToString(publicKey.Bytes()), + nativeschema.PropertyKeyActorRole: schemaRole, + } + + var resourceName string + if namespace == "root" || namespace == "" { + resourceName = fmt.Sprintf(nativeschema.ResourceFormatRootContainerObjects, cid.EncodeToString()) + } else { + resourceName = fmt.Sprintf(nativeschema.ResourceFormatNamespaceContainerObjects, namespace, cid.EncodeToString()) + } + + request := aperequest.NewRequest( + schemaMethod, + aperequest.NewResource(resourceName, make(map[string]string)), + reqProps, + ) + + rt := engine.NewRequestTargetExtended(namespace, cid.EncodeToString(), fmt.Sprintf("%s:%s", namespace, publicKey.Address()), nil) + status, found, err := s.router.IsAllowed(apechain.Ingress, rt, request) + if err != nil { + return apeErr(err) + } + if found && status == apechain.Allow { + return nil + } + err = fmt.Errorf("access to operation %s is denied by access policy engine: %s", schemaMethod, status.String()) + return apeErr(err) +} + +func apeErr(err error) error { + errAccessDenied := &apistatus.ObjectAccessDenied{} + errAccessDenied.WriteReason(err.Error()) + return errAccessDenied +} diff --git a/pkg/services/tree/getsubtree_test.go b/pkg/services/tree/getsubtree_test.go index 4bf679984..9a4223e30 100644 --- a/pkg/services/tree/getsubtree_test.go +++ b/pkg/services/tree/getsubtree_test.go @@ -130,7 +130,7 @@ func TestGetSubTreeOrderAsc(t *testing.T) { t.Run("boltdb forest", func(t *testing.T) { p := pilorama.NewBoltForest(pilorama.WithPath(filepath.Join(t.TempDir(), "pilorama"))) - require.NoError(t, p.Open(context.Background(), 0644)) + require.NoError(t, p.Open(context.Background(), 0o644)) require.NoError(t, p.Init()) testGetSubTreeOrderAsc(t, p) }) diff --git a/pkg/services/tree/options.go b/pkg/services/tree/options.go index 043e12cb2..0e5f64274 100644 --- a/pkg/services/tree/options.go +++ b/pkg/services/tree/options.go @@ -9,6 +9,7 @@ import ( "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/pilorama" "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/util/logger" cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id" + policyengine "git.frostfs.info/TrueCloudLab/policy-engine/pkg/engine" "github.com/nspcc-dev/neo-go/pkg/crypto/keys" ) @@ -38,6 +39,8 @@ type cfg struct { containerCacheSize int authorizedKeys [][]byte + router policyengine.ChainRouter + metrics MetricsRegister } @@ -139,3 +142,9 @@ func WithAuthorizedKeys(keys keys.PublicKeys) Option { } } } + +func WithAPERouter(router policyengine.ChainRouter) Option { + return func(c *cfg) { + c.router = router + } +} diff --git a/pkg/services/tree/service.go b/pkg/services/tree/service.go index 903db4455..6d3e24432 100644 --- a/pkg/services/tree/service.go +++ b/pkg/services/tree/service.go @@ -446,7 +446,7 @@ func getSortedSubTree(ctx context.Context, srv TreeService_GetSubTreeServer, cid type stackItem struct { values []pilorama.NodeInfo parent pilorama.Node - last string + last *string } // Traverse the tree in a DFS manner. Because we need to support arbitrary depth, @@ -502,7 +502,7 @@ func getSortedSubTree(ctx context.Context, srv TreeService_GetSubTreeServer, cid } if b.GetDepth() == 0 || uint32(len(stack)) < b.GetDepth() { - children, last, err := forest.TreeSortedByFilename(ctx, cid, b.GetTreeId(), node.ID, "", batchSize) + children, last, err := forest.TreeSortedByFilename(ctx, cid, b.GetTreeId(), node.ID, nil, batchSize) if err != nil { return err } diff --git a/pkg/services/tree/signature.go b/pkg/services/tree/signature.go index 985e1ad94..162b189e3 100644 --- a/pkg/services/tree/signature.go +++ b/pkg/services/tree/signature.go @@ -71,7 +71,7 @@ func (s *Service) verifyClient(req message, cid cidSDK.ID, rawBearer []byte, op return err } - role, err := roleFromReq(cnr, req, bt) + role, pubKey, err := roleAndPubKeyFromReq(cnr, req, bt) if err != nil { return fmt.Errorf("can't get request role: %w", err) } @@ -79,8 +79,11 @@ func (s *Service) verifyClient(req message, cid cidSDK.ID, rawBearer []byte, op basicACL := cnr.Value.BasicACL() // Basic ACL mask can be unset, if a container operations are performed // with strict APE checks only. + // + // FIXME(@aarifullin): tree service temporiraly performs APE checks on + // object verbs, because tree verbs have not been introduced yet. if basicACL == 0x0 { - return nil + return s.checkAPE(cnr, cid, op, role, pubKey) } if !basicACL.IsOpAllowed(op, role) { @@ -222,7 +225,7 @@ func SignMessage(m message, key *ecdsa.PrivateKey) error { return nil } -func roleFromReq(cnr *core.Container, req message, bt *bearer.Token) (acl.Role, error) { +func roleAndPubKeyFromReq(cnr *core.Container, req message, bt *bearer.Token) (acl.Role, *keys.PublicKey, error) { role := acl.RoleOthers owner := cnr.Value.Owner() @@ -233,7 +236,7 @@ func roleFromReq(cnr *core.Container, req message, bt *bearer.Token) (acl.Role, pub, err := keys.NewPublicKeyFromBytes(rawKey, elliptic.P256()) if err != nil { - return role, fmt.Errorf("invalid public key: %w", err) + return role, nil, fmt.Errorf("invalid public key: %w", err) } var reqSigner user.ID @@ -243,7 +246,7 @@ func roleFromReq(cnr *core.Container, req message, bt *bearer.Token) (acl.Role, role = acl.RoleOwner } - return role, nil + return role, pub, nil } func eACLOp(op acl.Op) eacl.Operation {