[#260] Use namespace as domain when create bucket

Signed-off-by: Denis Kirillov <d.kirillov@yadro.com>
This commit is contained in:
Denis Kirillov 2023-11-23 10:44:50 +03:00
parent 9ebfca654b
commit ff1ec56d24
12 changed files with 87 additions and 37 deletions

15
api/cache/buckets.go vendored
View file

@ -40,8 +40,7 @@ func NewBucketCache(config *Config) *BucketCache {
// Get returns a cached object. // Get returns a cached object.
func (o *BucketCache) Get(ns, bktName string) *data.BucketInfo { func (o *BucketCache) Get(ns, bktName string) *data.BucketInfo {
key := ns + "/" + bktName entry, err := o.cache.Get(formKey(ns, bktName))
entry, err := o.cache.Get(key)
if err != nil { if err != nil {
return nil return nil
} }
@ -57,11 +56,15 @@ func (o *BucketCache) Get(ns, bktName string) *data.BucketInfo {
} }
// Put puts an object to cache. // Put puts an object to cache.
func (o *BucketCache) Put(ns string, bkt *data.BucketInfo) error { func (o *BucketCache) Put(bkt *data.BucketInfo) error {
return o.cache.Set(ns+"/"+bkt.Name, bkt) return o.cache.Set(formKey(bkt.Zone, bkt.Name), bkt)
} }
// Delete deletes an object from cache. // Delete deletes an object from cache.
func (o *BucketCache) Delete(ns, bktName string) bool { func (o *BucketCache) Delete(bkt *data.BucketInfo) bool {
return o.cache.Remove(ns + "/" + bktName) return o.cache.Remove(formKey(bkt.Zone, bkt.Name))
}
func formKey(ns, name string) string {
return name + "." + ns
} }

View file

@ -36,15 +36,15 @@ func TestBucketsCacheType(t *testing.T) {
bktInfo := &data.BucketInfo{Name: "bucket"} bktInfo := &data.BucketInfo{Name: "bucket"}
err := cache.Put("", bktInfo) err := cache.Put(bktInfo)
require.NoError(t, err) require.NoError(t, err)
val := cache.Get("", bktInfo.Name) val := cache.Get("", bktInfo.Name)
require.Equal(t, bktInfo, val) require.Equal(t, bktInfo, val)
require.Equal(t, 0, observedLog.Len()) require.Equal(t, 0, observedLog.Len())
err = cache.cache.Set("/"+bktInfo.Name, "tmp") err = cache.cache.Set(bktInfo.Name+"."+bktInfo.Zone, "tmp")
require.NoError(t, err) require.NoError(t, err)
assertInvalidCacheEntry(t, cache.Get("", bktInfo.Name), observedLog) assertInvalidCacheEntry(t, cache.Get(bktInfo.Zone, bktInfo.Name), observedLog)
} }
func TestObjectNamesCacheType(t *testing.T) { func TestObjectNamesCacheType(t *testing.T) {

View file

@ -748,7 +748,8 @@ func (h *handler) CreateBucketHandler(w http.ResponseWriter, r *http.Request) {
ctx := r.Context() ctx := r.Context()
reqInfo := middleware.GetReqInfo(ctx) reqInfo := middleware.GetReqInfo(ctx)
p := &layer.CreateBucketParams{ p := &layer.CreateBucketParams{
Name: reqInfo.BucketName, Name: reqInfo.BucketName,
Namespace: reqInfo.Namespace,
} }
if err := checkBucketName(reqInfo.BucketName); err != nil { if err := checkBucketName(reqInfo.BucketName); err != nil {

View file

@ -380,6 +380,26 @@ func TestCreateBucket(t *testing.T) {
createBucketAssertS3Error(hc, bktName, box2, s3errors.ErrBucketAlreadyExists) createBucketAssertS3Error(hc, bktName, box2, s3errors.ErrBucketAlreadyExists)
} }
func TestCreateNamespacedBucket(t *testing.T) {
hc := prepareHandlerContext(t)
bktName := "bkt-name"
namespace := "yabloko"
box, _ := createAccessBox(t)
w, r := prepareTestRequest(hc, bktName, "", nil)
ctx := middleware.SetBoxData(r.Context(), box)
reqInfo := middleware.GetReqInfo(ctx)
reqInfo.Namespace = namespace
r = r.WithContext(middleware.SetReqInfo(ctx, reqInfo))
hc.Handler().CreateBucketHandler(w, r)
assertStatus(t, w, http.StatusOK)
bktInfo, err := hc.Layer().GetBucketInfo(middleware.SetReqInfo(hc.Context(), reqInfo), bktName)
require.NoError(t, err)
require.Equal(t, namespace+".ns", bktInfo.Zone)
}
func TestPutObjectClientCut(t *testing.T) { func TestPutObjectClientCut(t *testing.T) {
hc := prepareHandlerContext(t) hc := prepareHandlerContext(t)
bktName, objName1, objName2 := "bkt-name", "obj-name1", "obj-name2" bktName, objName1, objName2 := "bkt-name", "obj-name1", "obj-name2"

View file

@ -60,18 +60,18 @@ func (c *Cache) GetBucket(ns, name string) *data.BucketInfo {
return c.bucketCache.Get(ns, name) return c.bucketCache.Get(ns, name)
} }
func (c *Cache) PutBucket(ns string, bktInfo *data.BucketInfo) { func (c *Cache) PutBucket(bktInfo *data.BucketInfo) {
if err := c.bucketCache.Put(ns, bktInfo); err != nil { if err := c.bucketCache.Put(bktInfo); err != nil {
c.logger.Warn(logs.CouldntPutBucketInfoIntoCache, c.logger.Warn(logs.CouldntPutBucketInfoIntoCache,
zap.String("namespace", ns), zap.String("zone", bktInfo.Zone),
zap.String("bucket name", bktInfo.Name), zap.String("bucket name", bktInfo.Name),
zap.Stringer("bucket cid", bktInfo.CID), zap.Stringer("bucket cid", bktInfo.CID),
zap.Error(err)) zap.Error(err))
} }
} }
func (c *Cache) DeleteBucket(ns, name string) { func (c *Cache) DeleteBucket(bktInfo *data.BucketInfo) {
c.bucketCache.Delete(ns, name) c.bucketCache.Delete(bktInfo)
} }
func (c *Cache) CleanListCacheEntriesContainingObject(objectName string, cnrID cid.ID) { func (c *Cache) CleanListCacheEntriesContainingObject(objectName string, cnrID cid.ID) {

View file

@ -5,7 +5,6 @@ import (
"fmt" "fmt"
"strconv" "strconv"
v2container "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/container"
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api" "git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api"
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/data" "git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/data"
s3errors "git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/errors" s3errors "git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/errors"
@ -75,7 +74,12 @@ func (n *layer) containerInfo(ctx context.Context, idCnr cid.ID) (*data.BucketIn
} }
} }
n.cache.PutBucket(reqInfo.Namespace, info) zone, _ := n.features.FormContainerZone(reqInfo.Namespace)
if zone != info.Zone {
return nil, fmt.Errorf("ns '%s' and zone '%s' are mismatched", zone, info.Zone)
}
n.cache.PutBucket(info)
return info, nil return info, nil
} }
@ -105,9 +109,12 @@ func (n *layer) createContainer(ctx context.Context, p *CreateBucketParams) (*da
if p.LocationConstraint == "" { if p.LocationConstraint == "" {
p.LocationConstraint = api.DefaultLocationConstraint // s3tests_boto3.functional.test_s3:test_bucket_get_location p.LocationConstraint = api.DefaultLocationConstraint // s3tests_boto3.functional.test_s3:test_bucket_get_location
} }
zone, _ := n.features.FormContainerZone(p.Namespace)
bktInfo := &data.BucketInfo{ bktInfo := &data.BucketInfo{
Name: p.Name, Name: p.Name,
Zone: v2container.SysAttributeZoneDefault, Zone: zone,
Owner: n.BearerOwner(ctx), Owner: n.BearerOwner(ctx),
Created: TimeNow(ctx), Created: TimeNow(ctx),
LocationConstraint: p.LocationConstraint, LocationConstraint: p.LocationConstraint,
@ -130,6 +137,7 @@ func (n *layer) createContainer(ctx context.Context, p *CreateBucketParams) (*da
Creator: bktInfo.Owner, Creator: bktInfo.Owner,
Policy: p.Policy, Policy: p.Policy,
Name: p.Name, Name: p.Name,
Zone: zone,
SessionToken: p.SessionContainerCreation, SessionToken: p.SessionContainerCreation,
CreationTime: bktInfo.Created, CreationTime: bktInfo.Created,
AdditionalAttributes: attributes, AdditionalAttributes: attributes,
@ -145,7 +153,7 @@ func (n *layer) createContainer(ctx context.Context, p *CreateBucketParams) (*da
return nil, fmt.Errorf("set container eacl: %w", err) return nil, fmt.Errorf("set container eacl: %w", err)
} }
n.cache.PutBucket(bktInfo.Zone, bktInfo) n.cache.PutBucket(bktInfo)
return bktInfo, nil return bktInfo, nil
} }

View file

@ -30,6 +30,9 @@ type PrmContainerCreate struct {
// Name for the container. // Name for the container.
Name string Name string
// Zone for container registration.
Zone string
// CreationTime value for Timestamp attribute // CreationTime value for Timestamp attribute
CreationTime time.Time CreationTime time.Time

View file

@ -10,6 +10,7 @@ import (
"io" "io"
"time" "time"
v2container "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/container"
objectv2 "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/object" objectv2 "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/object"
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/middleware" "git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/middleware"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/bearer" "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/bearer"
@ -50,6 +51,14 @@ func (k *FeatureSettingsMock) SetMD5Enabled(md5Enabled bool) {
k.md5Enabled = md5Enabled k.md5Enabled = md5Enabled
} }
func (k *FeatureSettingsMock) FormContainerZone(ns string) (zone string, isDefault bool) {
if ns == "" {
return v2container.SysAttributeZoneDefault, true
}
return ns + ".ns", false
}
type TestFrostFS struct { type TestFrostFS struct {
FrostFS FrostFS
@ -143,6 +152,7 @@ func (t *TestFrostFS) CreateContainer(_ context.Context, prm PrmContainerCreate)
if prm.Name != "" { if prm.Name != "" {
var d container.Domain var d container.Domain
d.SetName(prm.Name) d.SetName(prm.Name)
d.SetZone(prm.Zone)
container.WriteDomain(&cnr, d) container.WriteDomain(&cnr, d)
container.SetName(&cnr, prm.Name) container.SetName(&cnr, prm.Name)

View file

@ -51,6 +51,7 @@ type (
ClientCut() bool ClientCut() bool
BufferMaxSizeForPut() uint64 BufferMaxSizeForPut() uint64
MD5Enabled() bool MD5Enabled() bool
FormContainerZone(ns string) (zone string, isDefault bool)
} }
layer struct { layer struct {
@ -172,6 +173,7 @@ type (
// CreateBucketParams stores bucket create request parameters. // CreateBucketParams stores bucket create request parameters.
CreateBucketParams struct { CreateBucketParams struct {
Name string Name string
Namespace string
Policy netmap.PlacementPolicy Policy netmap.PlacementPolicy
EACL *eacl.Table EACL *eacl.Table
SessionContainerCreation *session.Container SessionContainerCreation *session.Container
@ -816,8 +818,6 @@ func (n *layer) DeleteBucket(ctx context.Context, p *DeleteBucketParams) error {
return errors.GetAPIError(errors.ErrBucketNotEmpty) return errors.GetAPIError(errors.ErrBucketNotEmpty)
} }
reqInfo := middleware.GetReqInfo(ctx) n.cache.DeleteBucket(p.BktInfo)
n.cache.DeleteBucket(reqInfo.Namespace, p.BktInfo.Name)
return n.frostFS.DeleteContainer(ctx, p.BktInfo.CID, p.SessionToken) return n.frostFS.DeleteContainer(ctx, p.BktInfo.CID, p.SessionToken)
} }

View file

@ -10,7 +10,6 @@ import (
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container" "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container"
cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id" cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/ns" "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/ns"
"golang.org/x/exp/slices"
) )
const ( const (
@ -18,8 +17,6 @@ const (
DNSResolver = "dns" DNSResolver = "dns"
) )
const nsDomain = ".ns"
// ErrNoResolvers returns when trying to resolve container without any resolver. // ErrNoResolvers returns when trying to resolve container without any resolver.
var ErrNoResolvers = errors.New("no resolvers") var ErrNoResolvers = errors.New("no resolvers")
@ -33,7 +30,7 @@ type FrostFS interface {
} }
type Settings interface { type Settings interface {
DefaultNamespaces() []string FormContainerZone(ns string) (zone string, isDefault bool)
} }
type Config struct { type Config struct {
@ -176,15 +173,16 @@ func NewDNSResolver(frostFS FrostFS, settings Settings) (*Resolver, error) {
resolveFunc := func(ctx context.Context, name string) (cid.ID, error) { resolveFunc := func(ctx context.Context, name string) (cid.ID, error) {
var err error var err error
reqInfo := middleware.GetReqInfo(ctx) reqInfo := middleware.GetReqInfo(ctx)
domain := reqInfo.Namespace + nsDomain
if slices.Contains(settings.DefaultNamespaces(), domain) { zone, isDefault := settings.FormContainerZone(reqInfo.Namespace)
domain, err = frostFS.SystemDNS(ctx) if isDefault {
zone, err = frostFS.SystemDNS(ctx)
if err != nil { if err != nil {
return cid.ID{}, fmt.Errorf("read system DNS parameter of the FrostFS: %w", err) return cid.ID{}, fmt.Errorf("read system DNS parameter of the FrostFS: %w", err)
} }
} }
domain = name + "." + domain domain := name + "." + zone
cnrID, err := dns.ResolveContainerName(domain) cnrID, err := dns.ResolveContainerName(domain)
if err != nil { if err != nil {
return cid.ID{}, fmt.Errorf("couldn't resolve container '%s' as '%s': %w", name, domain, err) return cid.ID{}, fmt.Errorf("couldn't resolve container '%s' as '%s': %w", name, domain, err)
@ -217,9 +215,8 @@ func NewNNSResolver(address string, settings Settings) (*Resolver, error) {
d.SetName(name) d.SetName(name)
reqInfo := middleware.GetReqInfo(ctx) reqInfo := middleware.GetReqInfo(ctx)
if !slices.Contains(settings.DefaultNamespaces(), reqInfo.Namespace) { zone, _ := settings.FormContainerZone(reqInfo.Namespace)
d.SetZone(reqInfo.Namespace + nsDomain) d.SetZone(zone)
}
cnrID, err := nns.ResolveContainerDomain(d) cnrID, err := nns.ResolveContainerDomain(d)
if err != nil { if err != nil {

View file

@ -15,6 +15,7 @@ import (
"syscall" "syscall"
"time" "time"
v2container "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/container"
"git.frostfs.info/TrueCloudLab/frostfs-observability/tracing" "git.frostfs.info/TrueCloudLab/frostfs-observability/tracing"
grpctracing "git.frostfs.info/TrueCloudLab/frostfs-observability/tracing/grpc" grpctracing "git.frostfs.info/TrueCloudLab/frostfs-observability/tracing/grpc"
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api" "git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api"
@ -41,6 +42,7 @@ import (
"github.com/nspcc-dev/neo-go/pkg/crypto/keys" "github.com/nspcc-dev/neo-go/pkg/crypto/keys"
"github.com/spf13/viper" "github.com/spf13/viper"
"go.uber.org/zap" "go.uber.org/zap"
"golang.org/x/exp/slices"
"google.golang.org/grpc" "google.golang.org/grpc"
) )
@ -341,14 +343,19 @@ func (s *appSettings) setNamespaceHeader(nsHeader string) {
s.mu.Unlock() s.mu.Unlock()
} }
func (s *appSettings) DefaultNamespaces() []string { func (s *appSettings) FormContainerZone(ns string) (zone string, isDefault bool) {
s.mu.RLock() s.mu.RLock()
defer s.mu.RUnlock() namespaces := s.defaultNamespaces
return s.defaultNamespaces s.mu.RUnlock()
if slices.Contains(namespaces, ns) {
return v2container.SysAttributeZoneDefault, true
}
return ns + ".ns", false
} }
func (s *appSettings) setDefaultNamespaces(namespaces []string) { func (s *appSettings) setDefaultNamespaces(namespaces []string) {
for i := range namespaces { // to be set namespaces in evn variable as `S3_GW_KLUDGE_DEFAULT_NAMESPACES="" "root"` for i := range namespaces { // to be set namespaces in env variable as `S3_GW_KLUDGE_DEFAULT_NAMESPACES="" "root"`
namespaces[i] = strings.Trim(namespaces[i], "\"") namespaces[i] = strings.Trim(namespaces[i], "\"")
} }

View file

@ -126,6 +126,7 @@ func (x *FrostFS) CreateContainer(ctx context.Context, prm layer.PrmContainerCre
if prm.Name != "" { if prm.Name != "" {
var d container.Domain var d container.Domain
d.SetName(prm.Name) d.SetName(prm.Name)
d.SetZone(prm.Zone)
container.WriteDomain(&cnr, d) container.WriteDomain(&cnr, d)
container.SetName(&cnr, prm.Name) container.SetName(&cnr, prm.Name)