[#356] netmap: Return missing subnets error from IterateSubnets

Each NeoFS storage node should be present in at least one subnet.

Make `netmap.IterateSubnets` function to return an error if the node by the
end of the loop does not belong to any subnet.

Signed-off-by: Leonard Lyubich <leonard@nspcc.ru>
This commit is contained in:
Leonard Lyubich 2021-11-18 21:26:29 +03:00 committed by LeL
parent 348f9498bd
commit 051b103df3
2 changed files with 62 additions and 6 deletions

View file

@ -113,12 +113,16 @@ func WriteSubnetInfo(node *NodeInfo, info NodeSubnetInfo) {
// ErrRemoveSubnet is returned when a node needs to leave the subnet. // ErrRemoveSubnet is returned when a node needs to leave the subnet.
var ErrRemoveSubnet = errors.New("remove subnet") var ErrRemoveSubnet = errors.New("remove subnet")
var errNoSubnets = errors.New("no subnets")
// IterateSubnets iterates over all subnets the node belongs to and passes the IDs to f. // IterateSubnets iterates over all subnets the node belongs to and passes the IDs to f.
// Handler must not be nil. // Handler must not be nil.
// //
// If f returns ErrRemoveSubnet, then removes subnet entry. Breaks on any other non-nil error and returns it. // 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 any subnet attribute has wrong format. // Returns an error if any subnet attribute has wrong format.
// Returns an error if the node is not included in any subnet by the end of the loop.
func IterateSubnets(node *NodeInfo, f func(refs.SubnetID) error) error { func IterateSubnets(node *NodeInfo, f func(refs.SubnetID) error) error {
attrs := node.GetAttributes() attrs := node.GetAttributes()
@ -126,6 +130,7 @@ func IterateSubnets(node *NodeInfo, f func(refs.SubnetID) error) error {
err error err error
id refs.SubnetID id refs.SubnetID
metZero bool // if zero subnet's attribute was met in for-loop metZero bool // if zero subnet's attribute was met in for-loop
entries uint
) )
for i := 0; i < len(attrs); i++ { // range must not be used because of attrs mutation in body for i := 0; i < len(attrs); i++ { // range must not be used because of attrs mutation in body
@ -168,6 +173,7 @@ func IterateSubnets(node *NodeInfo, f func(refs.SubnetID) error) error {
if !isRemoveErr { if !isRemoveErr {
// no handler's error and non-zero subnet // no handler's error and non-zero subnet
entries++
continue continue
} else if metZero { } else if metZero {
// removal error and zero subnet. // removal error and zero subnet.
@ -183,7 +189,10 @@ func IterateSubnets(node *NodeInfo, f func(refs.SubnetID) error) error {
// we can set False or remove attribute, latter is more memory/network efficient. // we can set False or remove attribute, latter is more memory/network efficient.
attrs = append(attrs[:i], attrs[i+1:]...) attrs = append(attrs[:i], attrs[i+1:]...)
i-- i--
continue
} }
entries++
} }
if !metZero { if !metZero {
@ -199,9 +208,15 @@ func IterateSubnets(node *NodeInfo, f func(refs.SubnetID) error) error {
attr.SetValue(attrSubnetValExit) attr.SetValue(attrSubnetValExit)
attrs = append(attrs, &attr) attrs = append(attrs, &attr)
} else {
entries++
} }
} }
if entries <= 0 {
return errNoSubnets
}
node.SetAttributes(attrs) node.SetAttributes(attrs)
return nil return nil

View file

@ -179,6 +179,15 @@ func TestSubnets(t *testing.T) {
t.Run("zero", func(t *testing.T) { t.Run("zero", func(t *testing.T) {
var node netmap.NodeInfo var node netmap.NodeInfo
// enter to some non-zero subnet so that zero is not the only one
var attr netmap.Attribute
attr.SetKey(subnetAttrKey("321"))
attr.SetValue("True")
attrs := []*netmap.Attribute{&attr}
node.SetAttributes(attrs)
err := netmap.IterateSubnets(&node, func(id refs.SubnetID) error { err := netmap.IterateSubnets(&node, func(id refs.SubnetID) error {
if refs.IsZeroSubnet(&id) { if refs.IsZeroSubnet(&id) {
return netmap.ErrRemoveSubnet return netmap.ErrRemoveSubnet
@ -189,12 +198,19 @@ func TestSubnets(t *testing.T) {
require.NoError(t, err) require.NoError(t, err)
attrs := node.GetAttributes() attrs = node.GetAttributes()
require.Len(t, attrs, 1) require.Len(t, attrs, 2)
attr := attrs[0] found := false
assertSubnetAttrKey(t, attr, 0)
require.Equal(t, "False", attr.GetValue()) for i := range attrs {
if attrs[i].GetKey() == subnetAttrKey("0") {
require.Equal(t, "False", attrs[i].GetValue())
found = true
}
}
require.True(t, found)
}) })
t.Run("non-zero", func(t *testing.T) { t.Run("non-zero", func(t *testing.T) {
@ -222,5 +238,30 @@ func TestSubnets(t *testing.T) {
attrs = node.GetAttributes() attrs = node.GetAttributes()
require.Empty(t, attrs) require.Empty(t, attrs)
}) })
t.Run("all", func(t *testing.T) {
var (
node netmap.NodeInfo
attrs []*netmap.Attribute
)
// enter to some non-zero subnet so that zero is not the only one
for i := 1; i <= 5; i++ {
var attr netmap.Attribute
attr.SetKey(subnetAttrKey(strconv.Itoa(i)))
attr.SetValue("True")
attrs = append(attrs, &attr)
}
node.SetAttributes(attrs)
err := netmap.IterateSubnets(&node, func(id refs.SubnetID) error {
return netmap.ErrRemoveSubnet
})
require.Error(t, err)
})
}) })
} }