diff --git a/client/client.go b/client/client.go index 014b3d76..d4a3e3bd 100644 --- a/client/client.go +++ b/client/client.go @@ -5,6 +5,10 @@ import ( "crypto/ecdsa" "crypto/tls" "errors" + "fmt" + netmap "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/netmap/grpc" + "net/url" + "slices" "time" v2accounting "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/accounting" @@ -110,6 +114,22 @@ func (c *Client) Dial(ctx context.Context, prm PrmDial) error { return nil } +func (c *Client) NetMapDial(ctx context.Context, endpoint string) error { + u, err := url.Parse(endpoint) + if err != nil { + return err + } + if u.Scheme == "frostfs" { + nodes := c.prm.NetMap.GetNodes() + for _, node := range nodes { + if slices.Equal([]byte(u.Host), node.PublicKey) { + return c.Dial(ctx, PrmDial{Endpoint: node.Addresses[0]}) + } + } + } + return fmt.Errorf("dial failure: endpoint %s isn't valid", endpoint) +} + // sets underlying provider of frostFSAPIServer. The method is used for testing as an approach // to skip Dial stage and override FrostFS API server. MUST NOT be used outside test code. // In real applications wrapper over git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/rpc/client @@ -141,6 +161,8 @@ type PrmInit struct { ResponseInfoCallback func(ResponseMetaInfo) error + NetMap *netmap.Netmap + NetMagic uint64 } diff --git a/client/client_test.go b/client/client_test.go index d2e5274b..e09fc113 100644 --- a/client/client_test.go +++ b/client/client_test.go @@ -5,6 +5,8 @@ import ( "crypto/ecdsa" "crypto/elliptic" "crypto/rand" + "fmt" + netmap "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/netmap/grpc" "testing" apistatus "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client/status" @@ -65,3 +67,40 @@ func TestClient_DialContext(t *testing.T) { assert(ctx, context.DeadlineExceeded) } + +func TestClient_NetMapDialContext(t *testing.T) { + var prmInit PrmInit + var c Client + publicKey := "foo" + endpoint := "localhost:8080" + + prmInit.NetMap = &netmap.Netmap{ + Epoch: 0, + Nodes: []*netmap.NodeInfo{ + { + PublicKey: []byte(publicKey), + Addresses: []string{endpoint}, + }, + }, + } + + c.Init(prmInit) + + assert := func(ctx context.Context, errExpected error) { + // expect particular context error according to Dial docs + //require.ErrorIs(t, c.Dial(ctx, prm), errExpected) + require.ErrorIs(t, c.NetMapDial(ctx, fmt.Sprintf("frostfs://%s", publicKey)), errExpected) + } + + // create pre-abandoned context + ctx, cancel := context.WithCancel(context.Background()) + cancel() + + assert(ctx, context.Canceled) + + // create "pre-deadlined" context + ctx, cancel = context.WithTimeout(context.Background(), 0) + defer cancel() + + assert(ctx, context.DeadlineExceeded) +}