forked from TrueCloudLab/frostfs-s3-gw
[#285] Add resolving order
Signed-off-by: Denis Kirillov <denis@nspcc.ru>
This commit is contained in:
parent
097f745d3e
commit
8872b6f196
4 changed files with 197 additions and 42 deletions
|
@ -10,13 +10,12 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
apistatus "github.com/nspcc-dev/neofs-sdk-go/client/status"
|
|
||||||
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
||||||
"github.com/nspcc-dev/neofs-s3-gw/api"
|
"github.com/nspcc-dev/neofs-s3-gw/api"
|
||||||
"github.com/nspcc-dev/neofs-s3-gw/api/cache"
|
"github.com/nspcc-dev/neofs-s3-gw/api/cache"
|
||||||
"github.com/nspcc-dev/neofs-s3-gw/api/data"
|
"github.com/nspcc-dev/neofs-s3-gw/api/data"
|
||||||
"github.com/nspcc-dev/neofs-s3-gw/api/errors"
|
"github.com/nspcc-dev/neofs-s3-gw/api/errors"
|
||||||
|
"github.com/nspcc-dev/neofs-s3-gw/api/resolver"
|
||||||
"github.com/nspcc-dev/neofs-s3-gw/authmate"
|
"github.com/nspcc-dev/neofs-s3-gw/authmate"
|
||||||
"github.com/nspcc-dev/neofs-s3-gw/creds/accessbox"
|
"github.com/nspcc-dev/neofs-s3-gw/creds/accessbox"
|
||||||
"github.com/nspcc-dev/neofs-sdk-go/client"
|
"github.com/nspcc-dev/neofs-sdk-go/client"
|
||||||
|
@ -26,7 +25,6 @@ import (
|
||||||
"github.com/nspcc-dev/neofs-sdk-go/object"
|
"github.com/nspcc-dev/neofs-sdk-go/object"
|
||||||
"github.com/nspcc-dev/neofs-sdk-go/owner"
|
"github.com/nspcc-dev/neofs-sdk-go/owner"
|
||||||
"github.com/nspcc-dev/neofs-sdk-go/pool"
|
"github.com/nspcc-dev/neofs-sdk-go/pool"
|
||||||
"github.com/nspcc-dev/neofs-sdk-go/resolver"
|
|
||||||
"github.com/nspcc-dev/neofs-sdk-go/session"
|
"github.com/nspcc-dev/neofs-sdk-go/session"
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
)
|
)
|
||||||
|
@ -36,6 +34,7 @@ type (
|
||||||
pool pool.Pool
|
pool pool.Pool
|
||||||
log *zap.Logger
|
log *zap.Logger
|
||||||
anonKey AnonymousKey
|
anonKey AnonymousKey
|
||||||
|
resolver *resolver.BucketResolver
|
||||||
listsCache *cache.ObjectsListCache
|
listsCache *cache.ObjectsListCache
|
||||||
objCache *cache.ObjectsCache
|
objCache *cache.ObjectsCache
|
||||||
namesCache *cache.ObjectsNameCache
|
namesCache *cache.ObjectsNameCache
|
||||||
|
@ -47,6 +46,7 @@ type (
|
||||||
ChainAddress string
|
ChainAddress string
|
||||||
Caches *CachesConfig
|
Caches *CachesConfig
|
||||||
AnonKey AnonymousKey
|
AnonKey AnonymousKey
|
||||||
|
Resolver *resolver.BucketResolver
|
||||||
}
|
}
|
||||||
|
|
||||||
// AnonymousKey contains data for anonymous requests.
|
// AnonymousKey contains data for anonymous requests.
|
||||||
|
@ -237,9 +237,8 @@ type (
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
tagPrefix = "S3-Tag-"
|
tagPrefix = "S3-Tag-"
|
||||||
tagEmptyMark = "\\"
|
tagEmptyMark = "\\"
|
||||||
networkSystemDNSParam = "SystemDNS"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func (t *VersionedObject) String() string {
|
func (t *VersionedObject) String() string {
|
||||||
|
@ -264,6 +263,7 @@ func NewLayer(log *zap.Logger, conns pool.Pool, config *Config) Client {
|
||||||
pool: conns,
|
pool: conns,
|
||||||
log: log,
|
log: log,
|
||||||
anonKey: config.AnonKey,
|
anonKey: config.AnonKey,
|
||||||
|
resolver: config.Resolver,
|
||||||
listsCache: cache.NewObjectsListCache(config.Caches.ObjectsList),
|
listsCache: cache.NewObjectsListCache(config.Caches.ObjectsList),
|
||||||
objCache: cache.New(config.Caches.Objects),
|
objCache: cache.New(config.Caches.Objects),
|
||||||
namesCache: cache.NewObjectsNameCache(config.Caches.Names),
|
namesCache: cache.NewObjectsNameCache(config.Caches.Names),
|
||||||
|
@ -651,42 +651,7 @@ func (n *layer) CreateBucket(ctx context.Context, p *CreateBucketParams) (*cid.I
|
||||||
func (n *layer) ResolveBucket(ctx context.Context, name string) (*cid.ID, error) {
|
func (n *layer) ResolveBucket(ctx context.Context, name string) (*cid.ID, error) {
|
||||||
cnrID := cid.New()
|
cnrID := cid.New()
|
||||||
if err := cnrID.Parse(name); err != nil {
|
if err := cnrID.Parse(name); err != nil {
|
||||||
conn, _, err := n.pool.Connection()
|
return n.resolver.Resolve(ctx, name)
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
networkInfoRes, err := conn.NetworkInfo(ctx)
|
|
||||||
if err == nil {
|
|
||||||
err = apistatus.ErrFromStatus(networkInfoRes.Status())
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
networkInfo := networkInfoRes.Info()
|
|
||||||
|
|
||||||
var domain string
|
|
||||||
networkInfo.NetworkConfig().IterateParameters(func(parameter *netmap.NetworkParameter) bool {
|
|
||||||
if string(parameter.Key()) == networkSystemDNSParam {
|
|
||||||
domain = string(parameter.Value())
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
})
|
|
||||||
|
|
||||||
if domain != "" {
|
|
||||||
domain = name + "." + domain
|
|
||||||
if cnrID, err = resolver.ResolveContainerDomainName(domain); err == nil {
|
|
||||||
return cnrID, nil
|
|
||||||
}
|
|
||||||
n.log.Debug("trying fallback to direct nns since couldn't resolve system dns record",
|
|
||||||
zap.String("domain", domain), zap.Error(err))
|
|
||||||
}
|
|
||||||
|
|
||||||
// todo add fallback to use nns contract directly
|
|
||||||
|
|
||||||
return nil, fmt.Errorf("couldn't resolve container name '%s': not found", name)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return cnrID, nil
|
return cnrID, nil
|
||||||
|
|
153
api/resolver/resolver.go
Normal file
153
api/resolver/resolver.go
Normal file
|
@ -0,0 +1,153 @@
|
||||||
|
package resolver
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/rpc/client"
|
||||||
|
apistatus "github.com/nspcc-dev/neofs-sdk-go/client/status"
|
||||||
|
cid "github.com/nspcc-dev/neofs-sdk-go/container/id"
|
||||||
|
"github.com/nspcc-dev/neofs-sdk-go/netmap"
|
||||||
|
"github.com/nspcc-dev/neofs-sdk-go/pool"
|
||||||
|
"github.com/nspcc-dev/neofs-sdk-go/resolver"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
NNSResolver = "nns"
|
||||||
|
DNSResolver = "dns"
|
||||||
|
|
||||||
|
networkSystemDNSParam = "SystemDNS"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Config struct {
|
||||||
|
Pool pool.Pool
|
||||||
|
RPC *client.Client
|
||||||
|
}
|
||||||
|
|
||||||
|
type BucketResolver struct {
|
||||||
|
Name string
|
||||||
|
resolve func(context.Context, string) (*cid.ID, error)
|
||||||
|
|
||||||
|
next *BucketResolver
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *BucketResolver) Resolve(ctx context.Context, name string) (*cid.ID, error) {
|
||||||
|
cnrID, err := r.resolve(ctx, name)
|
||||||
|
if err != nil {
|
||||||
|
if r.next != nil {
|
||||||
|
return r.next.Resolve(ctx, name)
|
||||||
|
}
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return cnrID, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewResolver(order []string, cfg *Config) (*BucketResolver, error) {
|
||||||
|
if len(order) == 0 {
|
||||||
|
return nil, fmt.Errorf("resolving order must not be empty")
|
||||||
|
}
|
||||||
|
|
||||||
|
bucketResolver, err := newResolver(order[len(order)-1], cfg, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := len(order) - 2; i >= 0; i-- {
|
||||||
|
resolverName := order[i]
|
||||||
|
next := bucketResolver
|
||||||
|
|
||||||
|
bucketResolver, err = newResolver(resolverName, cfg, next)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return bucketResolver, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func newResolver(name string, cfg *Config, next *BucketResolver) (*BucketResolver, error) {
|
||||||
|
switch name {
|
||||||
|
case DNSResolver:
|
||||||
|
return NewDNSResolver(cfg.Pool, next)
|
||||||
|
case NNSResolver:
|
||||||
|
return NewNNSResolver(cfg.RPC, next)
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("unknown resolver: %s", name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewDNSResolver(p pool.Pool, next *BucketResolver) (*BucketResolver, error) {
|
||||||
|
if p == nil {
|
||||||
|
return nil, fmt.Errorf("pool must not be nil for DNS resolver")
|
||||||
|
}
|
||||||
|
|
||||||
|
resolveFunc := func(ctx context.Context, name string) (*cid.ID, error) {
|
||||||
|
conn, _, err := p.Connection()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
networkInfoRes, err := conn.NetworkInfo(ctx)
|
||||||
|
if err == nil {
|
||||||
|
err = apistatus.ErrFromStatus(networkInfoRes.Status())
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
networkInfo := networkInfoRes.Info()
|
||||||
|
|
||||||
|
var domain string
|
||||||
|
networkInfo.NetworkConfig().IterateParameters(func(parameter *netmap.NetworkParameter) bool {
|
||||||
|
if string(parameter.Key()) == networkSystemDNSParam {
|
||||||
|
domain = string(parameter.Value())
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
})
|
||||||
|
|
||||||
|
if domain == "" {
|
||||||
|
return nil, fmt.Errorf("couldn't resolve container '%s': not found", name)
|
||||||
|
}
|
||||||
|
|
||||||
|
domain = name + "." + domain
|
||||||
|
cnrID, err := resolver.ResolveContainerDomainName(domain)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("couldn't resolve container '%s' as '%s': %w", name, domain, err)
|
||||||
|
}
|
||||||
|
return cnrID, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return &BucketResolver{
|
||||||
|
Name: DNSResolver,
|
||||||
|
|
||||||
|
resolve: resolveFunc,
|
||||||
|
next: next,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewNNSResolver(rpc *client.Client, next *BucketResolver) (*BucketResolver, error) {
|
||||||
|
if rpc == nil {
|
||||||
|
return nil, fmt.Errorf("rpc client must not be nil for NNS resolver")
|
||||||
|
}
|
||||||
|
|
||||||
|
nnsRPCResolver, err := resolver.NewNNSResolver(rpc)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
resolveFunc := func(_ context.Context, name string) (*cid.ID, error) {
|
||||||
|
cnrID, err := nnsRPCResolver.ResolveContainerName(name)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("couldn't resolve container '%s': %w", name, err)
|
||||||
|
}
|
||||||
|
return cnrID, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return &BucketResolver{
|
||||||
|
Name: NNSResolver,
|
||||||
|
|
||||||
|
resolve: resolveFunc,
|
||||||
|
next: next,
|
||||||
|
}, nil
|
||||||
|
}
|
|
@ -10,11 +10,13 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/rpc/client"
|
||||||
"github.com/nspcc-dev/neofs-s3-gw/api"
|
"github.com/nspcc-dev/neofs-s3-gw/api"
|
||||||
"github.com/nspcc-dev/neofs-s3-gw/api/auth"
|
"github.com/nspcc-dev/neofs-s3-gw/api/auth"
|
||||||
"github.com/nspcc-dev/neofs-s3-gw/api/cache"
|
"github.com/nspcc-dev/neofs-s3-gw/api/cache"
|
||||||
"github.com/nspcc-dev/neofs-s3-gw/api/handler"
|
"github.com/nspcc-dev/neofs-s3-gw/api/handler"
|
||||||
"github.com/nspcc-dev/neofs-s3-gw/api/layer"
|
"github.com/nspcc-dev/neofs-s3-gw/api/layer"
|
||||||
|
"github.com/nspcc-dev/neofs-s3-gw/api/resolver"
|
||||||
"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/policy"
|
"github.com/nspcc-dev/neofs-sdk-go/policy"
|
||||||
|
@ -119,11 +121,32 @@ func newApp(ctx context.Context, l *zap.Logger, v *viper.Viper) *App {
|
||||||
l.Fatal("couldn't generate random key", zap.Error(err))
|
l.Fatal("couldn't generate random key", zap.Error(err))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
resolveCfg := &resolver.Config{
|
||||||
|
Pool: conns,
|
||||||
|
}
|
||||||
|
|
||||||
|
if rpcEndpoint := v.GetString(cfgRPCEndpoint); rpcEndpoint != "" {
|
||||||
|
rpc, err := client.New(ctx, rpcEndpoint, client.Options{})
|
||||||
|
if err != nil {
|
||||||
|
l.Fatal("couldn't create rpc client", zap.String("endpoint", rpcEndpoint), zap.Error(err))
|
||||||
|
} else if err = rpc.Init(); err != nil {
|
||||||
|
l.Fatal("couldn't init rpc client", zap.String("endpoint", rpcEndpoint), zap.Error(err))
|
||||||
|
}
|
||||||
|
resolveCfg.RPC = rpc
|
||||||
|
}
|
||||||
|
|
||||||
|
order := v.GetStringSlice(cfgResolveOrder)
|
||||||
|
bucketResolver, err := resolver.NewResolver(order, resolveCfg)
|
||||||
|
if err != nil {
|
||||||
|
l.Fatal("failed to form resolver", zap.Error(err))
|
||||||
|
}
|
||||||
|
|
||||||
layerCfg := &layer.Config{
|
layerCfg := &layer.Config{
|
||||||
Caches: getCacheOptions(v, l),
|
Caches: getCacheOptions(v, l),
|
||||||
AnonKey: layer.AnonymousKey{
|
AnonKey: layer.AnonymousKey{
|
||||||
Key: randomKey,
|
Key: randomKey,
|
||||||
},
|
},
|
||||||
|
Resolver: bucketResolver,
|
||||||
}
|
}
|
||||||
|
|
||||||
// prepare object layer
|
// prepare object layer
|
||||||
|
|
|
@ -8,6 +8,7 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/nspcc-dev/neofs-s3-gw/api/resolver"
|
||||||
"github.com/nspcc-dev/neofs-s3-gw/internal/version"
|
"github.com/nspcc-dev/neofs-s3-gw/internal/version"
|
||||||
"github.com/nspcc-dev/neofs-sdk-go/pool"
|
"github.com/nspcc-dev/neofs-sdk-go/pool"
|
||||||
"github.com/spf13/pflag"
|
"github.com/spf13/pflag"
|
||||||
|
@ -90,6 +91,12 @@ const ( // Settings.
|
||||||
// Peers.
|
// Peers.
|
||||||
cfgPeers = "peers"
|
cfgPeers = "peers"
|
||||||
|
|
||||||
|
// NeoGo.
|
||||||
|
cfgRPCEndpoint = "rpc-endpoint"
|
||||||
|
|
||||||
|
// Resolving.
|
||||||
|
cfgResolveOrder = "resolve-order"
|
||||||
|
|
||||||
// Application.
|
// Application.
|
||||||
cfgApplicationName = "app.name"
|
cfgApplicationName = "app.name"
|
||||||
cfgApplicationVersion = "app.version"
|
cfgApplicationVersion = "app.version"
|
||||||
|
@ -203,6 +210,9 @@ func newSettings() *viper.Viper {
|
||||||
|
|
||||||
peers := flags.StringArrayP(cfgPeers, "p", nil, "set NeoFS nodes")
|
peers := flags.StringArrayP(cfgPeers, "p", nil, "set NeoFS nodes")
|
||||||
|
|
||||||
|
flags.StringP(cfgRPCEndpoint, "r", "", "set RPC endpoint")
|
||||||
|
resolveMethods := flags.StringSlice(cfgResolveOrder, []string{resolver.DNSResolver}, "set bucket name resolve order")
|
||||||
|
|
||||||
domains := flags.StringArrayP(cfgListenDomains, "d", nil, "set domains to be listened")
|
domains := flags.StringArrayP(cfgListenDomains, "d", nil, "set domains to be listened")
|
||||||
|
|
||||||
// set prefers:
|
// set prefers:
|
||||||
|
@ -228,6 +238,10 @@ func newSettings() *viper.Viper {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if resolveMethods != nil {
|
||||||
|
v.SetDefault(cfgResolveOrder, *resolveMethods)
|
||||||
|
}
|
||||||
|
|
||||||
if peers != nil && len(*peers) > 0 {
|
if peers != nil && len(*peers) > 0 {
|
||||||
for i := range *peers {
|
for i := range *peers {
|
||||||
v.SetDefault(cfgPeers+"."+strconv.Itoa(i)+".address", (*peers)[i])
|
v.SetDefault(cfgPeers+"."+strconv.Itoa(i)+".address", (*peers)[i])
|
||||||
|
|
Loading…
Reference in a new issue