From 1877325da4519dc70e6bcb93cf1bc409f7d451f6 Mon Sep 17 00:00:00 2001 From: Pavel Karpy Date: Fri, 1 Sep 2023 19:40:55 +0300 Subject: [PATCH] ir: Check that just bootstrapped SN is available Closes #2475. Signed-off-by: Pavel Karpy --- CHANGELOG.md | 1 + pkg/innerring/innerring.go | 2 + .../nodevalidation/availability/validator.go | 151 ++++++++++++++++++ 3 files changed, 154 insertions(+) create mode 100644 pkg/innerring/processors/netmap/nodevalidation/availability/validator.go diff --git a/CHANGELOG.md b/CHANGELOG.md index f1d5c0ad90..b41f0bf594 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,6 +26,7 @@ minor release, the component will be purged, so be prepared (see `Updating` sect - `neofs-cli object nodes` command to get SNs for an object (#2512) - Fetching container estimations via iterators to prevent NeoVM stack overflow (#2173) - `neofs-adm morph netmap-candidates` CLI command (#1889) +- SN network validation (is available by its announced addresses) on bootstrap by the IR (#2475) ### Fixed - `neo-go` RPC connection loss handling (#1337) diff --git a/pkg/innerring/innerring.go b/pkg/innerring/innerring.go index 6195cfd3ce..d27c0b25c6 100644 --- a/pkg/innerring/innerring.go +++ b/pkg/innerring/innerring.go @@ -23,6 +23,7 @@ import ( "github.com/nspcc-dev/neofs-node/pkg/innerring/processors/neofs" "github.com/nspcc-dev/neofs-node/pkg/innerring/processors/netmap" nodevalidator "github.com/nspcc-dev/neofs-node/pkg/innerring/processors/netmap/nodevalidation" + availabilityvalidator "github.com/nspcc-dev/neofs-node/pkg/innerring/processors/netmap/nodevalidation/availability" addrvalidator "github.com/nspcc-dev/neofs-node/pkg/innerring/processors/netmap/nodevalidation/maddress" statevalidation "github.com/nspcc-dev/neofs-node/pkg/innerring/processors/netmap/nodevalidation/state" "github.com/nspcc-dev/neofs-node/pkg/innerring/processors/reputation" @@ -754,6 +755,7 @@ func New(ctx context.Context, log *zap.Logger, cfg *viper.Viper, errChan chan<- &netMapCandidateStateValidator, addrvalidator.New(), locodeValidator, + availabilityvalidator.New(), ), NodeStateSettings: netSettings, }) diff --git a/pkg/innerring/processors/netmap/nodevalidation/availability/validator.go b/pkg/innerring/processors/netmap/nodevalidation/availability/validator.go new file mode 100644 index 0000000000..e75e0ca391 --- /dev/null +++ b/pkg/innerring/processors/netmap/nodevalidation/availability/validator.go @@ -0,0 +1,151 @@ +package availability + +import ( + "bytes" + "context" + "fmt" + "time" + + "github.com/nspcc-dev/neofs-sdk-go/client" + "github.com/nspcc-dev/neofs-sdk-go/netmap" +) + +// Validator is a utility that verifies node's +// accessibility according to its announced addresses. +// +// For correct operation, the Validator must be created +// using the constructor (New). After successful creation, +// the Validator is immediately ready to work through API. +type Validator struct{} + +func (v Validator) VerifyAndUpdate(nodeInfo *netmap.NodeInfo) error { + var results []*client.ResEndpointInfo + var err error + + nodeInfo.IterateNetworkEndpoints(func(s string) bool { + var res *client.ResEndpointInfo + var c *client.Client + + c, err = createSDKClient(s) + if err != nil { + err = fmt.Errorf("'%s': client creation: %w", s, err) + return true + } + defer func() { + _ = c.Close() + }() + + timeoutContext, cancel := context.WithTimeout(context.Background(), pingTimeout) + defer cancel() + + res, err = c.EndpointInfo(timeoutContext, client.PrmEndpointInfo{}) + if err != nil { + err = fmt.Errorf("'%s': could not ping node with `EndpointInfo`: %w", s, err) + return true + } + + results = append(results, res) + return false + }) + if err != nil { + return err + } + + for _, res := range results { + err = compareNodeInfos(*nodeInfo, res.NodeInfo()) + if err != nil { + return fmt.Errorf("`EndpointInfo` RPC call result differs: %w", err) + } + } + + return nil +} + +// New creates a new instance of the Validator. +// +// Panics if at least one value of the parameters is invalid. +// +// The created Validator does not require additional +// initialization and is completely ready for work. +func New() *Validator { + return &Validator{} +} + +func compareNodeInfos(niExp, niGot netmap.NodeInfo) error { + // a node can be in a STATE_1 (and respond with it) + // but the request can mean a state transfer to a + // STATE_2, so make both node infos in the same state, + // e.g. ONLINE + niGot.SetOnline() + niExp.SetOnline() + if exp, got := niExp.Marshal(), niGot.Marshal(); bytes.Equal(exp, got) { + return nil + } + + var err error + + if exp, got := niExp.Hash(), niGot.Hash(); exp != got { + return fmt.Errorf("hash: got %d, expect %d", got, exp) + } + + if exp, got := niExp.NumberOfAttributes(), niGot.NumberOfAttributes(); exp != got { + return fmt.Errorf("attr number: got %d, expect %d", got, exp) + } + + niExp.IterateAttributes(func(key, value string) { + vGot := niGot.Attribute(key) + if vGot != value { + err = fmt.Errorf("non-equal %s attribute: got %s, expect %s", key, vGot, value) + } + }) + if err != nil { + return err + } + + if exp, got := niExp.NumberOfNetworkEndpoints(), niGot.NumberOfNetworkEndpoints(); exp != got { + return fmt.Errorf("address number: got %d, expect %d", got, exp) + } + + expAddrM := make(map[string]struct{}, niExp.NumberOfAttributes()) + niExp.IterateNetworkEndpoints(func(s string) bool { + expAddrM[s] = struct{}{} + return false + }) + + niGot.IterateNetworkEndpoints(func(s string) bool { + if _, ok := expAddrM[s]; !ok { + err = fmt.Errorf("got unexpected address: %s", s) + return true + } + + return false + }) + if err != nil { + return err + } + + return nil +} + +const pingTimeout = 15 * time.Second + +func createSDKClient(e string) (*client.Client, error) { + var prmInit client.PrmInit + var prmDial client.PrmDial + + prmDial.SetTimeout(pingTimeout) + prmDial.SetStreamTimeout(pingTimeout) + prmDial.SetServerURI(e) + + c, err := client.New(prmInit) + if err != nil { + return nil, fmt.Errorf("can't create SDK client: %w", err) + } + + err = c.Dial(prmDial) + if err != nil { + return nil, fmt.Errorf("can't init SDK client: %w", err) + } + + return c, nil +}