[#142] Support resolving container nicename

Signed-off-by: Denis Kirillov <denis@nspcc.ru>
This commit is contained in:
Denis Kirillov 2022-04-20 12:17:20 +03:00 committed by Kirillov Denis
parent 2b780c1772
commit a42606742a
14 changed files with 458 additions and 74 deletions

46
resolver/neofs.go Normal file
View file

@ -0,0 +1,46 @@
package resolver
import (
"context"
"errors"
"fmt"
"github.com/nspcc-dev/neofs-sdk-go/netmap"
"github.com/nspcc-dev/neofs-sdk-go/pool"
)
// NeoFSResolver represents virtual connection to the NeoFS network.
// It implements resolver.NeoFS.
type NeoFSResolver struct {
pool *pool.Pool
}
// NewNeoFSResolver creates new NeoFSResolver using provided pool.Pool.
func NewNeoFSResolver(p *pool.Pool) *NeoFSResolver {
return &NeoFSResolver{pool: p}
}
// SystemDNS implements resolver.NeoFS interface method.
func (x *NeoFSResolver) SystemDNS(ctx context.Context) (string, error) {
networkInfo, err := x.pool.NetworkInfo(ctx)
if err != nil {
return "", fmt.Errorf("read network info via client: %w", err)
}
var domain string
networkInfo.NetworkConfig().IterateParameters(func(parameter *netmap.NetworkParameter) bool {
if string(parameter.Key()) == "SystemDNS" {
domain = string(parameter.Value())
return true
}
return false
})
if domain == "" {
return "", errors.New("system DNS parameter not found or empty")
}
return domain, nil
}

140
resolver/resolver.go Normal file
View file

@ -0,0 +1,140 @@
package resolver
import (
"context"
"fmt"
cid "github.com/nspcc-dev/neofs-sdk-go/container/id"
"github.com/nspcc-dev/neofs-sdk-go/ns"
)
const (
NNSResolver = "nns"
DNSResolver = "dns"
)
// NeoFS represents virtual connection to the NeoFS network.
type NeoFS interface {
// SystemDNS reads system DNS network parameters of the NeoFS.
//
// Returns exactly on non-zero value. Returns any error encountered
// which prevented the parameter to be read.
SystemDNS(context.Context) (string, error)
}
type Config struct {
NeoFS NeoFS
RPCAddress string
}
type ContainerResolver struct {
Name string
resolve func(context.Context, string) (*cid.ID, error)
next *ContainerResolver
}
func (r *ContainerResolver) SetResolveFunc(fn func(context.Context, string) (*cid.ID, error)) {
r.resolve = fn
}
func (r *ContainerResolver) Resolve(ctx context.Context, name string) (*cid.ID, error) {
cnrID, err := r.resolve(ctx, name)
if err != nil {
if r.next != nil {
cnrID, inErr := r.next.Resolve(ctx, name)
if inErr != nil {
return nil, fmt.Errorf("%s; %w", err.Error(), inErr)
}
return cnrID, nil
}
return nil, err
}
return cnrID, nil
}
func NewResolver(order []string, cfg *Config) (*ContainerResolver, 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 *ContainerResolver) (*ContainerResolver, error) {
switch name {
case DNSResolver:
return NewDNSResolver(cfg.NeoFS, next)
case NNSResolver:
return NewNNSResolver(cfg.RPCAddress, next)
default:
return nil, fmt.Errorf("unknown resolver: %s", name)
}
}
func NewDNSResolver(neoFS NeoFS, next *ContainerResolver) (*ContainerResolver, error) {
if neoFS == nil {
return nil, fmt.Errorf("pool must not be nil for DNS resolver")
}
var dns ns.DNS
resolveFunc := func(ctx context.Context, name string) (*cid.ID, error) {
domain, err := neoFS.SystemDNS(ctx)
if err != nil {
return nil, fmt.Errorf("read system DNS parameter of the NeoFS: %w", err)
}
domain = name + "." + domain
cnrID, err := dns.ResolveContainerName(domain)
if err != nil {
return nil, fmt.Errorf("couldn't resolve container '%s' as '%s': %w", name, domain, err)
}
return &cnrID, nil
}
return &ContainerResolver{
Name: DNSResolver,
resolve: resolveFunc,
next: next,
}, nil
}
func NewNNSResolver(rpcAddress string, next *ContainerResolver) (*ContainerResolver, error) {
var nns ns.NNS
if err := nns.Dial(rpcAddress); err != nil {
return nil, fmt.Errorf("could not dial nns: %w", err)
}
resolveFunc := func(_ context.Context, name string) (*cid.ID, error) {
cnrID, err := nns.ResolveContainerName(name)
if err != nil {
return nil, fmt.Errorf("couldn't resolve container '%s': %w", name, err)
}
return &cnrID, nil
}
return &ContainerResolver{
Name: NNSResolver,
resolve: resolveFunc,
next: next,
}, nil
}