[#568] Add configuration for region to policy map

Signed-off-by: Denis Kirillov <denis@nspcc.ru>
This commit is contained in:
Denis Kirillov 2022-11-03 09:34:18 +03:00 committed by Alex Vanin
parent 3212805955
commit d47840f137
5 changed files with 101 additions and 44 deletions

View file

@ -24,12 +24,17 @@ type (
// Config contains data which handler needs to keep. // Config contains data which handler needs to keep.
Config struct { Config struct {
DefaultPolicy netmap.PlacementPolicy Policy PlacementPolicy
DefaultMaxAge int DefaultMaxAge int
NotificatorEnabled bool NotificatorEnabled bool
TLSEnabled bool TLSEnabled bool
CopiesNumber uint32 CopiesNumber uint32
} }
PlacementPolicy struct {
Default netmap.PlacementPolicy
RegionMap map[string]netmap.PlacementPolicy
}
) )
const ( const (

View file

@ -123,6 +123,7 @@ func (h *handler) HeadBucketHandler(w http.ResponseWriter, r *http.Request) {
} }
w.Header().Set(api.ContainerID, bktInfo.CID.EncodeToString()) w.Header().Set(api.ContainerID, bktInfo.CID.EncodeToString())
w.Header().Set(api.AmzBucketRegion, bktInfo.LocationConstraint)
api.WriteResponse(w, http.StatusOK, nil, api.MimeNone) api.WriteResponse(w, http.StatusOK, nil, api.MimeNone)
} }

View file

