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"
|
||||
"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.
|
||||
|
@ -239,7 +239,6 @@ type (
|
|||
const (
|
||||
tagPrefix = "S3-Tag-"
|
||||
tagEmptyMark = "\\"
|
||||
networkSystemDNSParam = "SystemDNS"
|
||||
)
|
||||
|
||||
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
|
||||
|
|
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"
|
||||
|
||||
"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
|
||||
|
|
|
@ -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])
|
||||
|
|
Loading…
Reference in a new issue