forked from TrueCloudLab/frostfs-s3-gw
[#568] Add configuration for region to policy map
Signed-off-by: Denis Kirillov <denis@nspcc.ru>
This commit is contained in:
parent
3212805955
commit
d47840f137
5 changed files with 101 additions and 44 deletions
|
@ -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 (
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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, ®ionMap); 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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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"
|
||||||
|
|
Loading…
Reference in a new issue