@ -667,12 +667,10 @@ func parseMetadata(r *http.Request) map[string]string {
} }
func (h *handler) CreateBucketHandler(w http.ResponseWriter, r *http.Request) { func (h *handler) CreateBucketHandler(w http.ResponseWriter, r *http.Request) {
var ( reqInfo := api.GetReqInfo(r.Context())
reqInfo = api.GetReqInfo(r.Context()) p := &layer.CreateBucketParams{
p = layer.CreateBucketParams{
Name: reqInfo.BucketName, Name: reqInfo.BucketName,
} }
)
if err := checkBucketName(reqInfo.BucketName); err != nil { if err := checkBucketName(reqInfo.BucketName); err != nil {
h.logAndSendError(w, "invalid bucket name", reqInfo, err) h.logAndSendError(w, "invalid bucket name", reqInfo, err)
@ -722,24 +720,11 @@ func (h *handler) CreateBucketHandler(w http.ResponseWriter, r *http.Request) {
return return
} }
useDefaultPolicy := true h.setPolicy(p, createParams.LocationConstraint, policies)
if createParams.LocationConstraint != "" {
for _, placementPolicy := range policies {
if placementPolicy.LocationConstraint == createParams.LocationConstraint {
p.Policy = placementPolicy.Policy
p.LocationConstraint = createParams.LocationConstraint
useDefaultPolicy = false
break
}
}
}
if useDefaultPolicy {
p.Policy = h.cfg.DefaultPolicy
}
p.ObjectLockEnabled = isLockEnabled(r.Header) p.ObjectLockEnabled = isLockEnabled(r.Header)
bktInfo, err := h.obj.CreateBucket(r.Context(), &p) bktInfo, err := h.obj.CreateBucket(r.Context(), p)
if err != nil { if err != nil {
h.logAndSendError(w, "could not create bucket", reqInfo, err) h.logAndSendError(w, "could not create bucket", reqInfo, err)
return return
@ -762,6 +747,27 @@ func (h *handler) CreateBucketHandler(w http.ResponseWriter, r *http.Request) {
api.WriteSuccessResponseHeadersOnly(w) api.WriteSuccessResponseHeadersOnly(w)
} }
func (h handler) setPolicy(prm *layer.CreateBucketParams, locationConstraint string, userPolicies []*accessbox.ContainerPolicy) {
prm.Policy = h.cfg.Policy.Default
if locationConstraint == "" {
return
}
if policy, ok := h.cfg.Policy.RegionMap[locationConstraint]; ok {
prm.Policy = policy
prm.LocationConstraint = locationConstraint
}
for _, placementPolicy := range userPolicies {
if placementPolicy.LocationConstraint == locationConstraint {
prm.Policy = placementPolicy.Policy
prm.LocationConstraint = locationConstraint
return
}
}
}
func isLockEnabled(header http.Header) bool { func isLockEnabled(header http.Header) bool {
lockEnabledStr := header.Get(api.AmzBucketObjectLockEnabled) lockEnabledStr := header.Get(api.AmzBucketObjectLockEnabled)
lockEnabled, _ := strconv.ParseBool(lockEnabledStr) lockEnabled, _ := strconv.ParseBool(lockEnabledStr)

View file

@ -4,6 +4,7 @@ import (
"context" "context"
"crypto/tls" "crypto/tls"
"encoding/hex" "encoding/hex"
"encoding/json"
"errors" "errors"
"fmt" "fmt"
"net" "net"
@ -27,6 +28,7 @@ import (
"github.com/nspcc-dev/neofs-s3-gw/internal/neofs" "github.com/nspcc-dev/neofs-s3-gw/internal/neofs"
"github.com/nspcc-dev/neofs-s3-gw/internal/version" "github.com/nspcc-dev/neofs-s3-gw/internal/version"
"github.com/nspcc-dev/neofs-s3-gw/internal/wallet" "github.com/nspcc-dev/neofs-s3-gw/internal/wallet"
"github.com/nspcc-dev/neofs-sdk-go/netmap"
"github.com/nspcc-dev/neofs-sdk-go/pool" "github.com/nspcc-dev/neofs-sdk-go/pool"
"github.com/spf13/viper" "github.com/spf13/viper"
"go.uber.org/zap" "go.uber.org/zap"
@ -596,41 +598,83 @@ func getAccessBoxCacheConfig(v *viper.Viper, l *zap.Logger) *cache.Config {
} }
func getHandlerOptions(v *viper.Viper, l *zap.Logger) *handler.Config { func getHandlerOptions(v *viper.Viper, l *zap.Logger) *handler.Config {
var ( cfg := &handler.Config{
cfg handler.Config Policy: handler.PlacementPolicy{
err error RegionMap: make(map[string]netmap.PlacementPolicy),
policyStr = handler.DefaultPolicy },
defaultMaxAge = handler.DefaultMaxAge DefaultMaxAge: handler.DefaultMaxAge,
setCopiesNumber = handler.DefaultCopiesNumber NotificatorEnabled: v.GetBool(cfgEnableNATS),
) TLSEnabled: v.IsSet(cfgTLSKeyFile) && v.IsSet(cfgTLSCertFile),
CopiesNumber: handler.DefaultCopiesNumber,
if v.IsSet(cfgDefaultPolicy) {
policyStr = v.GetString(cfgDefaultPolicy)
} }
if err = cfg.DefaultPolicy.DecodeString(policyStr); err != nil { defaultPolicyStr := handler.DefaultPolicy
l.Fatal("couldn't parse container default policy", if v.IsSet(cfgPolicyDefault) {
zap.Error(err)) defaultPolicyStr = v.GetString(cfgPolicyDefault)
}
if err := cfg.Policy.Default.DecodeString(defaultPolicyStr); err != nil {
l.Fatal("couldn't parse container default policy", zap.Error(err))
}
regionPolicyMap, err := parseRegionMap(v)
if err != nil {
l.Fatal("couldn't parse region mapping policy file", zap.Error(err))
}
for region, policy := range regionPolicyMap {
var pp netmap.PlacementPolicy
if err = pp.DecodeString(policy); err == nil {
cfg.Policy.RegionMap[region] = pp
continue
}
if err = pp.UnmarshalJSON([]byte(policy)); err == nil {
cfg.Policy.RegionMap[region] = pp
continue
}
l.Fatal("couldn't parse region mapping policy", zap.String("name", region), zap.Error(err))
} }
if v.IsSet(cfgDefaultMaxAge) { if v.IsSet(cfgDefaultMaxAge) {
defaultMaxAge = v.GetInt(cfgDefaultMaxAge) defaultMaxAge := v.GetInt(cfgDefaultMaxAge)
if defaultMaxAge <= 0 && defaultMaxAge != -1 { if defaultMaxAge <= 0 && defaultMaxAge != -1 {
l.Fatal("invalid defaultMaxAge", l.Fatal("invalid defaultMaxAge",
zap.String("parameter", cfgDefaultMaxAge), zap.String("parameter", cfgDefaultMaxAge),
zap.String("value in config", strconv.Itoa(defaultMaxAge))) zap.String("value in config", strconv.Itoa(defaultMaxAge)))
} }
cfg.DefaultMaxAge = defaultMaxAge
} }
if val := v.GetUint32(cfgSetCopiesNumber); val > 0 { if val := v.GetUint32(cfgSetCopiesNumber); val > 0 {
setCopiesNumber = val cfg.CopiesNumber = val
} }
cfg.DefaultMaxAge = defaultMaxAge return cfg
cfg.NotificatorEnabled = v.GetBool(cfgEnableNATS) }
cfg.TLSEnabled = v.IsSet(cfgTLSKeyFile) && v.IsSet(cfgTLSCertFile)
cfg.CopiesNumber = setCopiesNumber func parseRegionMap(v *viper.Viper) (map[string]string, error) {
regionMap := make(map[string]string)
return &cfg
filePath := v.GetString(cfgPolicyRegionMapFile)
if filePath == "" {
return regionMap, nil
}
data, err := os.ReadFile(filePath)
if err != nil {
return nil, fmt.Errorf("coudln't read file '%s'", filePath)
}
if err = json.Unmarshal(data, &regionMap); err != nil {
return nil, fmt.Errorf("unmarshal policies: %w", err)
}
if _, ok := regionMap[api.DefaultLocationConstraint]; ok {
return nil, fmt.Errorf("config overrides %s location constraint", api.DefaultLocationConstraint)
}
return regionMap, nil
} }

View file

@ -76,7 +76,8 @@ const ( // Settings.
cfgNATSRootCAFiles = "nats.root_ca" cfgNATSRootCAFiles = "nats.root_ca"
// Policy. // Policy.
cfgDefaultPolicy = "default_policy" cfgPolicyDefault = "placement_policy.default"
cfgPolicyRegionMapFile = "placement_policy.region_mapping"
// CORS. // CORS.
cfgDefaultMaxAge = "cors.default_max_age" cfgDefaultMaxAge = "cors.default_max_age"