feature/260-support_frostfsid #265

Merged
alexvanin merged 1 commit from dkirillov/frostfs-s3-gw:feature/260-support_frostfsid into master 2024-09-04 19:51:12 +00:00
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.
func (o *BucketCache) Get(ns, bktName string) *data.BucketInfo {
key := ns + "/" + bktName
entry, err := o.cache.Get(key)
entry, err := o.cache.Get(formKey(ns, bktName))
if err != nil {
return nil
}
@ -57,11 +56,15 @@ func (o *BucketCache) Get(ns, bktName string) *data.BucketInfo {
}
// Put puts an object to cache.
func (o *BucketCache) Put(ns string, bkt *data.BucketInfo) error {
return o.cache.Set(ns+"/"+bkt.Name, bkt)
func (o *BucketCache) Put(bkt *data.BucketInfo) error {
return o.cache.Set(formKey(bkt.Zone, bkt.Name), bkt)

Is it too bad to keep namespace string as a part of BucketInfo struct? Technically it is bucket info when bucket is created.

Is it too bad to keep namespace string as a part of BucketInfo struct? Technically it is bucket info when bucket is created.
}
// Delete deletes an object from cache.
func (o *BucketCache) Delete(ns, bktName string) bool {
return o.cache.Remove(ns + "/" + bktName)
func (o *BucketCache) Delete(bkt *data.BucketInfo) bool {
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"}
err := cache.Put("", bktInfo)
err := cache.Put(bktInfo)
require.NoError(t, err)
val := cache.Get("", bktInfo.Name)
require.Equal(t, bktInfo, val)
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)
assertInvalidCacheEntry(t, cache.Get("", bktInfo.Name), observedLog)
assertInvalidCacheEntry(t, cache.Get(bktInfo.Zone, bktInfo.Name), observedLog)
}
func TestObjectNamesCacheType(t *testing.T) {

View file

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

View file

@ -380,6 +380,26 @@ func TestCreateBucket(t *testing.T) {
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) {
hc := prepareHandlerContext(t)
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)
}
func (c *Cache) PutBucket(ns string, bktInfo *data.BucketInfo) {
if err := c.bucketCache.Put(ns, bktInfo); err != nil {
func (c *Cache) PutBucket(bktInfo *data.BucketInfo) {
if err := c.bucketCache.Put(bktInfo); err != nil {
c.logger.Warn(logs.CouldntPutBucketInfoIntoCache,
zap.String("namespace", ns),
zap.String("zone", bktInfo.Zone),
zap.String("bucket name", bktInfo.Name),
zap.Stringer("bucket cid", bktInfo.CID),
zap.Error(err))
}
}
func (c *Cache) DeleteBucket(ns, name string) {
c.bucketCache.Delete(ns, name)
func (c *Cache) DeleteBucket(bktInfo *data.BucketInfo) {
c.bucketCache.Delete(bktInfo)
}
func (c *Cache) CleanListCacheEntriesContainingObject(objectName string, cnrID cid.ID) {

View file

@ -5,7 +5,6 @@ import (
"fmt"
"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/data"
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
}
@ -105,9 +109,12 @@ func (n *layer) createContainer(ctx context.Context, p *CreateBucketParams) (*da
if p.LocationConstraint == "" {
p.LocationConstraint = api.DefaultLocationConstraint // s3tests_boto3.functional.test_s3:test_bucket_get_location
}
zone, _ := n.features.FormContainerZone(p.Namespace)
bktInfo := &data.BucketInfo{
Name: p.Name,
Zone: v2container.SysAttributeZoneDefault,
Zone: zone,
Owner: n.BearerOwner(ctx),
Created: TimeNow(ctx),
LocationConstraint: p.LocationConstraint,
@ -130,6 +137,7 @@ func (n *layer) createContainer(ctx context.Context, p *CreateBucketParams) (*da
Creator: bktInfo.Owner,
Policy: p.Policy,
Name: p.Name,
Zone: zone,
SessionToken: p.SessionContainerCreation,
CreationTime: bktInfo.Created,
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)
}
n.cache.PutBucket(bktInfo.Zone, bktInfo)
n.cache.PutBucket(bktInfo)
return bktInfo, nil
}

View file

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

View file

@ -10,6 +10,7 @@ import (
"io"
"time"
v2container "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/container"
objectv2 "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/object"
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/middleware"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/bearer"
@ -50,6 +51,14 @@ func (k *FeatureSettingsMock) SetMD5Enabled(md5Enabled bool) {
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 {
FrostFS
@ -143,6 +152,7 @@ func (t *TestFrostFS) CreateContainer(_ context.Context, prm PrmContainerCreate)
if prm.Name != "" {
var d container.Domain
d.SetName(prm.Name)
d.SetZone(prm.Zone)
container.WriteDomain(&cnr, d)
container.SetName(&cnr, prm.Name)

View file

@ -51,6 +51,7 @@ type (
ClientCut() bool
BufferMaxSizeForPut() uint64
MD5Enabled() bool
FormContainerZone(ns string) (zone string, isDefault bool)
}
layer struct {
@ -172,6 +173,7 @@ type (
// CreateBucketParams stores bucket create request parameters.
CreateBucketParams struct {
Name string
Namespace string
Policy netmap.PlacementPolicy
EACL *eacl.Table
SessionContainerCreation *session.Container
@ -816,8 +818,6 @@ func (n *layer) DeleteBucket(ctx context.Context, p *DeleteBucketParams) error {
return errors.GetAPIError(errors.ErrBucketNotEmpty)
}
reqInfo := middleware.GetReqInfo(ctx)
n.cache.DeleteBucket(reqInfo.Namespace, p.BktInfo.Name)
n.cache.DeleteBucket(p.BktInfo)
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"
cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/ns"
"golang.org/x/exp/slices"
)
const (
@ -18,8 +17,6 @@ const (
DNSResolver = "dns"
)
const nsDomain = ".ns"
// ErrNoResolvers returns when trying to resolve container without any resolver.
var ErrNoResolvers = errors.New("no resolvers")
@ -33,7 +30,7 @@ type FrostFS interface {
}
type Settings interface {
DefaultNamespaces() []string
FormContainerZone(ns string) (zone string, isDefault bool)
}
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) {
var err error
reqInfo := middleware.GetReqInfo(ctx)
domain := reqInfo.Namespace + nsDomain
if slices.Contains(settings.DefaultNamespaces(), domain) {
domain, err = frostFS.SystemDNS(ctx)
zone, isDefault := settings.FormContainerZone(reqInfo.Namespace)
if isDefault {
zone, err = frostFS.SystemDNS(ctx)
if err != nil {
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)
if err != nil {
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)
reqInfo := middleware.GetReqInfo(ctx)
if !slices.Contains(settings.DefaultNamespaces(), reqInfo.Namespace) {
d.SetZone(reqInfo.Namespace + nsDomain)
}
zone, _ := settings.FormContainerZone(reqInfo.Namespace)
d.SetZone(zone)
cnrID, err := nns.ResolveContainerDomain(d)
if err != nil {

View file

@ -15,6 +15,7 @@ import (
"syscall"
"time"
v2container "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/container"
"git.frostfs.info/TrueCloudLab/frostfs-observability/tracing"
grpctracing "git.frostfs.info/TrueCloudLab/frostfs-observability/tracing/grpc"
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api"
@ -41,6 +42,7 @@ import (
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
"github.com/spf13/viper"
"go.uber.org/zap"
"golang.org/x/exp/slices"
"google.golang.org/grpc"
)
@ -341,14 +343,19 @@ func (s *appSettings) setNamespaceHeader(nsHeader string) {
s.mu.Unlock()
}
func (s *appSettings) DefaultNamespaces() []string {
func (s *appSettings) FormContainerZone(ns string) (zone string, isDefault bool) {
s.mu.RLock()
defer s.mu.RUnlock()
return s.defaultNamespaces
namespaces := s.defaultNamespaces
s.mu.RUnlock()
if slices.Contains(namespaces, ns) {
return v2container.SysAttributeZoneDefault, true
}
return ns + ".ns", false
}
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"`

evn typo

evn typo
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 != "" {
var d container.Domain
d.SetName(prm.Name)
d.SetZone(prm.Zone)
container.WriteDomain(&cnr, d)
container.SetName(&cnr, prm.Name)