[#82] netmap: Add functionality for working with subnets
* Implement `NodeInfo.EnterSubnet` method which adds node's subnet. * Implement `NodeInfo.IterateSubnets` which behaves similar to eponymous function from neofs-api-go/v2. * Implement `BelongsToSubnet` which checks if `NodeInfo` describes the node belonging to a specific subnet. Signed-off-by: Leonard Lyubich <leonard@nspcc.ru>
This commit is contained in:
parent
b8989e3abb
commit
e7ac7f339e
2 changed files with 170 additions and 0 deletions
68
netmap/subnet.go
Normal file
68
netmap/subnet.go
Normal file
|
@ -0,0 +1,68 @@
|
||||||
|
package netmap
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
|
||||||
|
"github.com/nspcc-dev/neofs-api-go/v2/netmap"
|
||||||
|
"github.com/nspcc-dev/neofs-api-go/v2/refs"
|
||||||
|
subnetid "github.com/nspcc-dev/neofs-sdk-go/subnet/id"
|
||||||
|
)
|
||||||
|
|
||||||
|
// EnterSubnet writes to NodeInfo the intention to enter the subnet. Must not be called on nil.
|
||||||
|
// Zero NodeInfo belongs to zero subnet.
|
||||||
|
func (i *NodeInfo) EnterSubnet(id subnetid.ID) {
|
||||||
|
var (
|
||||||
|
idv2 refs.SubnetID
|
||||||
|
info netmap.NodeSubnetInfo
|
||||||
|
)
|
||||||
|
|
||||||
|
id.WriteToV2(&idv2)
|
||||||
|
|
||||||
|
info.SetID(&idv2)
|
||||||
|
info.SetEntryFlag(true)
|
||||||
|
|
||||||
|
netmap.WriteSubnetInfo((*netmap.NodeInfo)(i), info)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ErrRemoveSubnet is returned when a node needs to leave the subnet.
|
||||||
|
var ErrRemoveSubnet = netmap.ErrRemoveSubnet
|
||||||
|
|
||||||
|
// IterateSubnets iterates over all subnets the node belongs to and passes the IDs to f.
|
||||||
|
// Must not be called on nil. Handler must not be nil.
|
||||||
|
//
|
||||||
|
// If f returns ErrRemoveSubnet, then removes subnet entry. Note that this leads to an instant mutation of NodeInfo.
|
||||||
|
// Breaks on any other non-nil error and returns it.
|
||||||
|
//
|
||||||
|
// Returns an error if subnet incorrectly enabled/disabled.
|
||||||
|
// Returns an error if the node is not included in any subnet by the end of the loop.
|
||||||
|
func (i *NodeInfo) IterateSubnets(f func(subnetid.ID) error) error {
|
||||||
|
var id subnetid.ID
|
||||||
|
|
||||||
|
return netmap.IterateSubnets((*netmap.NodeInfo)(i), func(idv2 refs.SubnetID) error {
|
||||||
|
id.FromV2(idv2)
|
||||||
|
|
||||||
|
err := f(id)
|
||||||
|
if errors.Is(err, ErrRemoveSubnet) {
|
||||||
|
return netmap.ErrRemoveSubnet
|
||||||
|
}
|
||||||
|
|
||||||
|
return err
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
var errAbortSubnetIter = errors.New("abort subnet iterator")
|
||||||
|
|
||||||
|
// BelongsToSubnet checks if node belongs to subnet by ID.
|
||||||
|
//
|
||||||
|
// Function is NPE-safe: nil NodeInfo always belongs to zero subnet only.
|
||||||
|
func BelongsToSubnet(node *NodeInfo, id subnetid.ID) bool {
|
||||||
|
err := node.IterateSubnets(func(id_ subnetid.ID) error {
|
||||||
|
if id.Equals(&id_) {
|
||||||
|
return errAbortSubnetIter
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
|
||||||
|
return errors.Is(err, errAbortSubnetIter)
|
||||||
|
}
|
102
netmap/subnet_test.go
Normal file
102
netmap/subnet_test.go
Normal file
|
@ -0,0 +1,102 @@
|
||||||
|
package netmap_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/nspcc-dev/neofs-sdk-go/netmap"
|
||||||
|
subnetid "github.com/nspcc-dev/neofs-sdk-go/subnet/id"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestNodeInfoSubnets(t *testing.T) {
|
||||||
|
t.Run("enter subnet", func(t *testing.T) {
|
||||||
|
var id subnetid.ID
|
||||||
|
|
||||||
|
id.SetNumber(13)
|
||||||
|
|
||||||
|
var node netmap.NodeInfo
|
||||||
|
|
||||||
|
node.EnterSubnet(id)
|
||||||
|
|
||||||
|
mIDs := make(map[string]struct{})
|
||||||
|
|
||||||
|
err := node.IterateSubnets(func(id subnetid.ID) error {
|
||||||
|
mIDs[id.String()] = struct{}{}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
_, ok := mIDs[id.String()]
|
||||||
|
require.True(t, ok)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("iterate with removal", func(t *testing.T) {
|
||||||
|
t.Run("not last", func(t *testing.T) {
|
||||||
|
var id, idrm subnetid.ID
|
||||||
|
|
||||||
|
id.SetNumber(13)
|
||||||
|
idrm.SetNumber(23)
|
||||||
|
|
||||||
|
var node netmap.NodeInfo
|
||||||
|
|
||||||
|
node.EnterSubnet(id)
|
||||||
|
node.EnterSubnet(idrm)
|
||||||
|
|
||||||
|
err := node.IterateSubnets(func(id subnetid.ID) error {
|
||||||
|
if subnetid.IsZero(id) || id.Equals(&idrm) {
|
||||||
|
return netmap.ErrRemoveSubnet
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
mIDs := make(map[string]struct{})
|
||||||
|
|
||||||
|
err = node.IterateSubnets(func(id subnetid.ID) error {
|
||||||
|
mIDs[id.String()] = struct{}{}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
var zeroID subnetid.ID
|
||||||
|
|
||||||
|
_, ok := mIDs[zeroID.String()]
|
||||||
|
require.False(t, ok)
|
||||||
|
|
||||||
|
_, ok = mIDs[idrm.String()]
|
||||||
|
require.False(t, ok)
|
||||||
|
|
||||||
|
_, ok = mIDs[id.String()]
|
||||||
|
require.True(t, ok)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("last", func(t *testing.T) {
|
||||||
|
var node netmap.NodeInfo
|
||||||
|
|
||||||
|
err := node.IterateSubnets(func(id subnetid.ID) error {
|
||||||
|
return netmap.ErrRemoveSubnet
|
||||||
|
})
|
||||||
|
|
||||||
|
require.Error(t, err)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBelongsToSubnet(t *testing.T) {
|
||||||
|
var id, idMiss, idZero subnetid.ID
|
||||||
|
|
||||||
|
id.SetNumber(13)
|
||||||
|
idMiss.SetNumber(23)
|
||||||
|
|
||||||
|
var node netmap.NodeInfo
|
||||||
|
|
||||||
|
node.EnterSubnet(id)
|
||||||
|
|
||||||
|
require.True(t, netmap.BelongsToSubnet(&node, idZero))
|
||||||
|
require.True(t, netmap.BelongsToSubnet(&node, id))
|
||||||
|
require.False(t, netmap.BelongsToSubnet(&node, idMiss))
|
||||||
|
}
|
Loading…
Reference in a new issue