forked from TrueCloudLab/frostfs-sdk-go
*: drop subnet support
We don't need them and while there are reason to have some remnants in the protocol itself we can remove them from the SDK immediately to not include them into the 1.0.0 release. Refs. nspcc-dev/neofs-api#261. Signed-off-by: Roman Khimov <roman@nspcc.ru>
This commit is contained in:
parent
7002b3b0df
commit
de560d7424
18 changed files with 2 additions and 994 deletions
|
@ -15,7 +15,6 @@ import (
|
|||
cid "github.com/nspcc-dev/neofs-sdk-go/container/id"
|
||||
neofscrypto "github.com/nspcc-dev/neofs-sdk-go/crypto"
|
||||
"github.com/nspcc-dev/neofs-sdk-go/netmap"
|
||||
subnetid "github.com/nspcc-dev/neofs-sdk-go/subnet/id"
|
||||
"github.com/nspcc-dev/neofs-sdk-go/user"
|
||||
"github.com/nspcc-dev/neofs-sdk-go/version"
|
||||
)
|
||||
|
@ -116,8 +115,6 @@ func (x *Container) readFromV2(m container.Container, checkFieldPresence bool) e
|
|||
}
|
||||
|
||||
switch key {
|
||||
case container.SysAttributeSubnet:
|
||||
err = new(subnetid.ID).DecodeString(val)
|
||||
case attributeTimestamp:
|
||||
_, err = strconv.ParseInt(val, 10, 64)
|
||||
}
|
||||
|
@ -381,28 +378,6 @@ func CreatedAt(cnr Container) time.Time {
|
|||
return time.Unix(sec, 0)
|
||||
}
|
||||
|
||||
// SetSubnet places the Container on the specified NeoFS subnet. If called,
|
||||
// container nodes will only be selected from the given subnet, otherwise from
|
||||
// the entire network.
|
||||
func SetSubnet(cnr *Container, subNet subnetid.ID) {
|
||||
cnr.SetAttribute(container.SysAttributeSubnet, subNet.EncodeToString())
|
||||
}
|
||||
|
||||
// Subnet return container subnet set using SetSubnet.
|
||||
//
|
||||
// Zero Container is bound to zero subnet.
|
||||
func Subnet(cnr Container) (res subnetid.ID) {
|
||||
val := cnr.Attribute(container.SysAttributeSubnet)
|
||||
if val != "" {
|
||||
err := res.DecodeString(val)
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("invalid subnet attribute: %s (%v)", val, err))
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
const attributeHomoHashEnabled = "true"
|
||||
|
||||
// DisableHomomorphicHashing sets flag to disable homomorphic hashing of the
|
||||
|
|
|
@ -17,8 +17,6 @@ import (
|
|||
neofscrypto "github.com/nspcc-dev/neofs-sdk-go/crypto"
|
||||
"github.com/nspcc-dev/neofs-sdk-go/crypto/test"
|
||||
netmaptest "github.com/nspcc-dev/neofs-sdk-go/netmap/test"
|
||||
subnetid "github.com/nspcc-dev/neofs-sdk-go/subnet/id"
|
||||
subnetidtest "github.com/nspcc-dev/neofs-sdk-go/subnet/id/test"
|
||||
usertest "github.com/nspcc-dev/neofs-sdk-go/user/test"
|
||||
"github.com/nspcc-dev/neofs-sdk-go/version"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
@ -233,28 +231,6 @@ func TestSetCreationTime(t *testing.T) {
|
|||
require.Equal(t, creat.Unix(), container.CreatedAt(val2).Unix())
|
||||
}
|
||||
|
||||
func TestSetSubnet(t *testing.T) {
|
||||
var val container.Container
|
||||
|
||||
require.True(t, subnetid.IsZero(container.Subnet(val)))
|
||||
|
||||
val = containertest.Container(t)
|
||||
|
||||
sub := subnetidtest.ID()
|
||||
|
||||
container.SetSubnet(&val, sub)
|
||||
|
||||
var msg v2container.Container
|
||||
val.WriteToV2(&msg)
|
||||
|
||||
assertContainsAttribute(t, msg, v2container.SysAttributeSubnet, sub.EncodeToString())
|
||||
|
||||
var val2 container.Container
|
||||
require.NoError(t, val2.ReadFromV2(msg))
|
||||
|
||||
require.Equal(t, sub, container.Subnet(val))
|
||||
}
|
||||
|
||||
func TestDisableHomomorphicHashing(t *testing.T) {
|
||||
var val container.Container
|
||||
|
||||
|
|
|
@ -1,254 +0,0 @@
|
|||
{
|
||||
"name": "subnet tests",
|
||||
"nodes": [
|
||||
{
|
||||
"attributes": [
|
||||
{
|
||||
"key": "ID",
|
||||
"value": "0"
|
||||
},
|
||||
{
|
||||
"key": "City",
|
||||
"value": "Paris"
|
||||
},
|
||||
{
|
||||
"key": "__NEOFS__SUBNET_0",
|
||||
"value": "False"
|
||||
}
|
||||
],
|
||||
"state": "UNSPECIFIED"
|
||||
},
|
||||
{
|
||||
"attributes": [
|
||||
{
|
||||
"key": "ID",
|
||||
"value": "1"
|
||||
},
|
||||
{
|
||||
"key": "City",
|
||||
"value": "Paris"
|
||||
}
|
||||
],
|
||||
"state": "UNSPECIFIED"
|
||||
},
|
||||
{
|
||||
"attributes": [
|
||||
{
|
||||
"key": "ID",
|
||||
"value": "2"
|
||||
},
|
||||
{
|
||||
"key": "City",
|
||||
"value": "London"
|
||||
},
|
||||
{
|
||||
"key": "__NEOFS__SUBNET_1",
|
||||
"value": "True"
|
||||
}
|
||||
],
|
||||
"state": "UNSPECIFIED"
|
||||
},
|
||||
{
|
||||
"attributes": [
|
||||
{
|
||||
"key": "ID",
|
||||
"value": "3"
|
||||
},
|
||||
{
|
||||
"key": "City",
|
||||
"value": "London"
|
||||
}
|
||||
],
|
||||
"state": "UNSPECIFIED"
|
||||
},
|
||||
{
|
||||
"attributes": [
|
||||
{
|
||||
"key": "ID",
|
||||
"value": "4"
|
||||
},
|
||||
{
|
||||
"key": "City",
|
||||
"value": "Toronto"
|
||||
},
|
||||
{
|
||||
"key": "__NEOFS__SUBNET_1",
|
||||
"value": "True"
|
||||
}
|
||||
],
|
||||
"state": "UNSPECIFIED"
|
||||
},
|
||||
{
|
||||
"attributes": [
|
||||
{
|
||||
"key": "ID",
|
||||
"value": "5"
|
||||
},
|
||||
{
|
||||
"key": "City",
|
||||
"value": "Toronto"
|
||||
},
|
||||
{
|
||||
"key": "__NEOFS__SUBNET_2",
|
||||
"value": "True"
|
||||
}
|
||||
],
|
||||
"state": "UNSPECIFIED"
|
||||
},
|
||||
{
|
||||
"attributes": [
|
||||
{
|
||||
"key": "ID",
|
||||
"value": "6"
|
||||
},
|
||||
{
|
||||
"key": "City",
|
||||
"value": "Tokyo"
|
||||
},
|
||||
{
|
||||
"key": "__NEOFS__SUBNET_2",
|
||||
"value": "True"
|
||||
}
|
||||
],
|
||||
"state": "UNSPECIFIED"
|
||||
},
|
||||
{
|
||||
"attributes": [
|
||||
{
|
||||
"key": "ID",
|
||||
"value": "7"
|
||||
},
|
||||
{
|
||||
"key": "City",
|
||||
"value": "Tokyo"
|
||||
},
|
||||
{
|
||||
"key": "__NEOFS__SUBNET_2",
|
||||
"value": "True"
|
||||
}
|
||||
],
|
||||
"state": "UNSPECIFIED"
|
||||
}
|
||||
],
|
||||
"tests": {
|
||||
"select from default subnet, fail": {
|
||||
"policy": {
|
||||
"replicas": [
|
||||
{
|
||||
"count": 1,
|
||||
"selector": "S"
|
||||
}
|
||||
],
|
||||
"containerBackupFactor": 0,
|
||||
"selectors": [
|
||||
{
|
||||
"name": "S",
|
||||
"count": 2,
|
||||
"clause": "SAME",
|
||||
"attribute": "City",
|
||||
"filter": "F"
|
||||
}
|
||||
],
|
||||
"filters": [
|
||||
{
|
||||
"name": "F",
|
||||
"key": "City",
|
||||
"op": "EQ",
|
||||
"value": "Paris",
|
||||
"filters": []
|
||||
}
|
||||
],
|
||||
"subnetId": null
|
||||
},
|
||||
"error": "not enough nodes"
|
||||
},
|
||||
"select from default subnet, success": {
|
||||
"policy": {
|
||||
"replicas": [
|
||||
{
|
||||
"count": 1,
|
||||
"selector": "S"
|
||||
}
|
||||
],
|
||||
"containerBackupFactor": 0,
|
||||
"selectors": [
|
||||
{
|
||||
"name": "S",
|
||||
"count": 2,
|
||||
"clause": "SAME",
|
||||
"attribute": "City",
|
||||
"filter": "F"
|
||||
}
|
||||
],
|
||||
"filters": [
|
||||
{
|
||||
"name": "F",
|
||||
"key": "City",
|
||||
"op": "EQ",
|
||||
"value": "Toronto",
|
||||
"filters": []
|
||||
}
|
||||
],
|
||||
"subnetId": null
|
||||
},
|
||||
"result": [
|
||||
[
|
||||
4,
|
||||
5
|
||||
]
|
||||
]
|
||||
},
|
||||
"select from non-default subnet, success": {
|
||||
"policy": {
|
||||
"replicas": [
|
||||
{
|
||||
"count": 3,
|
||||
"selector": ""
|
||||
}
|
||||
],
|
||||
"containerBackupFactor": 0,
|
||||
"selectors": [],
|
||||
"filters": [],
|
||||
"subnetId": {
|
||||
"value": 2
|
||||
}
|
||||
},
|
||||
"result": [
|
||||
[
|
||||
5,
|
||||
6,
|
||||
7
|
||||
]
|
||||
]
|
||||
},
|
||||
"select subnet via filters": {
|
||||
"policy": {
|
||||
"replicas": [
|
||||
{
|
||||
"count": 1,
|
||||
"selector": "S"
|
||||
}
|
||||
],
|
||||
"containerBackupFactor": 1,
|
||||
"selectors": [
|
||||
{
|
||||
"name": "S",
|
||||
"count": 1,
|
||||
"clause": "SAME",
|
||||
"attribute": "City",
|
||||
"filter": "F"
|
||||
}
|
||||
],
|
||||
"filters": [
|
||||
{
|
||||
"name": "F",
|
||||
"key": "__NEOFS_SUBNET.2.ENABLED",
|
||||
"op": "EQ",
|
||||
"value": "True"
|
||||
}
|
||||
]
|
||||
},
|
||||
"error": "not enough nodes"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -9,9 +9,7 @@ import (
|
|||
|
||||
"github.com/nspcc-dev/hrw"
|
||||
"github.com/nspcc-dev/neofs-api-go/v2/netmap"
|
||||
"github.com/nspcc-dev/neofs-api-go/v2/refs"
|
||||
neofscrypto "github.com/nspcc-dev/neofs-sdk-go/crypto"
|
||||
subnetid "github.com/nspcc-dev/neofs-sdk-go/subnet/id"
|
||||
)
|
||||
|
||||
// NodeInfo groups information about NeoFS storage node which is reflected
|
||||
|
@ -53,8 +51,6 @@ func (x *NodeInfo) readFromV2(m netmap.NodeInfo, checkFieldPresence bool) error
|
|||
return fmt.Errorf("duplicated attbiuted %s", key)
|
||||
}
|
||||
|
||||
const subnetPrefix = "__NEOFS__SUBNET_"
|
||||
|
||||
switch {
|
||||
case key == attrCapacity:
|
||||
_, err = strconv.ParseUint(attributes[i].GetValue(), 10, 64)
|
||||
|
@ -67,17 +63,6 @@ func (x *NodeInfo) readFromV2(m netmap.NodeInfo, checkFieldPresence bool) error
|
|||
if err != nil {
|
||||
return fmt.Errorf("invalid %s attribute: %w", attrPrice, err)
|
||||
}
|
||||
case strings.HasPrefix(key, subnetPrefix):
|
||||
var id subnetid.ID
|
||||
|
||||
err = id.DecodeString(strings.TrimPrefix(key, subnetPrefix))
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid key to the subnet attribute %s: %w", key, err)
|
||||
}
|
||||
|
||||
if val := attributes[i].GetValue(); val != "True" && val != "False" {
|
||||
return fmt.Errorf("invalid value of the subnet attribute %s: %w", val, err)
|
||||
}
|
||||
default:
|
||||
if attributes[i].GetValue() == "" {
|
||||
return fmt.Errorf("empty value of the attribute %s", key)
|
||||
|
@ -478,81 +463,6 @@ func (x *NodeInfo) SortAttributes() {
|
|||
x.m.SetAttributes(as)
|
||||
}
|
||||
|
||||
// EnterSubnet writes storage node's intention to enter the given subnet.
|
||||
//
|
||||
// Zero NodeInfo belongs to zero subnet.
|
||||
func (x *NodeInfo) EnterSubnet(id subnetid.ID) {
|
||||
x.changeSubnet(id, true)
|
||||
}
|
||||
|
||||
// ExitSubnet writes storage node's intention to exit the given subnet.
|
||||
func (x *NodeInfo) ExitSubnet(id subnetid.ID) {
|
||||
x.changeSubnet(id, false)
|
||||
}
|
||||
|
||||
func (x *NodeInfo) changeSubnet(id subnetid.ID, isMember bool) {
|
||||
var (
|
||||
idv2 refs.SubnetID
|
||||
info netmap.NodeSubnetInfo
|
||||
)
|
||||
|
||||
id.WriteToV2(&idv2)
|
||||
|
||||
info.SetID(&idv2)
|
||||
info.SetEntryFlag(isMember)
|
||||
|
||||
netmap.WriteSubnetInfo(&x.m, 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.
|
||||
// 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 to any subnet by the end of the loop.
|
||||
//
|
||||
// See also EnterSubnet, ExitSubnet.
|
||||
func (x NodeInfo) IterateSubnets(f func(subnetid.ID) error) error {
|
||||
var id subnetid.ID
|
||||
|
||||
return netmap.IterateSubnets(&x.m, func(idv2 refs.SubnetID) error {
|
||||
err := id.ReadFromV2(idv2)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid subnet: %w", err)
|
||||
}
|
||||
|
||||
err = f(id)
|
||||
if errors.Is(err, ErrRemoveSubnet) {
|
||||
return netmap.ErrRemoveSubnet
|
||||
}
|
||||
|
||||
return err
|
||||
})
|
||||
}
|
||||
|
||||
var errAbortSubnetIter = errors.New("abort subnet iterator")
|
||||
|
||||
// BelongsToSubnet is a helper function over the IterateSubnets method which
|
||||
// checks whether a node belongs to a subnet.
|
||||
//
|
||||
// Zero NodeInfo 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)
|
||||
}
|
||||
|
||||
// SetOffline sets the state of the node to "offline". When a node updates
|
||||
// information about itself in the network map, this action is interpreted as
|
||||
// an intention to leave the network.
|
||||
|
|
|
@ -9,9 +9,7 @@ import (
|
|||
|
||||
"github.com/antlr/antlr4/runtime/Go/antlr/v4"
|
||||
"github.com/nspcc-dev/neofs-api-go/v2/netmap"
|
||||
"github.com/nspcc-dev/neofs-api-go/v2/refs"
|
||||
"github.com/nspcc-dev/neofs-sdk-go/netmap/parser"
|
||||
subnetid "github.com/nspcc-dev/neofs-sdk-go/subnet/id"
|
||||
)
|
||||
|
||||
// PlacementPolicy declares policy to store objects in the NeoFS container.
|
||||
|
@ -25,8 +23,6 @@ import (
|
|||
type PlacementPolicy struct {
|
||||
backupFactor uint32
|
||||
|
||||
subnet subnetid.ID
|
||||
|
||||
filters []netmap.Filter
|
||||
|
||||
selectors []netmap.Selector
|
||||
|
@ -40,16 +36,6 @@ func (p *PlacementPolicy) readFromV2(m netmap.PlacementPolicy, checkFieldPresenc
|
|||
return errors.New("missing replicas")
|
||||
}
|
||||
|
||||
subnetV2 := m.GetSubnetID()
|
||||
if subnetV2 != nil {
|
||||
err := p.subnet.ReadFromV2(*subnetV2)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid subnet: %w", err)
|
||||
}
|
||||
} else {
|
||||
p.subnet = subnetid.ID{}
|
||||
}
|
||||
|
||||
p.backupFactor = m.GetContainerBackupFactor()
|
||||
p.selectors = m.GetSelectors()
|
||||
p.filters = m.GetFilters()
|
||||
|
@ -123,29 +109,12 @@ func (p *PlacementPolicy) ReadFromV2(m netmap.PlacementPolicy) error {
|
|||
//
|
||||
// See also ReadFromV2.
|
||||
func (p PlacementPolicy) WriteToV2(m *netmap.PlacementPolicy) {
|
||||
var subnetV2 refs.SubnetID
|
||||
p.subnet.WriteToV2(&subnetV2)
|
||||
|
||||
m.SetContainerBackupFactor(p.backupFactor)
|
||||
m.SetSubnetID(&subnetV2)
|
||||
m.SetFilters(p.filters)
|
||||
m.SetSelectors(p.selectors)
|
||||
m.SetReplicas(p.replicas)
|
||||
}
|
||||
|
||||
// RestrictSubnet sets a rule to select nodes from the given subnet only.
|
||||
// By default, nodes from zero subnet are selected (whole network map).
|
||||
func (p *PlacementPolicy) RestrictSubnet(subnet subnetid.ID) {
|
||||
p.subnet = subnet
|
||||
}
|
||||
|
||||
// Subnet returns subnet set using RestrictSubnet.
|
||||
//
|
||||
// Zero PlacementPolicy returns zero subnet meaning unlimited.
|
||||
func (p PlacementPolicy) Subnet() subnetid.ID {
|
||||
return p.subnet
|
||||
}
|
||||
|
||||
// ReplicaDescriptor replica descriptor characterizes replicas of objects from
|
||||
// the subset selected by a particular Selector.
|
||||
type ReplicaDescriptor struct {
|
||||
|
|
|
@ -6,7 +6,6 @@ import (
|
|||
|
||||
"github.com/nspcc-dev/hrw"
|
||||
"github.com/nspcc-dev/neofs-api-go/v2/netmap"
|
||||
subnetid "github.com/nspcc-dev/neofs-sdk-go/subnet/id"
|
||||
)
|
||||
|
||||
// processSelectors processes selectors and returns error is any of them is invalid.
|
||||
|
@ -59,7 +58,7 @@ func calcBucketWeight(ns nodes, a aggregator, wf weightFunc) float64 {
|
|||
// Last argument specifies if more buckets can be used to fulfill CBF.
|
||||
func (c *context) getSelection(p PlacementPolicy, s netmap.Selector) ([]nodes, error) {
|
||||
bucketCount, nodesInBucket := calcNodesCount(s)
|
||||
buckets := c.getSelectionBase(p.subnet, s)
|
||||
buckets := c.getSelectionBase(s)
|
||||
|
||||
if len(buckets) < bucketCount {
|
||||
return nil, fmt.Errorf("%w: '%s'", errNotEnoughNodes, s.GetName())
|
||||
|
@ -132,7 +131,7 @@ type nodeAttrPair struct {
|
|||
|
||||
// getSelectionBase returns nodes grouped by selector attribute.
|
||||
// It it guaranteed that each pair will contain at least one node.
|
||||
func (c *context) getSelectionBase(subnetID subnetid.ID, s netmap.Selector) []nodeAttrPair {
|
||||
func (c *context) getSelectionBase(s netmap.Selector) []nodeAttrPair {
|
||||
fName := s.GetFilter()
|
||||
f := c.processedFilters[fName]
|
||||
isMain := fName == mainFilterName
|
||||
|
@ -141,10 +140,6 @@ func (c *context) getSelectionBase(subnetID subnetid.ID, s netmap.Selector) []no
|
|||
attr := s.GetAttribute()
|
||||
|
||||
for i := range c.netMap.nodes {
|
||||
// TODO(fyrchik): make `BelongsToSubnet` to accept pointer
|
||||
if !BelongsToSubnet(c.netMap.nodes[i], subnetID) {
|
||||
continue
|
||||
}
|
||||
if isMain || c.match(f, c.netMap.nodes[i]) {
|
||||
if attr == "" {
|
||||
// Default attribute is transparent identifier which is different for every node.
|
||||
|
|
|
@ -1,126 +0,0 @@
|
|||
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.SetNumeric(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.SetNumeric(13)
|
||||
idrm.SetNumeric(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 TestEnterSubnet(t *testing.T) {
|
||||
var (
|
||||
id subnetid.ID
|
||||
node netmap.NodeInfo
|
||||
)
|
||||
|
||||
require.True(t, netmap.BelongsToSubnet(node, id))
|
||||
|
||||
node.EnterSubnet(id)
|
||||
require.True(t, netmap.BelongsToSubnet(node, id))
|
||||
|
||||
node.ExitSubnet(id)
|
||||
require.False(t, netmap.BelongsToSubnet(node, id))
|
||||
|
||||
id.SetNumeric(10)
|
||||
node.EnterSubnet(id)
|
||||
require.True(t, netmap.BelongsToSubnet(node, id))
|
||||
require.False(t, netmap.BelongsToSubnet(node, subnetid.ID{}))
|
||||
|
||||
node.ExitSubnet(id)
|
||||
require.False(t, netmap.BelongsToSubnet(node, id))
|
||||
require.False(t, netmap.BelongsToSubnet(node, subnetid.ID{}))
|
||||
}
|
||||
|
||||
func TestBelongsToSubnet(t *testing.T) {
|
||||
var id, idMiss, idZero subnetid.ID
|
||||
|
||||
id.SetNumeric(13)
|
||||
idMiss.SetNumeric(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))
|
||||
}
|
|
@ -4,7 +4,6 @@ import (
|
|||
"math/rand"
|
||||
|
||||
"github.com/nspcc-dev/neofs-sdk-go/netmap"
|
||||
subnetidtest "github.com/nspcc-dev/neofs-sdk-go/subnet/id/test"
|
||||
)
|
||||
|
||||
func filter(withInner bool) (x netmap.Filter) {
|
||||
|
@ -48,7 +47,6 @@ func PlacementPolicy() (p netmap.PlacementPolicy) {
|
|||
p.AddFilters(Filter(), Filter())
|
||||
p.AddReplicas(Replica(), Replica())
|
||||
p.AddSelectors(Selector(), Selector())
|
||||
p.RestrictSubnet(subnetidtest.ID())
|
||||
|
||||
return
|
||||
}
|
||||
|
|
|
@ -1,9 +0,0 @@
|
|||
/*
|
||||
Package subnet collects functionality related to the NeoFS subnets.
|
||||
|
||||
Subnet of a particular NeoFS network consists of a subset of the storage nodes
|
||||
of that network. Subnet of the whole network is called zero. Info type acts as
|
||||
a subnet descriptor. Each subnet is owned by the user who created it. Information
|
||||
about all subnets is stored in the Subnet contract of the NeoFS Sidechain.
|
||||
*/
|
||||
package subnet
|
|
@ -1,9 +0,0 @@
|
|||
/*
|
||||
Package subnetid provides primitives to work with subnet identification in NeoFS.
|
||||
|
||||
ID type is used for global subnet identity inside the NeoFS network.
|
||||
|
||||
Using package types in an application is recommended to potentially work with
|
||||
different protocol versions with which these types are compatible.
|
||||
*/
|
||||
package subnetid
|
105
subnet/id/id.go
105
subnet/id/id.go
|
@ -1,105 +0,0 @@
|
|||
package subnetid
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
|
||||
"github.com/nspcc-dev/neofs-api-go/v2/refs"
|
||||
)
|
||||
|
||||
// ID represents unique identifier of the subnet in the NeoFS network.
|
||||
//
|
||||
// ID is mutually compatible with github.com/nspcc-dev/neofs-api-go/v2/refs.SubnetID
|
||||
// message. See ReadFromV2 / WriteToV2 methods.
|
||||
//
|
||||
// Instances can be created using built-in var declaration. Zero value is
|
||||
// equivalent to identifier of the zero subnet (whole NeoFS network).
|
||||
type ID struct {
|
||||
m refs.SubnetID
|
||||
}
|
||||
|
||||
// ReadFromV2 reads ID from the refs.SubnetID message. Checks if the
|
||||
// message conforms to NeoFS API V2 protocol.
|
||||
//
|
||||
// See also WriteToV2.
|
||||
func (x *ID) ReadFromV2(msg refs.SubnetID) error {
|
||||
x.m = msg
|
||||
return nil
|
||||
}
|
||||
|
||||
// WriteToV2 writes ID to refs.SubnetID message structure. The message MUST NOT
|
||||
// be nil.
|
||||
//
|
||||
// See also ReadFromV2.
|
||||
func (x ID) WriteToV2(msg *refs.SubnetID) {
|
||||
*msg = x.m
|
||||
}
|
||||
|
||||
// Equals defines a comparison relation between two ID instances.
|
||||
//
|
||||
// Note that comparison using '==' operator is not recommended since it MAY result
|
||||
// in loss of compatibility.
|
||||
func (x ID) Equals(x2 ID) bool {
|
||||
return x.m.GetValue() == x2.m.GetValue()
|
||||
}
|
||||
|
||||
// EncodeToString encodes ID into NeoFS API protocol string (base10 encoding).
|
||||
//
|
||||
// See also DecodeString.
|
||||
func (x ID) EncodeToString() string {
|
||||
return strconv.FormatUint(uint64(x.m.GetValue()), 10)
|
||||
}
|
||||
|
||||
// DecodeString decodes string calculated using EncodeToString. Returns
|
||||
// an error if s is malformed.
|
||||
func (x *ID) DecodeString(s string) error {
|
||||
num, err := strconv.ParseUint(s, 10, 32)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid numeric value: %w", err)
|
||||
}
|
||||
|
||||
x.m.SetValue(uint32(num))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// String implements fmt.Stringer.
|
||||
//
|
||||
// String is designed to be human-readable, and its format MAY differ between
|
||||
// SDK versions. String MAY return same result as EncodeToString. String MUST NOT
|
||||
// be used to encode ID into NeoFS protocol string.
|
||||
func (x ID) String() string {
|
||||
return "#" + strconv.FormatUint(uint64(x.m.GetValue()), 10)
|
||||
}
|
||||
|
||||
// Marshal encodes ID into a binary format of the NeoFS API protocol
|
||||
// (Protocol Buffers with direct field order).
|
||||
//
|
||||
// See also Unmarshal.
|
||||
func (x ID) Marshal() []byte {
|
||||
return x.m.StableMarshal(nil)
|
||||
}
|
||||
|
||||
// Unmarshal decodes binary ID calculated using Marshal. Returns an error
|
||||
// describing a format violation.
|
||||
func (x *ID) Unmarshal(data []byte) error {
|
||||
return x.m.Unmarshal(data)
|
||||
}
|
||||
|
||||
// SetNumeric sets ID value in numeric format. By default, number is 0 which
|
||||
// refers to the zero subnet.
|
||||
func (x *ID) SetNumeric(num uint32) {
|
||||
x.m.SetValue(num)
|
||||
}
|
||||
|
||||
// IsZero compares id with zero subnet ID.
|
||||
func IsZero(id ID) bool {
|
||||
return id.Equals(ID{})
|
||||
}
|
||||
|
||||
// MakeZero makes ID to refer to zero subnet.
|
||||
//
|
||||
// Makes no sense to call on zero value (e.g. declared using var).
|
||||
func MakeZero(id *ID) {
|
||||
id.SetNumeric(0)
|
||||
}
|
|
@ -1,100 +0,0 @@
|
|||
package subnetid_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/nspcc-dev/neofs-api-go/v2/refs"
|
||||
subnetid "github.com/nspcc-dev/neofs-sdk-go/subnet/id"
|
||||
subnetidtest "github.com/nspcc-dev/neofs-sdk-go/subnet/id/test"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestIsZero(t *testing.T) {
|
||||
var id subnetid.ID
|
||||
|
||||
require.True(t, subnetid.IsZero(id))
|
||||
|
||||
id.SetNumeric(13)
|
||||
require.False(t, subnetid.IsZero(id))
|
||||
|
||||
id.SetNumeric(0)
|
||||
require.True(t, subnetid.IsZero(id))
|
||||
}
|
||||
|
||||
func TestID_ReadFromV2(t *testing.T) {
|
||||
const num = 13
|
||||
|
||||
var id1 subnetid.ID
|
||||
id1.SetNumeric(num)
|
||||
|
||||
var idv2 refs.SubnetID
|
||||
idv2.SetValue(num)
|
||||
|
||||
var id2 subnetid.ID
|
||||
require.NoError(t, id2.ReadFromV2(idv2))
|
||||
|
||||
require.True(t, id1.Equals(id2))
|
||||
}
|
||||
|
||||
func TestID_WriteToV2(t *testing.T) {
|
||||
const num = 13
|
||||
|
||||
var (
|
||||
id subnetid.ID
|
||||
idv2 refs.SubnetID
|
||||
)
|
||||
|
||||
id.WriteToV2(&idv2)
|
||||
require.Zero(t, idv2.GetValue())
|
||||
|
||||
id.SetNumeric(num)
|
||||
|
||||
id.WriteToV2(&idv2)
|
||||
require.EqualValues(t, num, idv2.GetValue())
|
||||
}
|
||||
|
||||
func TestID_Equals(t *testing.T) {
|
||||
const num = 13
|
||||
|
||||
var id1, id2, idOther, id0 subnetid.ID
|
||||
|
||||
id0.Equals(subnetid.ID{})
|
||||
|
||||
id1.SetNumeric(num)
|
||||
id2.SetNumeric(num)
|
||||
idOther.SetNumeric(num + 1)
|
||||
|
||||
require.True(t, id1.Equals(id2))
|
||||
require.False(t, id1.Equals(idOther))
|
||||
require.False(t, id2.Equals(idOther))
|
||||
}
|
||||
|
||||
func TestSubnetIDEncoding(t *testing.T) {
|
||||
id := subnetidtest.ID()
|
||||
|
||||
t.Run("binary", func(t *testing.T) {
|
||||
var id2 subnetid.ID
|
||||
require.NoError(t, id2.Unmarshal(id.Marshal()))
|
||||
|
||||
require.True(t, id2.Equals(id))
|
||||
})
|
||||
|
||||
t.Run("text", func(t *testing.T) {
|
||||
var id2 subnetid.ID
|
||||
require.NoError(t, id2.DecodeString(id.EncodeToString()))
|
||||
|
||||
require.True(t, id2.Equals(id))
|
||||
})
|
||||
}
|
||||
|
||||
func TestMakeZero(t *testing.T) {
|
||||
var id subnetid.ID
|
||||
id.SetNumeric(13)
|
||||
|
||||
require.False(t, subnetid.IsZero(id))
|
||||
|
||||
subnetid.MakeZero(&id)
|
||||
|
||||
require.True(t, subnetid.IsZero(id))
|
||||
require.Equal(t, subnetid.ID{}, id)
|
||||
}
|
|
@ -1,13 +0,0 @@
|
|||
/*
|
||||
Package subnetidtest provides functions for convenient testing of subnetid package API.
|
||||
|
||||
Note that importing the package into source files is highly discouraged.
|
||||
|
||||
Random instance generation functions can be useful when testing expects any value, e.g.:
|
||||
|
||||
import subnetidtest "github.com/nspcc-dev/neofs-sdk-go/suibnet/id/test"
|
||||
|
||||
value := subnetidtest.ID()
|
||||
// test the value
|
||||
*/
|
||||
package subnetidtest
|
|
@ -1,13 +0,0 @@
|
|||
package subnetidtest
|
||||
|
||||
import (
|
||||
"math/rand"
|
||||
|
||||
subnetid "github.com/nspcc-dev/neofs-sdk-go/subnet/id"
|
||||
)
|
||||
|
||||
// ID generates and returns random subnetid.ID.
|
||||
func ID() (x subnetid.ID) {
|
||||
x.SetNumeric(rand.Uint32())
|
||||
return
|
||||
}
|
109
subnet/subnet.go
109
subnet/subnet.go
|
@ -1,109 +0,0 @@
|
|||
package subnet
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/nspcc-dev/neofs-api-go/v2/refs"
|
||||
"github.com/nspcc-dev/neofs-api-go/v2/subnet"
|
||||
subnetid "github.com/nspcc-dev/neofs-sdk-go/subnet/id"
|
||||
"github.com/nspcc-dev/neofs-sdk-go/user"
|
||||
)
|
||||
|
||||
// Info represents information about NeoFS subnet.
|
||||
//
|
||||
// Instances can be created using built-in var declaration.
|
||||
type Info struct {
|
||||
id subnetid.ID
|
||||
|
||||
owner user.ID
|
||||
}
|
||||
|
||||
// Marshal encodes Info into a binary format of the NeoFS API protocol
|
||||
// (Protocol Buffers with direct field order).
|
||||
//
|
||||
// See also Unmarshal.
|
||||
func (x Info) Marshal() []byte {
|
||||
var id refs.SubnetID
|
||||
x.id.WriteToV2(&id)
|
||||
|
||||
var owner refs.OwnerID
|
||||
x.owner.WriteToV2(&owner)
|
||||
|
||||
var m subnet.Info
|
||||
m.SetID(&id)
|
||||
m.SetOwner(&owner)
|
||||
|
||||
return m.StableMarshal(nil)
|
||||
}
|
||||
|
||||
// Unmarshal decodes binary Info calculated using Marshal. Returns an error
|
||||
// describing a format violation.
|
||||
func (x *Info) Unmarshal(data []byte) error {
|
||||
var m subnet.Info
|
||||
|
||||
err := m.Unmarshal(data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
id := m.ID()
|
||||
if id != nil {
|
||||
err = x.id.ReadFromV2(*id)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid ID: %w", err)
|
||||
}
|
||||
} else {
|
||||
subnetid.MakeZero(&x.id)
|
||||
}
|
||||
|
||||
owner := m.Owner()
|
||||
if owner != nil {
|
||||
err = x.owner.ReadFromV2(*owner)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid owner: %w", err)
|
||||
}
|
||||
} else {
|
||||
x.owner = user.ID{}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetID sets the identifier of the subnet that Info describes.
|
||||
//
|
||||
// See also ID.
|
||||
func (x *Info) SetID(id subnetid.ID) {
|
||||
x.id = id
|
||||
}
|
||||
|
||||
// ID returns subnet identifier set using SetID.
|
||||
//
|
||||
// Zero Info refers to the zero subnet.
|
||||
func (x Info) ID() subnetid.ID {
|
||||
return x.id
|
||||
}
|
||||
|
||||
// SetOwner sets identifier of the subnet owner.
|
||||
func (x *Info) SetOwner(id user.ID) {
|
||||
x.owner = id
|
||||
}
|
||||
|
||||
// Owner returns subnet owner set using SetOwner.
|
||||
//
|
||||
// Zero Info has no owner which is incorrect according to the
|
||||
// NeoFS API protocol.
|
||||
func (x Info) Owner() user.ID {
|
||||
return x.owner
|
||||
}
|
||||
|
||||
// AssertOwnership checks if the given info describes the subnet owned by the
|
||||
// given user.
|
||||
func AssertOwnership(info Info, id user.ID) bool {
|
||||
return id.Equals(info.Owner())
|
||||
}
|
||||
|
||||
// AssertReference checks if the given info describes the subnet referenced by
|
||||
// the given id.
|
||||
func AssertReference(info Info, id subnetid.ID) bool {
|
||||
return id.Equals(info.ID())
|
||||
}
|
|
@ -1,48 +0,0 @@
|
|||
package subnet_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
. "github.com/nspcc-dev/neofs-sdk-go/subnet"
|
||||
subnetid "github.com/nspcc-dev/neofs-sdk-go/subnet/id"
|
||||
subnetidtest "github.com/nspcc-dev/neofs-sdk-go/subnet/id/test"
|
||||
subnettest "github.com/nspcc-dev/neofs-sdk-go/subnet/test"
|
||||
usertest "github.com/nspcc-dev/neofs-sdk-go/user/test"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestInfoZero(t *testing.T) {
|
||||
var info Info
|
||||
|
||||
require.Zero(t, info.ID())
|
||||
require.True(t, subnetid.IsZero(info.ID()))
|
||||
}
|
||||
|
||||
func TestInfo_SetID(t *testing.T) {
|
||||
id := subnetidtest.ID()
|
||||
|
||||
var info Info
|
||||
info.SetID(id)
|
||||
|
||||
require.Equal(t, id, info.ID())
|
||||
require.True(t, AssertReference(info, id))
|
||||
}
|
||||
|
||||
func TestInfo_SetOwner(t *testing.T) {
|
||||
id := *usertest.ID(t)
|
||||
|
||||
var info Info
|
||||
info.SetOwner(id)
|
||||
|
||||
require.Equal(t, id, info.Owner())
|
||||
require.True(t, AssertOwnership(info, id))
|
||||
}
|
||||
|
||||
func TestInfo_Marshal(t *testing.T) {
|
||||
info := subnettest.Info(t)
|
||||
|
||||
var info2 Info
|
||||
require.NoError(t, info2.Unmarshal(info.Marshal()))
|
||||
|
||||
require.Equal(t, info, info2)
|
||||
}
|
|
@ -1,13 +0,0 @@
|
|||
/*
|
||||
Package subnettest provides functions for convenient testing of subnet package API.
|
||||
|
||||
Note that importing the package into source files is highly discouraged.
|
||||
|
||||
Random instance generation functions can be useful when testing expects any value, e.g.:
|
||||
|
||||
import subnettest "github.com/nspcc-dev/neofs-sdk-go/suibnet/test"
|
||||
|
||||
value := subnettest.Info()
|
||||
// test the value
|
||||
*/
|
||||
package subnettest
|
|
@ -1,16 +0,0 @@
|
|||
package subnettest
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/nspcc-dev/neofs-sdk-go/subnet"
|
||||
subnetidtest "github.com/nspcc-dev/neofs-sdk-go/subnet/id/test"
|
||||
usertest "github.com/nspcc-dev/neofs-sdk-go/user/test"
|
||||
)
|
||||
|
||||
// Info generates and returns random subnet.Info.
|
||||
func Info(t *testing.T) (x subnet.Info) {
|
||||
x.SetID(subnetidtest.ID())
|
||||
x.SetOwner(*usertest.ID(t))
|
||||
return
|
||||
}
|
Loading…
Reference in a new issue