Skip to content

Commit

Permalink
ir: Check that just bootstrapped SN is available
Browse files Browse the repository at this point in the history
Closes #2475.

Signed-off-by: Pavel Karpy <carpawell@nspcc.ru>
  • Loading branch information
carpawell committed Sep 7, 2023
1 parent 9224e9f commit 1877325
Show file tree
Hide file tree
Showing 3 changed files with 154 additions and 0 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
2 changes: 2 additions & 0 deletions pkg/innerring/innerring.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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,
})
Expand Down
Original file line number Diff line number Diff line change
@@ -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
}

0 comments on commit 1877325

Please sign in to comment.