[#285] Add resolving order

Signed-off-by: Denis Kirillov <denis@nspcc.ru>
This commit is contained in:
Denis Kirillov 2022-01-11 13:09:34 +03:00 committed by Alex Vanin
parent 097f745d3e
commit 8872b6f196
4 changed files with 197 additions and 42 deletions

View file

@ -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.
@ -239,7 +239,6 @@ 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
View 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
}

View file

@ -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

View file

@ -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])