frostfs-s3-gw/api/resolver/resolver.go

154 lines
3.4 KiB
Go
Raw Normal View History

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
}