From fed28a62f671489510568b4e19daaa6b484955a5 Mon Sep 17 00:00:00 2001 From: Evgenii Baidakov Date: Mon, 5 May 2025 14:39:19 +0400 Subject: [PATCH] *: Get metadata consistency policy from contract Closes #1148. Signed-off-by: Evgenii Baidakov --- api/handler/api.go | 3 ++- api/handler/put.go | 18 +++++++++++++++--- api/layer/layer.go | 10 +++++++++- api/layer/neofs.go | 3 +-- api/layer/neofs_mock.go | 2 +- cmd/s3-gw/app.go | 10 ++++++++++ cmd/s3-gw/storage_policy.go | 6 +++--- cmd/s3-gw/storage_policy_provider.go | 28 +++++++++++++++++++++++----- internal/neofs/neofs.go | 10 +++++----- 9 files changed, 69 insertions(+), 21 deletions(-) diff --git a/api/handler/api.go b/api/handler/api.go index 0fc409f9..553c58a2 100644 --- a/api/handler/api.go +++ b/api/handler/api.go @@ -35,6 +35,7 @@ type ( NotificatorEnabled bool CopiesNumber uint32 MaxDeletePerRequest int + ContainerMetadataPolicy string } PlacementPolicy interface { @@ -46,7 +47,7 @@ type ( PlacementPolicyProvider interface { // GetPlacementPolicy get policy by name. // Returns [models.ErrNotFound] if policy not found. - GetPlacementPolicy(userAddr util.Uint160, policyName string) (*netmap.PlacementPolicy, error) + GetPlacementPolicy(userAddr util.Uint160, policyName string) (*layer.PlacementPolicy, error) } // ACLStateProvider get bucket ACL state. diff --git a/api/handler/put.go b/api/handler/put.go index 79d65b30..6333d200 100644 --- a/api/handler/put.go +++ b/api/handler/put.go @@ -825,20 +825,32 @@ func (h *handler) CreateBucketHandler(w http.ResponseWriter, r *http.Request) { } func (h handler) setPolicy(prm *layer.CreateBucketParams, userAddr util.Uint160, locationConstraint string, userPolicies []*accessbox.ContainerPolicy) { - prm.Policy = h.cfg.Policy.Default() + prm.Policy = layer.PlacementPolicy{ + Version: layer.PlacementPolicyV1, + Placement: h.cfg.Policy.Default(), + Consistency: h.cfg.ContainerMetadataPolicy, + } if locationConstraint == "" { return } if policy, ok := h.cfg.Policy.Get(locationConstraint); ok { - prm.Policy = policy + prm.Policy = layer.PlacementPolicy{ + Version: layer.PlacementPolicyV1, + Placement: policy, + Consistency: h.cfg.ContainerMetadataPolicy, + } prm.LocationConstraint = locationConstraint } for _, placementPolicy := range userPolicies { if placementPolicy.LocationConstraint == locationConstraint { - prm.Policy = placementPolicy.Policy + prm.Policy = layer.PlacementPolicy{ + Version: layer.PlacementPolicyV1, + Placement: placementPolicy.Policy, + Consistency: h.cfg.ContainerMetadataPolicy, + } prm.LocationConstraint = locationConstraint return } diff --git a/api/layer/layer.go b/api/layer/layer.go index 588ec6e6..8c0ad12e 100644 --- a/api/layer/layer.go +++ b/api/layer/layer.go @@ -158,7 +158,7 @@ type ( // CreateBucketParams stores bucket create request parameters. CreateBucketParams struct { Name string - Policy netmap.PlacementPolicy + Policy PlacementPolicy EACL *eacl.Table SessionContainerCreation *session.Container SessionEACL *session.Container @@ -211,6 +211,12 @@ type ( ObjectInfo *data.ObjectInfo } + PlacementPolicy struct { + Version int + Placement netmap.PlacementPolicy + Consistency string + } + // Client provides S3 API client interface. Client interface { Initialize(ctx context.Context, c EventListener) error @@ -281,6 +287,8 @@ const ( AESEncryptionAlgorithm = "AES256" AESKeySize = 32 AttributeNeofsCopiesNumber = "neofs-copies-number" // such format to match X-Amz-Meta-Neofs-Copies-Number header + + PlacementPolicyV1 = 1 ) var ( diff --git a/api/layer/neofs.go b/api/layer/neofs.go index 5e5134d5..0279632f 100644 --- a/api/layer/neofs.go +++ b/api/layer/neofs.go @@ -15,7 +15,6 @@ import ( "github.com/nspcc-dev/neofs-sdk-go/container/acl" cid "github.com/nspcc-dev/neofs-sdk-go/container/id" "github.com/nspcc-dev/neofs-sdk-go/eacl" - "github.com/nspcc-dev/neofs-sdk-go/netmap" "github.com/nspcc-dev/neofs-sdk-go/object" oid "github.com/nspcc-dev/neofs-sdk-go/object/id" "github.com/nspcc-dev/neofs-sdk-go/session" @@ -31,7 +30,7 @@ type PrmContainerCreate struct { CreatorPubKey keys.PublicKey // Container placement policy. - Policy netmap.PlacementPolicy + Policy PlacementPolicy // Name for the container. Name string diff --git a/api/layer/neofs_mock.go b/api/layer/neofs_mock.go index 30702ea4..0a9c5c00 100644 --- a/api/layer/neofs_mock.go +++ b/api/layer/neofs_mock.go @@ -92,7 +92,7 @@ func (t *TestNeoFS) CreateContainer(_ context.Context, prm PrmContainerCreate) ( var cnr container.Container cnr.Init() cnr.SetOwner(prm.Creator) - cnr.SetPlacementPolicy(prm.Policy) + cnr.SetPlacementPolicy(prm.Policy.Placement) cnr.SetBasicACL(prm.BasicACL) creationTime := prm.CreationTime diff --git a/cmd/s3-gw/app.go b/cmd/s3-gw/app.go index 58f3270b..25d5ce44 100644 --- a/cmd/s3-gw/app.go +++ b/cmd/s3-gw/app.go @@ -800,6 +800,16 @@ func (a *App) initHandler() { cfg.MaxDeletePerRequest = defaultMaxObjectDeletePerRequest } + cfg.ContainerMetadataPolicy = a.cfg.GetString(cfgContainerMetadataPolicy) + + switch cfg.ContainerMetadataPolicy { + case neofs.ContainerMetaDataPolicyOptimistic: + case neofs.ContainerMetaDataPolicyStrict: + case "": + default: + a.log.Fatal("unsupported config value", zap.String("option", cfgContainerMetadataPolicy)) + } + var err error a.api, err = handler.New(a.log, a.obj, a.nc, cfg) if err != nil { diff --git a/cmd/s3-gw/storage_policy.go b/cmd/s3-gw/storage_policy.go index 52f846a3..872dedb2 100644 --- a/cmd/s3-gw/storage_policy.go +++ b/cmd/s3-gw/storage_policy.go @@ -4,7 +4,7 @@ import ( "sync" "github.com/nspcc-dev/neo-go/pkg/util" - "github.com/nspcc-dev/neofs-sdk-go/netmap" + "github.com/nspcc-dev/neofs-s3-gw/api/layer" ) type ( @@ -14,7 +14,7 @@ type ( } servicePolicyProvider interface { - GetPlacementPolicy(userAddr util.Uint160, policyName string) (*netmap.PlacementPolicy, error) + GetPlacementPolicy(userAddr util.Uint160, policyName string) (*layer.PlacementPolicy, error) } ) @@ -25,7 +25,7 @@ func newStoragePolicyService(provider servicePolicyProvider) *storagePolicyServi } } -func (s *storagePolicyService) GetPlacementPolicy(userAddr util.Uint160, policyName string) (*netmap.PlacementPolicy, error) { +func (s *storagePolicyService) GetPlacementPolicy(userAddr util.Uint160, policyName string) (*layer.PlacementPolicy, error) { s.mu.RLock() defer s.mu.RUnlock() diff --git a/cmd/s3-gw/storage_policy_provider.go b/cmd/s3-gw/storage_policy_provider.go index b99c2608..0e89a0c2 100644 --- a/cmd/s3-gw/storage_policy_provider.go +++ b/cmd/s3-gw/storage_policy_provider.go @@ -2,6 +2,7 @@ package main import ( "context" + "encoding/json" "errors" "fmt" "strings" @@ -12,6 +13,7 @@ import ( "github.com/nspcc-dev/neo-go/pkg/rpcclient/unwrap" "github.com/nspcc-dev/neo-go/pkg/util" rpcNNS "github.com/nspcc-dev/neofs-contract/rpc/nns" + "github.com/nspcc-dev/neofs-s3-gw/api/layer" "github.com/nspcc-dev/neofs-s3-gw/internal/models" "github.com/nspcc-dev/neofs-sdk-go/netmap" ) @@ -80,7 +82,7 @@ func resolveContract(cl *rpcclient.Client, inv *invoker.Invoker, contractName st return contractHash, nil } -func (p *storagePolicyProvider) GetPlacementPolicy(userAddr util.Uint160, policyName string) (*netmap.PlacementPolicy, error) { +func (p *storagePolicyProvider) GetPlacementPolicy(userAddr util.Uint160, policyName string) (*layer.PlacementPolicy, error) { payload, err := unwrap.Bytes( p.invoker().Call(p.contractHash, "resolvePolicy", userAddr, policyName), ) @@ -93,12 +95,28 @@ func (p *storagePolicyProvider) GetPlacementPolicy(userAddr util.Uint160, policy return nil, fmt.Errorf("get system storage policy: %w", err) } - var pp netmap.PlacementPolicy - if err = pp.UnmarshalJSON(payload); err != nil { + var ( + policy layer.PlacementPolicy + pp netmap.PlacementPolicy + ) + + if err = json.Unmarshal(payload, &policy); err != nil { return nil, fmt.Errorf("unmarshal placement policy: %w", err) } - return &pp, nil + switch policy.Version { + case layer.PlacementPolicyV1: + return &policy, nil + default: + if err = pp.UnmarshalJSON(payload); err != nil { + return nil, fmt.Errorf("unmarshal placement policy: %w", err) + } + + policy.Placement = pp + policy.Version = layer.PlacementPolicyV1 + } + + return &policy, nil } func (p *storagePolicyProvider) index() int { @@ -133,6 +151,6 @@ func rpcClient(ctx context.Context, endpoint string) (*rpcclient.Client, error) return cl, nil } -func (p *noOpStoragePolicyProvider) GetPlacementPolicy(_ util.Uint160, _ string) (*netmap.PlacementPolicy, error) { +func (p *noOpStoragePolicyProvider) GetPlacementPolicy(_ util.Uint160, _ string) (*layer.PlacementPolicy, error) { return nil, models.ErrNotFound } diff --git a/internal/neofs/neofs.go b/internal/neofs/neofs.go index 9d62f794..63939a35 100644 --- a/internal/neofs/neofs.go +++ b/internal/neofs/neofs.go @@ -153,7 +153,7 @@ func (x *NeoFS) CreateContainer(ctx context.Context, prm layer.PrmContainerCreat var cnr container.Container cnr.Init() - cnr.SetPlacementPolicy(prm.Policy) + cnr.SetPlacementPolicy(prm.Policy.Placement) cnr.SetOwner(prm.Creator) cnr.SetBasicACL(prm.BasicACL) @@ -181,9 +181,9 @@ func (x *NeoFS) CreateContainer(ctx context.Context, prm layer.PrmContainerCreat cnr.SetAttribute(layer.AttributeOwnerPublicKey, hex.EncodeToString(prm.CreatorPubKey.Bytes())) - if x.cfg.ContainerMetadataPolicy == ContainerMetaDataPolicyStrict || - x.cfg.ContainerMetadataPolicy == ContainerMetaDataPolicyOptimistic { - cnr.SetAttribute(containerMetaDataPolicyAttribute, x.cfg.ContainerMetadataPolicy) + if prm.Policy.Consistency == ContainerMetaDataPolicyStrict || + prm.Policy.Consistency == ContainerMetaDataPolicyOptimistic { + cnr.SetAttribute(containerMetaDataPolicyAttribute, prm.Policy.Consistency) } err := client.SyncContainerWithNetwork(ctx, &cnr, x.pool) @@ -688,7 +688,7 @@ func (x *AuthmateNeoFS) CreateContainer(ctx context.Context, prm authmate.PrmCon return x.neoFS.CreateContainer(ctx, layer.PrmContainerCreate{ Creator: prm.Owner, - Policy: prm.Policy, + Policy: layer.PlacementPolicy{Placement: prm.Policy, Version: layer.PlacementPolicyV1}, Name: prm.FriendlyName, BasicACL: basicACL, CreatorPubKey: prm.CreatorPubKey,