forked from TrueCloudLab/frostfs-sdk-go
[#210] subnet: Refactor and document package functionality
Signed-off-by: Leonard Lyubich <leonard@nspcc.ru>
This commit is contained in:
parent
6709b00c89
commit
eb3b990812
13 changed files with 276 additions and 216 deletions
|
@ -36,13 +36,31 @@ func (p *PlacementPolicy) ToV2() *netmap.PlacementPolicy {
|
||||||
|
|
||||||
// SubnetID returns subnet to select nodes from.
|
// SubnetID returns subnet to select nodes from.
|
||||||
func (p *PlacementPolicy) SubnetID() *subnetid.ID {
|
func (p *PlacementPolicy) SubnetID() *subnetid.ID {
|
||||||
return (*subnetid.ID)(
|
idv2 := (*netmap.PlacementPolicy)(p).GetSubnetID()
|
||||||
(*netmap.PlacementPolicy)(p).GetSubnetID())
|
if idv2 == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var id subnetid.ID
|
||||||
|
|
||||||
|
err := id.ReadFromV2(*idv2)
|
||||||
|
if err != nil {
|
||||||
|
panic(err) // will disappear after netmap package refactor
|
||||||
|
}
|
||||||
|
|
||||||
|
return &id
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetSubnetID sets subnet to select nodes from.
|
// SetSubnetID sets subnet to select nodes from.
|
||||||
func (p *PlacementPolicy) SetSubnetID(subnet *subnetid.ID) {
|
func (p *PlacementPolicy) SetSubnetID(subnet *subnetid.ID) {
|
||||||
(*netmap.PlacementPolicy)(p).SetSubnetID((*refs.SubnetID)(subnet))
|
var idv2 *refs.SubnetID
|
||||||
|
|
||||||
|
if subnet != nil {
|
||||||
|
idv2 = new(refs.SubnetID)
|
||||||
|
subnet.WriteToV2(idv2)
|
||||||
|
}
|
||||||
|
|
||||||
|
(*netmap.PlacementPolicy)(p).SetSubnetID(idv2)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Replicas returns list of object replica descriptors.
|
// Replicas returns list of object replica descriptors.
|
||||||
|
|
|
@ -2,6 +2,7 @@ package netmap
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
"github.com/nspcc-dev/neofs-api-go/v2/netmap"
|
"github.com/nspcc-dev/neofs-api-go/v2/netmap"
|
||||||
"github.com/nspcc-dev/neofs-api-go/v2/refs"
|
"github.com/nspcc-dev/neofs-api-go/v2/refs"
|
||||||
|
@ -48,9 +49,12 @@ func (i *NodeInfo) IterateSubnets(f func(subnetid.ID) error) error {
|
||||||
var id subnetid.ID
|
var id subnetid.ID
|
||||||
|
|
||||||
return netmap.IterateSubnets((*netmap.NodeInfo)(i), func(idv2 refs.SubnetID) error {
|
return netmap.IterateSubnets((*netmap.NodeInfo)(i), func(idv2 refs.SubnetID) error {
|
||||||
id.FromV2(idv2)
|
err := id.ReadFromV2(idv2)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("invalid subnet: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
err := f(id)
|
err = f(id)
|
||||||
if errors.Is(err, ErrRemoveSubnet) {
|
if errors.Is(err, ErrRemoveSubnet) {
|
||||||
return netmap.ErrRemoveSubnet
|
return netmap.ErrRemoveSubnet
|
||||||
}
|
}
|
||||||
|
@ -66,7 +70,7 @@ var errAbortSubnetIter = errors.New("abort subnet iterator")
|
||||||
// Function is NPE-safe: nil NodeInfo always belongs to zero subnet only.
|
// Function is NPE-safe: nil NodeInfo always belongs to zero subnet only.
|
||||||
func BelongsToSubnet(node *NodeInfo, id subnetid.ID) bool {
|
func BelongsToSubnet(node *NodeInfo, id subnetid.ID) bool {
|
||||||
err := node.IterateSubnets(func(id_ subnetid.ID) error {
|
err := node.IterateSubnets(func(id_ subnetid.ID) error {
|
||||||
if id.Equals(&id_) {
|
if id.Equals(id_) {
|
||||||
return errAbortSubnetIter
|
return errAbortSubnetIter
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -12,7 +12,7 @@ func TestNodeInfoSubnets(t *testing.T) {
|
||||||
t.Run("enter subnet", func(t *testing.T) {
|
t.Run("enter subnet", func(t *testing.T) {
|
||||||
var id subnetid.ID
|
var id subnetid.ID
|
||||||
|
|
||||||
id.SetNumber(13)
|
id.SetNumeric(13)
|
||||||
|
|
||||||
var node netmap.NodeInfo
|
var node netmap.NodeInfo
|
||||||
|
|
||||||
|
@ -35,8 +35,8 @@ func TestNodeInfoSubnets(t *testing.T) {
|
||||||
t.Run("not last", func(t *testing.T) {
|
t.Run("not last", func(t *testing.T) {
|
||||||
var id, idrm subnetid.ID
|
var id, idrm subnetid.ID
|
||||||
|
|
||||||
id.SetNumber(13)
|
id.SetNumeric(13)
|
||||||
idrm.SetNumber(23)
|
idrm.SetNumeric(23)
|
||||||
|
|
||||||
var node netmap.NodeInfo
|
var node netmap.NodeInfo
|
||||||
|
|
||||||
|
@ -44,7 +44,7 @@ func TestNodeInfoSubnets(t *testing.T) {
|
||||||
node.EnterSubnet(idrm)
|
node.EnterSubnet(idrm)
|
||||||
|
|
||||||
err := node.IterateSubnets(func(id subnetid.ID) error {
|
err := node.IterateSubnets(func(id subnetid.ID) error {
|
||||||
if subnetid.IsZero(id) || id.Equals(&idrm) {
|
if subnetid.IsZero(id) || id.Equals(idrm) {
|
||||||
return netmap.ErrRemoveSubnet
|
return netmap.ErrRemoveSubnet
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -100,7 +100,7 @@ func TestEnterSubnet(t *testing.T) {
|
||||||
node.ExitSubnet(id)
|
node.ExitSubnet(id)
|
||||||
require.False(t, netmap.BelongsToSubnet(&node, id))
|
require.False(t, netmap.BelongsToSubnet(&node, id))
|
||||||
|
|
||||||
id.SetNumber(10)
|
id.SetNumeric(10)
|
||||||
node.EnterSubnet(id)
|
node.EnterSubnet(id)
|
||||||
require.True(t, netmap.BelongsToSubnet(&node, id))
|
require.True(t, netmap.BelongsToSubnet(&node, id))
|
||||||
require.False(t, netmap.BelongsToSubnet(&node, subnetid.ID{}))
|
require.False(t, netmap.BelongsToSubnet(&node, subnetid.ID{}))
|
||||||
|
@ -113,8 +113,8 @@ func TestEnterSubnet(t *testing.T) {
|
||||||
func TestBelongsToSubnet(t *testing.T) {
|
func TestBelongsToSubnet(t *testing.T) {
|
||||||
var id, idMiss, idZero subnetid.ID
|
var id, idMiss, idZero subnetid.ID
|
||||||
|
|
||||||
id.SetNumber(13)
|
id.SetNumeric(13)
|
||||||
idMiss.SetNumber(23)
|
idMiss.SetNumeric(23)
|
||||||
|
|
||||||
var node netmap.NodeInfo
|
var node netmap.NodeInfo
|
||||||
|
|
||||||
|
|
10
subnet/doc.go
Normal file
10
subnet/doc.go
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
/*
|
||||||
|
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
|
10
subnet/id/doc.go
Normal file
10
subnet/id/doc.go
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
/*
|
||||||
|
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
|
121
subnet/id/id.go
121
subnet/id/id.go
|
@ -2,95 +2,104 @@ package subnetid
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
"github.com/nspcc-dev/neofs-api-go/v2/refs"
|
"github.com/nspcc-dev/neofs-api-go/v2/refs"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ID represents NeoFS subnet identifier.
|
// ID represents unique identifier of the subnet in the NeoFS network.
|
||||||
//
|
//
|
||||||
// The type is compatible with the corresponding message from NeoFS API V2 protocol.
|
// ID is mutually compatible with github.com/nspcc-dev/neofs-api-go/v2/refs.SubnetID
|
||||||
|
// message. See ReadFromV2 / WriteToV2 methods.
|
||||||
//
|
//
|
||||||
// Zero value and nil pointer is equivalent to zero subnet ID.
|
// Instances can be created using built-in var declaration. Zero value is
|
||||||
type ID refs.SubnetID
|
// equivalent to identifier of the zero subnet (whole NeoFS network).
|
||||||
|
type ID struct {
|
||||||
// FromV2 initializes ID from refs.SubnetID message structure. Must not be called on nil.
|
m refs.SubnetID
|
||||||
//
|
|
||||||
// Note: nil refs.SubnetID corresponds to zero ID value or nil pointer to it.
|
|
||||||
func (x *ID) FromV2(msg refs.SubnetID) {
|
|
||||||
*x = ID(msg)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// WriteToV2 writes ID to refs.SubnetID message structure. The message must not be nil.
|
// ReadFromV2 reads ID from the refs.SubnetID message. Checks if the
|
||||||
|
// message conforms to NeoFS API V2 protocol.
|
||||||
//
|
//
|
||||||
// Note: nil ID corresponds to zero refs.SubnetID value or nil pointer to it.
|
// 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) {
|
func (x ID) WriteToV2(msg *refs.SubnetID) {
|
||||||
*msg = refs.SubnetID(x)
|
*msg = x.m
|
||||||
}
|
}
|
||||||
|
|
||||||
// Equals returns true iff both instances identify the same subnet.
|
// Equals defines a comparison relation between two ID instances.
|
||||||
//
|
//
|
||||||
// Method is NPE-safe: nil pointer equals to pointer to zero value.
|
// Note that comparison using '==' operator is not recommended since it MAY result
|
||||||
func (x *ID) Equals(x2 *ID) bool {
|
// in loss of compatibility.
|
||||||
return (*refs.SubnetID)(x).GetValue() == (*refs.SubnetID)(x2).GetValue()
|
func (x ID) Equals(x2 ID) bool {
|
||||||
|
return x.m.GetValue() == x2.m.GetValue()
|
||||||
}
|
}
|
||||||
|
|
||||||
// MarshalText encodes ID into text format according to particular NeoFS API protocol.
|
// EncodeToString encodes ID into NeoFS API protocol string (base10 encoding).
|
||||||
// Supported versions:
|
|
||||||
// * V2 (see refs.SubnetID type).
|
|
||||||
//
|
//
|
||||||
// Implements encoding.TextMarshaler.
|
// See also DecodeString.
|
||||||
func (x *ID) MarshalText() ([]byte, error) {
|
func (x ID) EncodeToString() string {
|
||||||
return (*refs.SubnetID)(x).MarshalText()
|
return strconv.FormatUint(uint64(x.m.GetValue()), 10)
|
||||||
}
|
}
|
||||||
|
|
||||||
// UnmarshalText decodes ID from the text according to particular NeoFS API protocol.
|
// DecodeString decodes string calculated using EncodeToString. Returns
|
||||||
// Must not be called on nil. Supported versions:
|
// an error if s is malformed.
|
||||||
// * V2 (see refs.SubnetID type).
|
func (x *ID) DecodeString(s string) error {
|
||||||
//
|
num, err := strconv.ParseUint(s, 10, 32)
|
||||||
// Implements encoding.TextUnmarshaler.
|
|
||||||
func (x *ID) UnmarshalText(text []byte) error {
|
|
||||||
return (*refs.SubnetID)(x).UnmarshalText(text)
|
|
||||||
}
|
|
||||||
|
|
||||||
// String returns string representation of ID using MarshalText.
|
|
||||||
// Returns string with message on error.
|
|
||||||
//
|
|
||||||
// Implements fmt.Stringer.
|
|
||||||
func (x *ID) String() string {
|
|
||||||
text, err := x.MarshalText()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Sprintf("<invalid> %v", err)
|
return fmt.Errorf("invalid numeric value: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return string(text)
|
x.m.SetValue(uint32(num))
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Marshal encodes ID into a binary format of NeoFS API V2 protocol (Protocol Buffers with direct field order).
|
// String implements fmt.Stringer.
|
||||||
func (x *ID) Marshal() ([]byte, error) {
|
|
||||||
return (*refs.SubnetID)(x).StableMarshal(nil), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Unmarshal decodes ID from NeoFS API V2 binary format (see Marshal). Must not be called on nil.
|
|
||||||
//
|
//
|
||||||
// Note: empty data corresponds to zero ID value or nil pointer to it.
|
// 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 {
|
func (x *ID) Unmarshal(data []byte) error {
|
||||||
return (*refs.SubnetID)(x).Unmarshal(data)
|
return x.m.Unmarshal(data)
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetNumber sets ID value in uint32 format. Must not be called on nil.
|
// SetNumeric sets ID value in numeric format. By default, number is 0 which
|
||||||
// By default, number is 0 which refers to zero subnet.
|
// refers to the zero subnet.
|
||||||
func (x *ID) SetNumber(num uint32) {
|
func (x *ID) SetNumeric(num uint32) {
|
||||||
(*refs.SubnetID)(x).SetValue(num)
|
x.m.SetValue(num)
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsZero returns true iff the ID refers to zero subnet.
|
// IsZero compares id with zero subnet ID.
|
||||||
func IsZero(id ID) bool {
|
func IsZero(id ID) bool {
|
||||||
return id.Equals(nil)
|
return id.Equals(ID{})
|
||||||
}
|
}
|
||||||
|
|
||||||
// MakeZero makes ID to refer to zero subnet. Arg must not be nil (it is already zero).
|
// MakeZero makes ID to refer to zero subnet.
|
||||||
//
|
//
|
||||||
// Makes no sense to call on zero value (e.g. declared using var).
|
// Makes no sense to call on zero value (e.g. declared using var).
|
||||||
func MakeZero(id *ID) {
|
func MakeZero(id *ID) {
|
||||||
id.SetNumber(0)
|
id.SetNumeric(0)
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,26 +14,26 @@ func TestIsZero(t *testing.T) {
|
||||||
|
|
||||||
require.True(t, subnetid.IsZero(id))
|
require.True(t, subnetid.IsZero(id))
|
||||||
|
|
||||||
id.SetNumber(13)
|
id.SetNumeric(13)
|
||||||
require.False(t, subnetid.IsZero(id))
|
require.False(t, subnetid.IsZero(id))
|
||||||
|
|
||||||
id.SetNumber(0)
|
id.SetNumeric(0)
|
||||||
require.True(t, subnetid.IsZero(id))
|
require.True(t, subnetid.IsZero(id))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestID_FromV2(t *testing.T) {
|
func TestID_ReadFromV2(t *testing.T) {
|
||||||
const num = 13
|
const num = 13
|
||||||
|
|
||||||
var id1 subnetid.ID
|
var id1 subnetid.ID
|
||||||
id1.SetNumber(num)
|
id1.SetNumeric(num)
|
||||||
|
|
||||||
var idv2 refs.SubnetID
|
var idv2 refs.SubnetID
|
||||||
idv2.SetValue(num)
|
idv2.SetValue(num)
|
||||||
|
|
||||||
var id2 subnetid.ID
|
var id2 subnetid.ID
|
||||||
id2.FromV2(idv2)
|
require.NoError(t, id2.ReadFromV2(idv2))
|
||||||
|
|
||||||
require.True(t, id1.Equals(&id2))
|
require.True(t, id1.Equals(id2))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestID_WriteToV2(t *testing.T) {
|
func TestID_WriteToV2(t *testing.T) {
|
||||||
|
@ -47,7 +47,7 @@ func TestID_WriteToV2(t *testing.T) {
|
||||||
id.WriteToV2(&idv2)
|
id.WriteToV2(&idv2)
|
||||||
require.Zero(t, idv2.GetValue())
|
require.Zero(t, idv2.GetValue())
|
||||||
|
|
||||||
id.SetNumber(num)
|
id.SetNumeric(num)
|
||||||
|
|
||||||
id.WriteToV2(&idv2)
|
id.WriteToV2(&idv2)
|
||||||
require.EqualValues(t, num, idv2.GetValue())
|
require.EqualValues(t, num, idv2.GetValue())
|
||||||
|
@ -58,36 +58,30 @@ func TestID_Equals(t *testing.T) {
|
||||||
|
|
||||||
var id1, id2, idOther, id0 subnetid.ID
|
var id1, id2, idOther, id0 subnetid.ID
|
||||||
|
|
||||||
id0.Equals(nil)
|
id0.Equals(subnetid.ID{})
|
||||||
|
|
||||||
id1.SetNumber(num)
|
id1.SetNumeric(num)
|
||||||
id2.SetNumber(num)
|
id2.SetNumeric(num)
|
||||||
idOther.SetNumber(num + 1)
|
idOther.SetNumeric(num + 1)
|
||||||
|
|
||||||
require.True(t, id1.Equals(&id2))
|
require.True(t, id1.Equals(id2))
|
||||||
require.False(t, id1.Equals(&idOther))
|
require.False(t, id1.Equals(idOther))
|
||||||
require.False(t, id2.Equals(&idOther))
|
require.False(t, id2.Equals(idOther))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSubnetIDEncoding(t *testing.T) {
|
func TestSubnetIDEncoding(t *testing.T) {
|
||||||
id := subnetidtest.ID()
|
id := subnetidtest.ID()
|
||||||
|
|
||||||
t.Run("binary", func(t *testing.T) {
|
t.Run("binary", func(t *testing.T) {
|
||||||
data, err := id.Marshal()
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
var id2 subnetid.ID
|
var id2 subnetid.ID
|
||||||
require.NoError(t, id2.Unmarshal(data))
|
require.NoError(t, id2.Unmarshal(id.Marshal()))
|
||||||
|
|
||||||
require.True(t, id2.Equals(id))
|
require.True(t, id2.Equals(id))
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("text", func(t *testing.T) {
|
t.Run("text", func(t *testing.T) {
|
||||||
data, err := id.MarshalText()
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
var id2 subnetid.ID
|
var id2 subnetid.ID
|
||||||
require.NoError(t, id2.UnmarshalText(data))
|
require.NoError(t, id2.DecodeString(id.EncodeToString()))
|
||||||
|
|
||||||
require.True(t, id2.Equals(id))
|
require.True(t, id2.Equals(id))
|
||||||
})
|
})
|
||||||
|
@ -95,12 +89,12 @@ func TestSubnetIDEncoding(t *testing.T) {
|
||||||
|
|
||||||
func TestMakeZero(t *testing.T) {
|
func TestMakeZero(t *testing.T) {
|
||||||
var id subnetid.ID
|
var id subnetid.ID
|
||||||
|
id.SetNumeric(13)
|
||||||
id.SetNumber(13)
|
|
||||||
|
|
||||||
require.False(t, subnetid.IsZero(id))
|
require.False(t, subnetid.IsZero(id))
|
||||||
|
|
||||||
subnetid.MakeZero(&id)
|
subnetid.MakeZero(&id)
|
||||||
|
|
||||||
require.True(t, subnetid.IsZero(id))
|
require.True(t, subnetid.IsZero(id))
|
||||||
|
require.Equal(t, subnetid.ID{}, id)
|
||||||
}
|
}
|
||||||
|
|
13
subnet/id/test/doc.go
Normal file
13
subnet/id/test/doc.go
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
/*
|
||||||
|
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
|
|
@ -6,11 +6,8 @@ import (
|
||||||
subnetid "github.com/nspcc-dev/neofs-sdk-go/subnet/id"
|
subnetid "github.com/nspcc-dev/neofs-sdk-go/subnet/id"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ID generates and returns random subnetid.ID using math/rand.Uint32.
|
// ID generates and returns random subnetid.ID.
|
||||||
func ID() *subnetid.ID {
|
func ID() (x subnetid.ID) {
|
||||||
var id subnetid.ID
|
x.SetNumeric(rand.Uint32())
|
||||||
|
return
|
||||||
id.SetNumber(rand.Uint32())
|
|
||||||
|
|
||||||
return &id
|
|
||||||
}
|
}
|
||||||
|
|
155
subnet/subnet.go
155
subnet/subnet.go
|
@ -1,6 +1,8 @@
|
||||||
package subnet
|
package subnet
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
"github.com/nspcc-dev/neofs-api-go/v2/refs"
|
"github.com/nspcc-dev/neofs-api-go/v2/refs"
|
||||||
"github.com/nspcc-dev/neofs-api-go/v2/subnet"
|
"github.com/nspcc-dev/neofs-api-go/v2/subnet"
|
||||||
subnetid "github.com/nspcc-dev/neofs-sdk-go/subnet/id"
|
subnetid "github.com/nspcc-dev/neofs-sdk-go/subnet/id"
|
||||||
|
@ -9,102 +11,99 @@ import (
|
||||||
|
|
||||||
// Info represents information about NeoFS subnet.
|
// Info represents information about NeoFS subnet.
|
||||||
//
|
//
|
||||||
// The type is compatible with the corresponding message from NeoFS API V2 protocol.
|
// 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).
|
||||||
//
|
//
|
||||||
// Zero value and nil pointer to it represents zero subnet w/o an owner.
|
// See also Unmarshal.
|
||||||
type Info subnet.Info
|
func (x Info) Marshal() []byte {
|
||||||
|
var id refs.SubnetID
|
||||||
|
x.id.WriteToV2(&id)
|
||||||
|
|
||||||
// FromV2 initializes Info from subnet.Info message structure. Must not be called on nil.
|
var owner refs.OwnerID
|
||||||
func (x *Info) FromV2(msg subnet.Info) {
|
x.owner.WriteToV2(&owner)
|
||||||
*x = Info(msg)
|
|
||||||
|
var m subnet.Info
|
||||||
|
m.SetID(&id)
|
||||||
|
m.SetOwner(&owner)
|
||||||
|
|
||||||
|
return m.StableMarshal(nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
// WriteToV2 writes Info to subnet.Info message structure. The message must not be nil.
|
// Unmarshal decodes binary Info calculated using Marshal. Returns an error
|
||||||
func (x Info) WriteToV2(msg *subnet.Info) {
|
// describing a format violation.
|
||||||
*msg = subnet.Info(x)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Marshal encodes Info into a binary format of NeoFS API V2 protocol (Protocol Buffers with direct field order).
|
|
||||||
func (x *Info) Marshal() ([]byte, error) {
|
|
||||||
return (*subnet.Info)(x).StableMarshal(nil), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Unmarshal decodes Info from NeoFS API V2 binary format (see Marshal). Must not be called on nil.
|
|
||||||
//
|
|
||||||
// Note: empty data corresponds to zero Info value or nil pointer to it.
|
|
||||||
func (x *Info) Unmarshal(data []byte) error {
|
func (x *Info) Unmarshal(data []byte) error {
|
||||||
return (*subnet.Info)(x).Unmarshal(data)
|
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.
|
// SetID sets the identifier of the subnet that Info describes.
|
||||||
|
//
|
||||||
|
// See also ID.
|
||||||
func (x *Info) SetID(id subnetid.ID) {
|
func (x *Info) SetID(id subnetid.ID) {
|
||||||
infov2 := (*subnet.Info)(x)
|
x.id = id
|
||||||
|
|
||||||
idv2 := infov2.ID()
|
|
||||||
if idv2 == nil {
|
|
||||||
idv2 = new(refs.SubnetID)
|
|
||||||
infov2.SetID(idv2)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
id.WriteToV2(idv2)
|
// ID returns subnet identifier set using SetID.
|
||||||
|
//
|
||||||
|
// Zero Info refers to the zero subnet.
|
||||||
|
func (x Info) ID() subnetid.ID {
|
||||||
|
return x.id
|
||||||
}
|
}
|
||||||
|
|
||||||
// ReadID reads the identifier of the subnet that Info describes. Arg must not be nil.
|
// SetOwner sets identifier of the subnet owner.
|
||||||
func (x Info) ReadID(id *subnetid.ID) {
|
|
||||||
infov2 := (subnet.Info)(x)
|
|
||||||
|
|
||||||
idv2 := infov2.ID()
|
|
||||||
if idv2 == nil {
|
|
||||||
subnetid.MakeZero(id)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
id.FromV2(*idv2)
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetOwner sets subnet owner ID.
|
|
||||||
func (x *Info) SetOwner(id user.ID) {
|
func (x *Info) SetOwner(id user.ID) {
|
||||||
infov2 := (*subnet.Info)(x)
|
x.owner = id
|
||||||
|
|
||||||
idv2 := infov2.Owner()
|
|
||||||
if idv2 == nil {
|
|
||||||
idv2 = new(refs.OwnerID)
|
|
||||||
infov2.SetOwner(idv2)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
id.WriteToV2(idv2)
|
// 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
|
||||||
}
|
}
|
||||||
|
|
||||||
// ReadOwner reads the identifier of the subnet that Info describes.
|
// AssertOwnership checks if the given info describes the subnet owned by the
|
||||||
// Must be called only if owner is set (see HasOwner). Arg must not be nil.
|
// given user.
|
||||||
func (x Info) ReadOwner(id *user.ID) {
|
func AssertOwnership(info Info, id user.ID) bool {
|
||||||
infov2 := (subnet.Info)(x)
|
return id.Equals(info.Owner())
|
||||||
|
|
||||||
id2 := infov2.Owner()
|
|
||||||
if id2 == nil {
|
|
||||||
*id = user.ID{}
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if ownerV2 := infov2.Owner(); ownerV2 != nil {
|
// AssertReference checks if the given info describes the subnet referenced by
|
||||||
_ = id.ReadFromV2(*ownerV2)
|
// the given id.
|
||||||
}
|
func AssertReference(info Info, id subnetid.ID) bool {
|
||||||
}
|
return id.Equals(info.ID())
|
||||||
|
|
||||||
// IsOwner checks subnet ownership.
|
|
||||||
func IsOwner(info Info, id user.ID) bool {
|
|
||||||
var id2 user.ID
|
|
||||||
|
|
||||||
info.ReadOwner(&id2)
|
|
||||||
|
|
||||||
return id.Equals(id2)
|
|
||||||
}
|
|
||||||
|
|
||||||
// IDEquals checks if ID refers to subnet that Info describes.
|
|
||||||
func IDEquals(info Info, id subnetid.ID) bool {
|
|
||||||
id2 := new(subnetid.ID)
|
|
||||||
|
|
||||||
info.ReadID(id2)
|
|
||||||
|
|
||||||
return id.Equals(id2)
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,11 +3,10 @@ package subnet_test
|
||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
subnetv2 "github.com/nspcc-dev/neofs-api-go/v2/subnet"
|
|
||||||
subnettest "github.com/nspcc-dev/neofs-api-go/v2/subnet/test"
|
|
||||||
. "github.com/nspcc-dev/neofs-sdk-go/subnet"
|
. "github.com/nspcc-dev/neofs-sdk-go/subnet"
|
||||||
subnetid "github.com/nspcc-dev/neofs-sdk-go/subnet/id"
|
subnetid "github.com/nspcc-dev/neofs-sdk-go/subnet/id"
|
||||||
"github.com/nspcc-dev/neofs-sdk-go/user"
|
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"
|
usertest "github.com/nspcc-dev/neofs-sdk-go/user/test"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
@ -15,55 +14,35 @@ import (
|
||||||
func TestInfoZero(t *testing.T) {
|
func TestInfoZero(t *testing.T) {
|
||||||
var info Info
|
var info Info
|
||||||
|
|
||||||
var id subnetid.ID
|
require.Zero(t, info.ID())
|
||||||
info.ReadID(&id)
|
require.True(t, subnetid.IsZero(info.ID()))
|
||||||
|
|
||||||
require.True(t, subnetid.IsZero(id))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestInfo_SetID(t *testing.T) {
|
func TestInfo_SetID(t *testing.T) {
|
||||||
var (
|
id := subnetidtest.ID()
|
||||||
id subnetid.ID
|
|
||||||
info Info
|
|
||||||
)
|
|
||||||
|
|
||||||
id.SetNumber(222)
|
|
||||||
|
|
||||||
|
var info Info
|
||||||
info.SetID(id)
|
info.SetID(id)
|
||||||
|
|
||||||
require.True(t, IDEquals(info, id))
|
require.Equal(t, id, info.ID())
|
||||||
|
require.True(t, AssertReference(info, id))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestInfo_SetOwner(t *testing.T) {
|
func TestInfo_SetOwner(t *testing.T) {
|
||||||
var (
|
id := *usertest.ID()
|
||||||
id user.ID
|
|
||||||
info Info
|
|
||||||
)
|
|
||||||
|
|
||||||
id = *usertest.ID()
|
|
||||||
|
|
||||||
require.False(t, IsOwner(info, id))
|
|
||||||
|
|
||||||
|
var info Info
|
||||||
info.SetOwner(id)
|
info.SetOwner(id)
|
||||||
|
|
||||||
require.True(t, IsOwner(info, id))
|
require.Equal(t, id, info.Owner())
|
||||||
|
require.True(t, AssertOwnership(info, id))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestInfo_WriteToV2(t *testing.T) {
|
func TestInfo_Marshal(t *testing.T) {
|
||||||
var (
|
info := subnettest.Info()
|
||||||
infoTo, infoFrom Info
|
|
||||||
|
|
||||||
infoV2From, infoV2To subnetv2.Info
|
var info2 Info
|
||||||
)
|
require.NoError(t, info2.Unmarshal(info.Marshal()))
|
||||||
|
|
||||||
infoV2From = *subnettest.GenerateSubnetInfo(false)
|
require.Equal(t, info, info2)
|
||||||
|
|
||||||
infoFrom.FromV2(infoV2From)
|
|
||||||
|
|
||||||
infoFrom.WriteToV2(&infoV2To)
|
|
||||||
|
|
||||||
infoTo.FromV2(infoV2To)
|
|
||||||
|
|
||||||
require.Equal(t, infoV2From, infoV2To)
|
|
||||||
require.Equal(t, infoFrom, infoTo)
|
|
||||||
}
|
}
|
||||||
|
|
13
subnet/test/doc.go
Normal file
13
subnet/test/doc.go
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
/*
|
||||||
|
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
|
14
subnet/test/subnet.go
Normal file
14
subnet/test/subnet.go
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
package subnettest
|
||||||
|
|
||||||
|
import (
|
||||||
|
"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() (x subnet.Info) {
|
||||||
|
x.SetID(subnetidtest.ID())
|
||||||
|
x.SetOwner(*usertest.ID())
|
||||||
|
return
|
||||||
|
}
|
Loading…
Reference in a new issue