forked from TrueCloudLab/frostfs-sdk-go
[#53] sdk-go: Drop subnet
Signed-off-by: Dmitrii Stepanov <d.stepanov@yadro.com>
This commit is contained in:
parent
591dd1247d
commit
c8e620ad24
30 changed files with 38 additions and 1062 deletions
|
@ -16,7 +16,6 @@ import (
|
|||
frostfscrypto "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/crypto"
|
||||
frostfsecdsa "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/crypto/ecdsa"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/netmap"
|
||||
subnetid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/subnet/id"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/user"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/version"
|
||||
"github.com/google/uuid"
|
||||
|
@ -128,10 +127,7 @@ func checkAttributes(m container.Container) error {
|
|||
}
|
||||
|
||||
var err error
|
||||
switch key {
|
||||
case container.SysAttributeSubnet:
|
||||
err = new(subnetid.ID).DecodeString(val)
|
||||
case attributeTimestamp:
|
||||
if key == attributeTimestamp {
|
||||
_, err = strconv.ParseInt(val, 10, 64)
|
||||
}
|
||||
|
||||
|
@ -391,28 +387,6 @@ func CreatedAt(cnr Container) time.Time {
|
|||
return time.Unix(sec, 0)
|
||||
}
|
||||
|
||||
// SetSubnet places the Container on the specified FrostFS 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
|
||||
|
|
|
@ -15,8 +15,6 @@ import (
|
|||
containertest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/test"
|
||||
frostfscrypto "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/crypto"
|
||||
netmaptest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/netmap/test"
|
||||
subnetid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/subnet/id"
|
||||
subnetidtest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/subnet/id/test"
|
||||
usertest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/user/test"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/version"
|
||||
"github.com/google/uuid"
|
||||
|
@ -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()
|
||||
|
||||
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
|
||||
|
||||
|
|
|
@ -85,8 +85,7 @@
|
|||
"filter": "*"
|
||||
}
|
||||
],
|
||||
"filters": [],
|
||||
"subnetId": null
|
||||
"filters": []
|
||||
},
|
||||
"result": [
|
||||
[
|
||||
|
|
|
@ -57,8 +57,7 @@
|
|||
"filter": "*"
|
||||
}
|
||||
],
|
||||
"filters": [],
|
||||
"subnetId": null
|
||||
"filters": []
|
||||
},
|
||||
"result": [
|
||||
[
|
||||
|
@ -86,8 +85,7 @@
|
|||
"filter": "*"
|
||||
}
|
||||
],
|
||||
"filters": [],
|
||||
"subnetId": null
|
||||
"filters": []
|
||||
},
|
||||
"result": [
|
||||
[
|
||||
|
|
|
@ -61,8 +61,7 @@
|
|||
],
|
||||
"containerBackupFactor": 0,
|
||||
"selectors": [],
|
||||
"filters": [],
|
||||
"subnetId": null
|
||||
"filters": []
|
||||
},
|
||||
"result": [
|
||||
[
|
||||
|
@ -83,8 +82,7 @@
|
|||
],
|
||||
"containerBackupFactor": 3,
|
||||
"selectors": [],
|
||||
"filters": [],
|
||||
"subnetId": null
|
||||
"filters": []
|
||||
},
|
||||
"result": [
|
||||
[
|
||||
|
@ -113,8 +111,7 @@
|
|||
"filter": "*"
|
||||
}
|
||||
],
|
||||
"filters": [],
|
||||
"subnetId": null
|
||||
"filters": []
|
||||
},
|
||||
"result": [
|
||||
[
|
||||
|
@ -143,8 +140,7 @@
|
|||
"filter": "*"
|
||||
}
|
||||
],
|
||||
"filters": [],
|
||||
"subnetId": null
|
||||
"filters": []
|
||||
},
|
||||
"result": [
|
||||
[
|
||||
|
|
|
@ -107,8 +107,7 @@
|
|||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"subnetId": null
|
||||
]
|
||||
},
|
||||
"result": [
|
||||
[
|
||||
|
@ -200,8 +199,7 @@
|
|||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"subnetId": null
|
||||
]
|
||||
},
|
||||
"error": "not enough nodes"
|
||||
},
|
||||
|
@ -289,8 +287,7 @@
|
|||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"subnetId": null
|
||||
]
|
||||
},
|
||||
"error": "not enough nodes"
|
||||
},
|
||||
|
@ -378,8 +375,7 @@
|
|||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"subnetId": null
|
||||
]
|
||||
},
|
||||
"error": "not enough nodes"
|
||||
}
|
||||
|
|
|
@ -41,8 +41,7 @@
|
|||
"value": "4",
|
||||
"filters": []
|
||||
}
|
||||
],
|
||||
"subnetId": null
|
||||
]
|
||||
},
|
||||
"result": [
|
||||
[
|
||||
|
@ -76,8 +75,7 @@
|
|||
"value": "5",
|
||||
"filters": []
|
||||
}
|
||||
],
|
||||
"subnetId": null
|
||||
]
|
||||
},
|
||||
"error": "not enough nodes"
|
||||
},
|
||||
|
@ -107,8 +105,7 @@
|
|||
"value": "3",
|
||||
"filters": []
|
||||
}
|
||||
],
|
||||
"subnetId": null
|
||||
]
|
||||
},
|
||||
"result": [
|
||||
[
|
||||
|
@ -142,8 +139,7 @@
|
|||
"value": "4",
|
||||
"filters": []
|
||||
}
|
||||
],
|
||||
"subnetId": null
|
||||
]
|
||||
},
|
||||
"error": "not enough nodes"
|
||||
},
|
||||
|
@ -173,8 +169,7 @@
|
|||
"value": "4",
|
||||
"filters": []
|
||||
}
|
||||
],
|
||||
"subnetId": null
|
||||
]
|
||||
},
|
||||
"result": [
|
||||
[
|
||||
|
@ -208,8 +203,7 @@
|
|||
"value": "3",
|
||||
"filters": []
|
||||
}
|
||||
],
|
||||
"subnetId": null
|
||||
]
|
||||
},
|
||||
"error": "not enough nodes"
|
||||
},
|
||||
|
@ -239,8 +233,7 @@
|
|||
"value": "5",
|
||||
"filters": []
|
||||
}
|
||||
],
|
||||
"subnetId": null
|
||||
]
|
||||
},
|
||||
"result": [
|
||||
[
|
||||
|
@ -274,8 +267,7 @@
|
|||
"value": "4",
|
||||
"filters": []
|
||||
}
|
||||
],
|
||||
"subnetId": null
|
||||
]
|
||||
},
|
||||
"error": "not enough nodes"
|
||||
},
|
||||
|
@ -305,8 +297,7 @@
|
|||
"value": "Germany",
|
||||
"filters": []
|
||||
}
|
||||
],
|
||||
"subnetId": null
|
||||
]
|
||||
},
|
||||
"result": [
|
||||
[
|
||||
|
@ -340,8 +331,7 @@
|
|||
"value": "China",
|
||||
"filters": []
|
||||
}
|
||||
],
|
||||
"subnetId": null
|
||||
]
|
||||
},
|
||||
"error": "not enough nodes"
|
||||
},
|
||||
|
@ -371,8 +361,7 @@
|
|||
"value": "France",
|
||||
"filters": []
|
||||
}
|
||||
],
|
||||
"subnetId": null
|
||||
]
|
||||
},
|
||||
"result": [
|
||||
[
|
||||
|
@ -406,8 +395,7 @@
|
|||
"value": "Germany",
|
||||
"filters": []
|
||||
}
|
||||
],
|
||||
"subnetId": null
|
||||
]
|
||||
},
|
||||
"error": "not enough nodes"
|
||||
}
|
||||
|
|
|
@ -144,7 +144,7 @@
|
|||
],
|
||||
"tests": {
|
||||
"select 3 nodes in 3 distinct countries, same placement": {
|
||||
"policy": {"replicas":[{"count":1,"selector":"Main"}],"containerBackupFactor":1,"selectors":[{"name":"Main","count":3,"clause":"DISTINCT","attribute":"Country","filter":"*"}],"filters":[],"subnetId":null},
|
||||
"policy": {"replicas":[{"count":1,"selector":"Main"}],"containerBackupFactor":1,"selectors":[{"name":"Main","count":3,"clause":"DISTINCT","attribute":"Country","filter":"*"}],"filters":[]},
|
||||
"pivot": "Y29udGFpbmVySUQ=",
|
||||
"result": [[4, 0, 7]],
|
||||
"placement": {
|
||||
|
@ -153,7 +153,7 @@
|
|||
}
|
||||
},
|
||||
"select 6 nodes in 3 distinct countries, different placement": {
|
||||
"policy": {"replicas":[{"count":1,"selector":"Main"}],"containerBackupFactor":2,"selectors":[{"name":"Main","count":3,"clause":"DISTINCT","attribute":"Country","filter":"*"}],"filters":[],"subnetId":null},
|
||||
"policy": {"replicas":[{"count":1,"selector":"Main"}],"containerBackupFactor":2,"selectors":[{"name":"Main","count":3,"clause":"DISTINCT","attribute":"Country","filter":"*"}],"filters":[]},
|
||||
"pivot": "Y29udGFpbmVySUQ=",
|
||||
"result": [[4, 3, 0, 1, 7, 2]],
|
||||
"placement": {
|
||||
|
|
|
@ -93,8 +93,7 @@
|
|||
"value": "Europe",
|
||||
"filters": []
|
||||
}
|
||||
],
|
||||
"subnetId": null
|
||||
]
|
||||
},
|
||||
"result": [
|
||||
[
|
||||
|
|
|
@ -79,8 +79,7 @@
|
|||
"value": "Moscow",
|
||||
"filters": []
|
||||
}
|
||||
],
|
||||
"subnetId": null
|
||||
]
|
||||
},
|
||||
"result": [
|
||||
[
|
||||
|
|
|
@ -312,8 +312,7 @@
|
|||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"subnetId": null
|
||||
]
|
||||
},
|
||||
"result": [
|
||||
[
|
||||
|
|
|
@ -61,8 +61,7 @@
|
|||
],
|
||||
"containerBackupFactor": 0,
|
||||
"selectors": [],
|
||||
"filters": [],
|
||||
"subnetId": null
|
||||
"filters": []
|
||||
},
|
||||
"result": [
|
||||
[
|
||||
|
@ -82,8 +81,7 @@
|
|||
],
|
||||
"containerBackupFactor": 0,
|
||||
"selectors": [],
|
||||
"filters": [],
|
||||
"subnetId": null
|
||||
"filters": []
|
||||
},
|
||||
"result": [
|
||||
[
|
||||
|
@ -104,8 +102,7 @@
|
|||
],
|
||||
"containerBackupFactor": 0,
|
||||
"selectors": [],
|
||||
"filters": [],
|
||||
"subnetId": null
|
||||
"filters": []
|
||||
},
|
||||
"error": "not enough nodes"
|
||||
}
|
||||
|
|
|
@ -101,8 +101,7 @@
|
|||
"filter": "*"
|
||||
}
|
||||
],
|
||||
"filters": [],
|
||||
"subnetId": null
|
||||
"filters": []
|
||||
},
|
||||
"result": [
|
||||
[
|
||||
|
|
|
@ -43,8 +43,7 @@
|
|||
"value": "Russia",
|
||||
"filters": []
|
||||
}
|
||||
],
|
||||
"subnetId": null
|
||||
]
|
||||
},
|
||||
"error": "filter not found"
|
||||
},
|
||||
|
@ -69,8 +68,7 @@
|
|||
"value": "Russia",
|
||||
"filters": []
|
||||
}
|
||||
],
|
||||
"subnetId": null
|
||||
]
|
||||
},
|
||||
"error": "not enough nodes"
|
||||
},
|
||||
|
@ -95,8 +93,7 @@
|
|||
"value": "Russia",
|
||||
"filters": []
|
||||
}
|
||||
],
|
||||
"subnetId": null
|
||||
]
|
||||
},
|
||||
"error": "not enough nodes"
|
||||
}
|
||||
|
|
|
@ -1,254 +0,0 @@
|
|||
{
|
||||
"name": "subnet tests",
|
||||
"nodes": [
|
||||
{
|
||||
"attributes": [
|
||||
{
|
||||
"key": "ID",
|
||||
"value": "0"
|
||||
},
|
||||
{
|
||||
"key": "City",
|
||||
"value": "Paris"
|
||||
},
|
||||
{
|
||||
"key": "__SYSTEM__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": "__SYSTEM__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": "__SYSTEM__SUBNET_1",
|
||||
"value": "True"
|
||||
}
|
||||
],
|
||||
"state": "UNSPECIFIED"
|
||||
},
|
||||
{
|
||||
"attributes": [
|
||||
{
|
||||
"key": "ID",
|
||||
"value": "5"
|
||||
},
|
||||
{
|
||||
"key": "City",
|
||||
"value": "Toronto"
|
||||
},
|
||||
{
|
||||
"key": "__SYSTEM__SUBNET_2",
|
||||
"value": "True"
|
||||
}
|
||||
],
|
||||
"state": "UNSPECIFIED"
|
||||
},
|
||||
{
|
||||
"attributes": [
|
||||
{
|
||||
"key": "ID",
|
||||
"value": "6"
|
||||
},
|
||||
{
|
||||
"key": "City",
|
||||
"value": "Tokyo"
|
||||
},
|
||||
{
|
||||
"key": "__SYSTEM__SUBNET_2",
|
||||
"value": "True"
|
||||
}
|
||||
],
|
||||
"state": "UNSPECIFIED"
|
||||
},
|
||||
{
|
||||
"attributes": [
|
||||
{
|
||||
"key": "ID",
|
||||
"value": "7"
|
||||
},
|
||||
{
|
||||
"key": "City",
|
||||
"value": "Tokyo"
|
||||
},
|
||||
{
|
||||
"key": "__SYSTEM__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": "__SYSTEM__SUBNET.2.ENABLED",
|
||||
"op": "EQ",
|
||||
"value": "True"
|
||||
}
|
||||
]
|
||||
},
|
||||
"error": "not enough nodes"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -8,9 +8,7 @@ import (
|
|||
"strings"
|
||||
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/netmap"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/refs"
|
||||
frostfscrypto "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/crypto"
|
||||
subnetid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/subnet/id"
|
||||
"git.frostfs.info/TrueCloudLab/hrw"
|
||||
)
|
||||
|
||||
|
@ -54,8 +52,6 @@ func (x *NodeInfo) readFromV2(m netmap.NodeInfo, checkFieldPresence bool) error
|
|||
return fmt.Errorf("duplicated attbiuted %s", key)
|
||||
}
|
||||
|
||||
const subnetPrefix = "__SYSTEM__SUBNET_"
|
||||
|
||||
switch {
|
||||
case key == attrCapacity:
|
||||
_, err = strconv.ParseUint(attributes[i].GetValue(), 10, 64)
|
||||
|
@ -68,17 +64,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)
|
||||
|
@ -484,81 +469,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.
|
||||
|
|
|
@ -8,9 +8,7 @@ import (
|
|||
"strings"
|
||||
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/netmap"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/refs"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/netmap/parser"
|
||||
subnetid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/subnet/id"
|
||||
"github.com/antlr/antlr4/runtime/Go/antlr/v4"
|
||||
)
|
||||
|
||||
|
@ -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 {
|
||||
|
|
|
@ -5,7 +5,6 @@ import (
|
|||
"sort"
|
||||
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/netmap"
|
||||
subnetid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/subnet/id"
|
||||
"git.frostfs.info/TrueCloudLab/hrw"
|
||||
)
|
||||
|
||||
|
@ -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"
|
||||
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/netmap"
|
||||
subnetid "git.frostfs.info/TrueCloudLab/frostfs-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"
|
||||
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/netmap"
|
||||
subnetidtest "git.frostfs.info/TrueCloudLab/frostfs-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 FrostFS subnets.
|
||||
|
||||
Subnet of a particular FrostFS 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 FrostFS Sidechain.
|
||||
*/
|
||||
package subnet
|
|
@ -1,9 +0,0 @@
|
|||
/*
|
||||
Package subnetid provides primitives to work with subnet identification in FrostFS.
|
||||
|
||||
ID type is used for global subnet identity inside the FrostFS 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"
|
||||
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/refs"
|
||||
)
|
||||
|
||||
// ID represents unique identifier of the subnet in the FrostFS network.
|
||||
//
|
||||
// ID is mutually compatible with git.frostfs.info/TrueCloudLab/frostfs-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 FrostFS network).
|
||||
type ID struct {
|
||||
m refs.SubnetID
|
||||
}
|
||||
|
||||
// ReadFromV2 reads ID from the refs.SubnetID message. Checks if the
|
||||
// message conforms to FrostFS 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 FrostFS 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 FrostFS protocol string.
|
||||
func (x ID) String() string {
|
||||
return "#" + strconv.FormatUint(uint64(x.m.GetValue()), 10)
|
||||
}
|
||||
|
||||
// Marshal encodes ID into a binary format of the FrostFS 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"
|
||||
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/refs"
|
||||
subnetid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/subnet/id"
|
||||
subnetidtest "git.frostfs.info/TrueCloudLab/frostfs-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 "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/suibnet/id/test"
|
||||
|
||||
value := subnetidtest.ID()
|
||||
// test the value
|
||||
*/
|
||||
package subnetidtest
|
|
@ -1,13 +0,0 @@
|
|||
package subnetidtest
|
||||
|
||||
import (
|
||||
"math/rand"
|
||||
|
||||
subnetid "git.frostfs.info/TrueCloudLab/frostfs-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"
|
||||
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/refs"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/subnet"
|
||||
subnetid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/subnet/id"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/user"
|
||||
)
|
||||
|
||||
// Info represents information about FrostFS 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 FrostFS 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
|
||||
// FrostFS 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"
|
||||
|
||||
. "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/subnet"
|
||||
subnetid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/subnet/id"
|
||||
subnetidtest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/subnet/id/test"
|
||||
subnettest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/subnet/test"
|
||||
usertest "git.frostfs.info/TrueCloudLab/frostfs-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()
|
||||
|
||||
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()
|
||||
|
||||
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 "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/suibnet/test"
|
||||
|
||||
value := subnettest.Info()
|
||||
// test the value
|
||||
*/
|
||||
package subnettest
|
|
@ -1,14 +0,0 @@
|
|||
package subnettest
|
||||
|
||||
import (
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/subnet"
|
||||
subnetidtest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/subnet/id/test"
|
||||
usertest "git.frostfs.info/TrueCloudLab/frostfs-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