forked from TrueCloudLab/frostfs-sdk-go
[#245] storagegroup: Add Object <-> SG converters
Signed-off-by: Pavel Karpy <carpawell@nspcc.ru>
This commit is contained in:
parent
517d7a1e4a
commit
6709b00c89
2 changed files with 183 additions and 0 deletions
|
@ -1,9 +1,14 @@
|
||||||
package storagegroup
|
package storagegroup
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
objectV2 "github.com/nspcc-dev/neofs-api-go/v2/object"
|
||||||
"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/storagegroup"
|
"github.com/nspcc-dev/neofs-api-go/v2/storagegroup"
|
||||||
"github.com/nspcc-dev/neofs-sdk-go/checksum"
|
"github.com/nspcc-dev/neofs-sdk-go/checksum"
|
||||||
|
objectSDK "github.com/nspcc-dev/neofs-sdk-go/object"
|
||||||
oid "github.com/nspcc-dev/neofs-sdk-go/object/id"
|
oid "github.com/nspcc-dev/neofs-sdk-go/object/id"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -205,3 +210,87 @@ func formatCheck(v2 *storagegroup.StorageGroup) error {
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ReadFromObject assemble StorageGroup from a regular
|
||||||
|
// Object structure. Object must contain unambiguous information
|
||||||
|
// about its expiration epoch, otherwise behaviour is undefined.
|
||||||
|
//
|
||||||
|
// Returns any error appeared during storage group parsing; returns
|
||||||
|
// error if object is not of TypeStorageGroup type.
|
||||||
|
func ReadFromObject(sg *StorageGroup, o objectSDK.Object) error {
|
||||||
|
if typ := o.Type(); typ != objectSDK.TypeStorageGroup {
|
||||||
|
return fmt.Errorf("object is not of StorageGroup type: %s", typ)
|
||||||
|
}
|
||||||
|
|
||||||
|
err := sg.Unmarshal(o.Payload())
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("could not unmarshal object: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var expObj uint64
|
||||||
|
|
||||||
|
for _, attr := range o.Attributes() {
|
||||||
|
if attr.Key() == objectV2.SysAttributeExpEpoch {
|
||||||
|
expObj, err = strconv.ParseUint(attr.Value(), 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("could not get expiration from object: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Supporting deprecated functionality.
|
||||||
|
// See https://github.com/nspcc-dev/neofs-api/pull/205.
|
||||||
|
if expSG := sg.ExpirationEpoch(); expObj != expSG {
|
||||||
|
return fmt.Errorf(
|
||||||
|
"expiration does not match: from object: %d, from payload: %d",
|
||||||
|
expObj, expSG)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// WriteToObject writes StorageGroup to a regular
|
||||||
|
// Object structure. Object must not contain ambiguous
|
||||||
|
// information about its expiration epoch or must not
|
||||||
|
// have it at all.
|
||||||
|
//
|
||||||
|
// Written information:
|
||||||
|
// * expiration epoch;
|
||||||
|
// * object type (TypeStorageGroup);
|
||||||
|
// * raw payload.
|
||||||
|
func WriteToObject(sg StorageGroup, o *objectSDK.Object) {
|
||||||
|
sgRaw, err := sg.Marshal()
|
||||||
|
if err != nil {
|
||||||
|
// Marshal() does not return errors
|
||||||
|
// in the next API release
|
||||||
|
panic(fmt.Errorf("could not marshal storage group: %w", err))
|
||||||
|
}
|
||||||
|
|
||||||
|
o.SetPayload(sgRaw)
|
||||||
|
o.SetType(objectSDK.TypeStorageGroup)
|
||||||
|
|
||||||
|
attrs := o.Attributes()
|
||||||
|
var expAttrFound bool
|
||||||
|
|
||||||
|
for i := range attrs {
|
||||||
|
if attrs[i].Key() == objectV2.SysAttributeExpEpoch {
|
||||||
|
expAttrFound = true
|
||||||
|
attrs[i].SetValue(strconv.FormatUint(sg.ExpirationEpoch(), 10))
|
||||||
|
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !expAttrFound {
|
||||||
|
var attr objectSDK.Attribute
|
||||||
|
|
||||||
|
attr.SetKey(objectV2.SysAttributeExpEpoch)
|
||||||
|
attr.SetValue(strconv.FormatUint(sg.ExpirationEpoch(), 10))
|
||||||
|
|
||||||
|
attrs = append(attrs, attr)
|
||||||
|
}
|
||||||
|
|
||||||
|
o.SetAttributes(attrs...)
|
||||||
|
}
|
||||||
|
|
|
@ -2,13 +2,16 @@ package storagegroup_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/sha256"
|
"crypto/sha256"
|
||||||
|
"strconv"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
objectV2 "github.com/nspcc-dev/neofs-api-go/v2/object"
|
||||||
"github.com/nspcc-dev/neofs-api-go/v2/refs"
|
"github.com/nspcc-dev/neofs-api-go/v2/refs"
|
||||||
storagegroupV2 "github.com/nspcc-dev/neofs-api-go/v2/storagegroup"
|
storagegroupV2 "github.com/nspcc-dev/neofs-api-go/v2/storagegroup"
|
||||||
storagegroupV2test "github.com/nspcc-dev/neofs-api-go/v2/storagegroup/test"
|
storagegroupV2test "github.com/nspcc-dev/neofs-api-go/v2/storagegroup/test"
|
||||||
"github.com/nspcc-dev/neofs-sdk-go/checksum"
|
"github.com/nspcc-dev/neofs-sdk-go/checksum"
|
||||||
checksumtest "github.com/nspcc-dev/neofs-sdk-go/checksum/test"
|
checksumtest "github.com/nspcc-dev/neofs-sdk-go/checksum/test"
|
||||||
|
objectSDK "github.com/nspcc-dev/neofs-sdk-go/object"
|
||||||
oid "github.com/nspcc-dev/neofs-sdk-go/object/id"
|
oid "github.com/nspcc-dev/neofs-sdk-go/object/id"
|
||||||
oidtest "github.com/nspcc-dev/neofs-sdk-go/object/id/test"
|
oidtest "github.com/nspcc-dev/neofs-sdk-go/object/id/test"
|
||||||
"github.com/nspcc-dev/neofs-sdk-go/storagegroup"
|
"github.com/nspcc-dev/neofs-sdk-go/storagegroup"
|
||||||
|
@ -193,3 +196,94 @@ func TestStorageGroup_SetMembers_DoubleSetting(t *testing.T) {
|
||||||
// and apply update correctly
|
// and apply update correctly
|
||||||
sg.SetMembers(mm[:1])
|
sg.SetMembers(mm[:1])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestStorageGroupFromObject(t *testing.T) {
|
||||||
|
sg := storagegrouptest.StorageGroup()
|
||||||
|
|
||||||
|
var o objectSDK.Object
|
||||||
|
|
||||||
|
var expAttr objectSDK.Attribute
|
||||||
|
expAttr.SetKey(objectV2.SysAttributeExpEpoch)
|
||||||
|
expAttr.SetValue(strconv.FormatUint(sg.ExpirationEpoch(), 10))
|
||||||
|
|
||||||
|
sgRaw, err := sg.Marshal()
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
o.SetPayload(sgRaw)
|
||||||
|
o.SetType(objectSDK.TypeStorageGroup)
|
||||||
|
|
||||||
|
t.Run("correct object", func(t *testing.T) {
|
||||||
|
o.SetAttributes(objectSDK.Attribute{}, expAttr, objectSDK.Attribute{})
|
||||||
|
|
||||||
|
var sg2 storagegroup.StorageGroup
|
||||||
|
require.NoError(t, storagegroup.ReadFromObject(&sg2, o))
|
||||||
|
require.Equal(t, sg, sg2)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("incorrect exp attr", func(t *testing.T) {
|
||||||
|
var sg2 storagegroup.StorageGroup
|
||||||
|
|
||||||
|
expAttr.SetValue(strconv.FormatUint(sg.ExpirationEpoch()+1, 10))
|
||||||
|
o.SetAttributes(expAttr)
|
||||||
|
|
||||||
|
require.Error(t, storagegroup.ReadFromObject(&sg2, o))
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("incorrect object type", func(t *testing.T) {
|
||||||
|
var sg2 storagegroup.StorageGroup
|
||||||
|
|
||||||
|
o.SetType(objectSDK.TypeTombstone)
|
||||||
|
require.Error(t, storagegroup.ReadFromObject(&sg2, o))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestStorageGroupToObject(t *testing.T) {
|
||||||
|
sg := storagegrouptest.StorageGroup()
|
||||||
|
|
||||||
|
sgRaw, err := sg.Marshal()
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
t.Run("empty object", func(t *testing.T) {
|
||||||
|
var o objectSDK.Object
|
||||||
|
storagegroup.WriteToObject(sg, &o)
|
||||||
|
|
||||||
|
exp, found := expFromObj(t, o)
|
||||||
|
require.True(t, found)
|
||||||
|
|
||||||
|
require.Equal(t, sgRaw, o.Payload())
|
||||||
|
require.Equal(t, sg.ExpirationEpoch(), exp)
|
||||||
|
require.Equal(t, objectSDK.TypeStorageGroup, o.Type())
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("obj already has exp attr", func(t *testing.T) {
|
||||||
|
var o objectSDK.Object
|
||||||
|
|
||||||
|
var attr objectSDK.Attribute
|
||||||
|
attr.SetKey(objectV2.SysAttributeExpEpoch)
|
||||||
|
attr.SetValue(strconv.FormatUint(sg.ExpirationEpoch()+1, 10))
|
||||||
|
|
||||||
|
o.SetAttributes(objectSDK.Attribute{}, attr, objectSDK.Attribute{})
|
||||||
|
|
||||||
|
storagegroup.WriteToObject(sg, &o)
|
||||||
|
|
||||||
|
exp, found := expFromObj(t, o)
|
||||||
|
require.True(t, found)
|
||||||
|
|
||||||
|
require.Equal(t, sgRaw, o.Payload())
|
||||||
|
require.Equal(t, sg.ExpirationEpoch(), exp)
|
||||||
|
require.Equal(t, objectSDK.TypeStorageGroup, o.Type())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func expFromObj(t *testing.T, o objectSDK.Object) (uint64, bool) {
|
||||||
|
for _, attr := range o.Attributes() {
|
||||||
|
if attr.Key() == objectV2.SysAttributeExpEpoch {
|
||||||
|
exp, err := strconv.ParseUint(attr.Value(), 10, 64)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
return exp, true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0, false
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue