From 8872b6f196846549b647af23036495e35d1b4469 Mon Sep 17 00:00:00 2001 From: Denis Kirillov Date: Tue, 11 Jan 2022 13:09:34 +0300 Subject: [PATCH] [#285] Add resolving order Signed-off-by: Denis Kirillov --- api/layer/layer.go | 49 ++---------- api/resolver/resolver.go | 153 ++++++++++++++++++++++++++++++++++++++ cmd/s3-gw/app.go | 23 ++++++ cmd/s3-gw/app_settings.go | 14 ++++ 4 files changed, 197 insertions(+), 42 deletions(-) create mode 100644 api/resolver/resolver.go diff --git a/api/layer/layer.go b/api/layer/layer.go index f5e382e8c..5aa2ccc31 100644 --- a/api/layer/layer.go +++ b/api/layer/layer.go @@ -10,13 +10,12 @@ import ( "strings" "time" - apistatus "github.com/nspcc-dev/neofs-sdk-go/client/status" - "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/cache" "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/resolver" "github.com/nspcc-dev/neofs-s3-gw/authmate" "github.com/nspcc-dev/neofs-s3-gw/creds/accessbox" "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/owner" "github.com/nspcc-dev/neofs-sdk-go/pool" - "github.com/nspcc-dev/neofs-sdk-go/resolver" "github.com/nspcc-dev/neofs-sdk-go/session" "go.uber.org/zap" ) @@ -36,6 +34,7 @@ type ( pool pool.Pool log *zap.Logger anonKey AnonymousKey + resolver *resolver.BucketResolver listsCache *cache.ObjectsListCache objCache *cache.ObjectsCache namesCache *cache.ObjectsNameCache @@ -47,6 +46,7 @@ type ( ChainAddress string Caches *CachesConfig AnonKey AnonymousKey + Resolver *resolver.BucketResolver } // AnonymousKey contains data for anonymous requests. @@ -237,9 +237,8 @@ type ( ) const ( - tagPrefix = "S3-Tag-" - tagEmptyMark = "\\" - networkSystemDNSParam = "SystemDNS" + tagPrefix = "S3-Tag-" + tagEmptyMark = "\\" ) func (t *VersionedObject) String() string { @@ -264,6 +263,7 @@ func NewLayer(log *zap.Logger, conns pool.Pool, config *Config) Client { pool: conns, log: log, anonKey: config.AnonKey, + resolver: config.Resolver, listsCache: cache.NewObjectsListCache(config.Caches.ObjectsList), objCache: cache.New(config.Caches.Objects), 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) { cnrID := cid.New() if err := cnrID.Parse(name); err != nil { - conn, _, err := n.pool.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 != "" { - 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 n.resolver.Resolve(ctx, name) } return cnrID, nil diff --git a/api/resolver/resolver.go b/api/resolver/resolver.go new file mode 100644 index 000000000..ae6d107e9 --- /dev/null +++ b/api/resolver/resolver.go @@ -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 +} diff --git a/cmd/s3-gw/app.go b/cmd/s3-gw/app.go index bea589879..6feb5f506 100644 --- a/cmd/s3-gw/app.go +++ b/cmd/s3-gw/app.go @@ -10,11 +10,13 @@ import ( "time" "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/auth" "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/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/wallet" "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)) } + 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{ Caches: getCacheOptions(v, l), AnonKey: layer.AnonymousKey{ Key: randomKey, }, + Resolver: bucketResolver, } // prepare object layer diff --git a/cmd/s3-gw/app_settings.go b/cmd/s3-gw/app_settings.go index 2340babcf..816a86a61 100644 --- a/cmd/s3-gw/app_settings.go +++ b/cmd/s3-gw/app_settings.go @@ -8,6 +8,7 @@ import ( "strings" "time" + "github.com/nspcc-dev/neofs-s3-gw/api/resolver" "github.com/nspcc-dev/neofs-s3-gw/internal/version" "github.com/nspcc-dev/neofs-sdk-go/pool" "github.com/spf13/pflag" @@ -90,6 +91,12 @@ const ( // Settings. // Peers. cfgPeers = "peers" + // NeoGo. + cfgRPCEndpoint = "rpc-endpoint" + + // Resolving. + cfgResolveOrder = "resolve-order" + // Application. cfgApplicationName = "app.name" cfgApplicationVersion = "app.version" @@ -203,6 +210,9 @@ func newSettings() *viper.Viper { 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") // set prefers: @@ -228,6 +238,10 @@ func newSettings() *viper.Viper { panic(err) } + if resolveMethods != nil { + v.SetDefault(cfgResolveOrder, *resolveMethods) + } + if peers != nil && len(*peers) > 0 { for i := range *peers { v.SetDefault(cfgPeers+"."+strconv.Itoa(i)+".address", (*peers)[i])