From 73686827d36d02a158de693e013f60cbd533b1ab Mon Sep 17 00:00:00 2001 From: Evgenii Stratonikov Date: Mon, 8 Nov 2021 15:25:13 +0300 Subject: [PATCH] [#60] container: move package from neofs-api-go Also, remove deprecated methods and types. Close #60. Signed-off-by: Evgenii Stratonikov --- container/announcement.go | 77 ++++++++++++ container/announcement_test.go | 99 ++++++++++++++++ container/attribute.go | 137 +++++++++++++++++++++ container/attribute_test.go | 155 ++++++++++++++++++++++++ container/container.go | 191 ++++++++++++++++++++++++++++++ container/container_test.go | 135 +++++++++++++++++++++ container/opts.go | 104 ++++++++++++++++ container/test/generate.go | 48 ++++++++ container/wellknown_attributes.go | 11 ++ netmap/test/generate.go | 55 +++++++++ 10 files changed, 1012 insertions(+) create mode 100644 container/announcement.go create mode 100644 container/announcement_test.go create mode 100644 container/attribute.go create mode 100644 container/attribute_test.go create mode 100644 container/container.go create mode 100644 container/container_test.go create mode 100644 container/opts.go create mode 100644 container/test/generate.go create mode 100644 container/wellknown_attributes.go diff --git a/container/announcement.go b/container/announcement.go new file mode 100644 index 00000000..1e41cf84 --- /dev/null +++ b/container/announcement.go @@ -0,0 +1,77 @@ +package container + +import ( + "github.com/nspcc-dev/neofs-api-go/v2/container" + cid "github.com/nspcc-dev/neofs-sdk-go/container/id" +) + +// UsedSpaceAnnouncement is an announcement message used by storage nodes to +// estimate actual container sizes. +type UsedSpaceAnnouncement container.UsedSpaceAnnouncement + +// NewAnnouncement initialize empty UsedSpaceAnnouncement message. +// +// Defaults: +// - epoch: 0; +// - usedSpace: 0; +// - cid: nil. +func NewAnnouncement() *UsedSpaceAnnouncement { + return NewAnnouncementFromV2(new(container.UsedSpaceAnnouncement)) +} + +// NewAnnouncementFromV2 wraps protocol dependent version of +// UsedSpaceAnnouncement message. +// +// Nil container.UsedSpaceAnnouncement converts to nil. +func NewAnnouncementFromV2(v *container.UsedSpaceAnnouncement) *UsedSpaceAnnouncement { + return (*UsedSpaceAnnouncement)(v) +} + +// Epoch of the announcement. +func (a *UsedSpaceAnnouncement) Epoch() uint64 { + return (*container.UsedSpaceAnnouncement)(a).GetEpoch() +} + +// SetEpoch sets announcement epoch value. +func (a *UsedSpaceAnnouncement) SetEpoch(epoch uint64) { + (*container.UsedSpaceAnnouncement)(a).SetEpoch(epoch) +} + +// ContainerID of the announcement. +func (a *UsedSpaceAnnouncement) ContainerID() *cid.ID { + return cid.NewFromV2( + (*container.UsedSpaceAnnouncement)(a).GetContainerID(), + ) +} + +// SetContainerID sets announcement container value. +func (a *UsedSpaceAnnouncement) SetContainerID(cid *cid.ID) { + (*container.UsedSpaceAnnouncement)(a).SetContainerID(cid.ToV2()) +} + +// UsedSpace in container. +func (a *UsedSpaceAnnouncement) UsedSpace() uint64 { + return (*container.UsedSpaceAnnouncement)(a).GetUsedSpace() +} + +// SetUsedSpace sets used space value by specified container. +func (a *UsedSpaceAnnouncement) SetUsedSpace(value uint64) { + (*container.UsedSpaceAnnouncement)(a).SetUsedSpace(value) +} + +// ToV2 returns protocol dependent version of UsedSpaceAnnouncement message. +// +// Nil UsedSpaceAnnouncement converts to nil. +func (a *UsedSpaceAnnouncement) ToV2() *container.UsedSpaceAnnouncement { + return (*container.UsedSpaceAnnouncement)(a) +} + +// Marshal marshals UsedSpaceAnnouncement into a protobuf binary form. +func (a *UsedSpaceAnnouncement) Marshal() ([]byte, error) { + return a.ToV2().StableMarshal(nil) +} + +// Unmarshal unmarshals protobuf binary representation of UsedSpaceAnnouncement. +func (a *UsedSpaceAnnouncement) Unmarshal(data []byte) error { + return a.ToV2().Unmarshal(data) +} diff --git a/container/announcement_test.go b/container/announcement_test.go new file mode 100644 index 00000000..00b62b4b --- /dev/null +++ b/container/announcement_test.go @@ -0,0 +1,99 @@ +package container_test + +import ( + "crypto/sha256" + "testing" + + containerv2 "github.com/nspcc-dev/neofs-api-go/v2/container" + "github.com/nspcc-dev/neofs-api-go/v2/refs" + "github.com/nspcc-dev/neofs-sdk-go/container" + cid "github.com/nspcc-dev/neofs-sdk-go/container/id" + cidtest "github.com/nspcc-dev/neofs-sdk-go/container/id/test" + containertest "github.com/nspcc-dev/neofs-sdk-go/container/test" + "github.com/stretchr/testify/require" +) + +func TestAnnouncement(t *testing.T) { + const epoch, usedSpace uint64 = 10, 100 + + cidValue := [sha256.Size]byte{1, 2, 3} + id := cidtest.GenerateIDWithChecksum(cidValue) + + a := container.NewAnnouncement() + a.SetEpoch(epoch) + a.SetContainerID(id) + a.SetUsedSpace(usedSpace) + + require.Equal(t, epoch, a.Epoch()) + require.Equal(t, usedSpace, a.UsedSpace()) + require.Equal(t, id, a.ContainerID()) + + t.Run("test v2", func(t *testing.T) { + const newEpoch, newUsedSpace uint64 = 20, 200 + + newCidValue := [32]byte{4, 5, 6} + newCID := new(refs.ContainerID) + newCID.SetValue(newCidValue[:]) + + v2 := a.ToV2() + require.Equal(t, usedSpace, v2.GetUsedSpace()) + require.Equal(t, epoch, v2.GetEpoch()) + require.Equal(t, cidValue[:], v2.GetContainerID().GetValue()) + + v2.SetEpoch(newEpoch) + v2.SetUsedSpace(newUsedSpace) + v2.SetContainerID(newCID) + + newA := container.NewAnnouncementFromV2(v2) + + require.Equal(t, newEpoch, newA.Epoch()) + require.Equal(t, newUsedSpace, newA.UsedSpace()) + require.Equal(t, cid.NewFromV2(newCID), newA.ContainerID()) + }) +} + +func TestUsedSpaceEncoding(t *testing.T) { + a := containertest.UsedSpaceAnnouncement() + + t.Run("binary", func(t *testing.T) { + data, err := a.Marshal() + require.NoError(t, err) + + a2 := container.NewAnnouncement() + require.NoError(t, a2.Unmarshal(data)) + + require.Equal(t, a, a2) + }) +} + +func TestUsedSpaceAnnouncement_ToV2(t *testing.T) { + t.Run("nil", func(t *testing.T) { + var x *container.UsedSpaceAnnouncement + + require.Nil(t, x.ToV2()) + }) + + t.Run("default values", func(t *testing.T) { + announcement := container.NewAnnouncement() + + // check initial values + require.Zero(t, announcement.Epoch()) + require.Zero(t, announcement.UsedSpace()) + require.Nil(t, announcement.ContainerID()) + + // convert to v2 message + announcementV2 := announcement.ToV2() + + require.Zero(t, announcementV2.GetEpoch()) + require.Zero(t, announcementV2.GetUsedSpace()) + require.Nil(t, announcementV2.GetContainerID()) + }) +} + +func TestNewAnnouncementFromV2(t *testing.T) { + t.Run("from nil", func(t *testing.T) { + var x *containerv2.UsedSpaceAnnouncement + + require.Nil(t, container.NewAnnouncementFromV2(x)) + }) +} diff --git a/container/attribute.go b/container/attribute.go new file mode 100644 index 00000000..fc870628 --- /dev/null +++ b/container/attribute.go @@ -0,0 +1,137 @@ +package container + +import ( + "github.com/nspcc-dev/neofs-api-go/v2/container" +) + +type ( + Attribute container.Attribute + Attributes []*Attribute +) + +// NewAttribute creates and initializes blank Attribute. +// +// Defaults: +// - key: ""; +// - value: "". +func NewAttribute() *Attribute { + return NewAttributeFromV2(new(container.Attribute)) +} + +func (a *Attribute) SetKey(v string) { + (*container.Attribute)(a).SetKey(v) +} + +func (a *Attribute) SetValue(v string) { + (*container.Attribute)(a).SetValue(v) +} + +func (a *Attribute) Key() string { + return (*container.Attribute)(a).GetKey() +} + +func (a *Attribute) Value() string { + return (*container.Attribute)(a).GetValue() +} + +// NewAttributeFromV2 wraps protocol dependent version of +// Attribute message. +// +// Nil container.Attribute converts to nil. +func NewAttributeFromV2(v *container.Attribute) *Attribute { + return (*Attribute)(v) +} + +// ToV2 converts Attribute to v2 Attribute message. +// +// Nil Attribute converts to nil. +func (a *Attribute) ToV2() *container.Attribute { + return (*container.Attribute)(a) +} + +func NewAttributesFromV2(v []*container.Attribute) Attributes { + if v == nil { + return nil + } + + attrs := make(Attributes, 0, len(v)) + for i := range v { + attrs = append(attrs, NewAttributeFromV2(v[i])) + } + + return attrs +} + +func (a Attributes) ToV2() []*container.Attribute { + if a == nil { + return nil + } + + attrs := make([]*container.Attribute, 0, len(a)) + for i := range a { + attrs = append(attrs, a[i].ToV2()) + } + + return attrs +} + +// sets value of the attribute by key. +func setAttribute(c *Container, key, value string) { + var a *Attribute + + iterateAttributes(c, func(a_ *Attribute) bool { + if a_.Key() == key { + a = a_ + } + + return a != nil + }) + + if a == nil { + a = NewAttribute() + a.SetKey(key) + + c.SetAttributes(append(c.Attributes(), a)) + } + + a.SetValue(value) +} + +// iterates over container attributes. Stops at f's true return. +// +// Handler must not be nil. +func iterateAttributes(c *Container, f func(*Attribute) bool) { + for _, a := range c.Attributes() { + if f(a) { + return + } + } +} + +// SetNativeNameWithZone sets container native name and its zone. +// +// Use SetNativeName to set default zone. +func SetNativeNameWithZone(c *Container, name, zone string) { + setAttribute(c, container.SysAttributeName, name) + setAttribute(c, container.SysAttributeZone, zone) +} + +// SetNativeName sets container native name with default zone (container). +func SetNativeName(c *Container, name string) { + SetNativeNameWithZone(c, name, container.SysAttributeZoneDefault) +} + +// GetNativeNameWithZone returns container native name and its zone. +func GetNativeNameWithZone(c *Container) (name string, zone string) { + iterateAttributes(c, func(a *Attribute) bool { + if key := a.Key(); key == container.SysAttributeName { + name = a.Value() + } else if key == container.SysAttributeZone { + zone = a.Value() + } + + return name != "" && zone != "" + }) + + return +} diff --git a/container/attribute_test.go b/container/attribute_test.go new file mode 100644 index 00000000..b4278f4e --- /dev/null +++ b/container/attribute_test.go @@ -0,0 +1,155 @@ +package container_test + +import ( + "testing" + + containerv2 "github.com/nspcc-dev/neofs-api-go/v2/container" + "github.com/nspcc-dev/neofs-sdk-go/container" + "github.com/stretchr/testify/require" +) + +func TestAttribute(t *testing.T) { + t.Run("nil", func(t *testing.T) { + var x *container.Attribute + + require.Nil(t, x.ToV2()) + }) + + t.Run("default values", func(t *testing.T) { + attr := container.NewAttribute() + + // check initial values + require.Empty(t, attr.Key()) + require.Empty(t, attr.Value()) + + // convert to v2 message + attrV2 := attr.ToV2() + require.Empty(t, attrV2.GetKey()) + require.Empty(t, attrV2.GetValue()) + }) + + const ( + key = "key" + value = "value" + ) + + attr := container.NewAttribute() + attr.SetKey(key) + attr.SetValue(value) + + require.Equal(t, key, attr.Key()) + require.Equal(t, value, attr.Value()) + + t.Run("test v2", func(t *testing.T) { + const ( + newKey = "newKey" + newValue = "newValue" + ) + + v2 := attr.ToV2() + require.Equal(t, key, v2.GetKey()) + require.Equal(t, value, v2.GetValue()) + + v2.SetKey(newKey) + v2.SetValue(newValue) + + newAttr := container.NewAttributeFromV2(v2) + + require.Equal(t, newKey, newAttr.Key()) + require.Equal(t, newValue, newAttr.Value()) + }) +} + +func TestAttributes(t *testing.T) { + t.Run("nil", func(t *testing.T) { + var x container.Attributes + + require.Nil(t, x.ToV2()) + + require.Nil(t, container.NewAttributesFromV2(nil)) + }) + + var ( + keys = []string{"key1", "key2", "key3"} + vals = []string{"val1", "val2", "val3"} + ) + + attrs := make(container.Attributes, 0, len(keys)) + + for i := range keys { + attr := container.NewAttribute() + attr.SetKey(keys[i]) + attr.SetValue(vals[i]) + + attrs = append(attrs, attr) + } + + t.Run("test v2", func(t *testing.T) { + const postfix = "x" + + v2 := attrs.ToV2() + require.Len(t, v2, len(keys)) + + for i := range v2 { + k := v2[i].GetKey() + v := v2[i].GetValue() + + require.Equal(t, keys[i], k) + require.Equal(t, vals[i], v) + + v2[i].SetKey(k + postfix) + v2[i].SetValue(v + postfix) + } + + newAttrs := container.NewAttributesFromV2(v2) + require.Len(t, newAttrs, len(keys)) + + for i := range newAttrs { + require.Equal(t, keys[i]+postfix, newAttrs[i].Key()) + require.Equal(t, vals[i]+postfix, newAttrs[i].Value()) + } + }) +} + +func TestNewAttributeFromV2(t *testing.T) { + t.Run("from nil", func(t *testing.T) { + var x *containerv2.Attribute + + require.Nil(t, container.NewAttributeFromV2(x)) + }) +} + +func TestGetNameWithZone(t *testing.T) { + c := container.New() + + for _, item := range [...]struct { + name, zone string + }{ + {"name1", ""}, + {"name1", "zone1"}, + {"name2", "zone1"}, + {"name2", "zone2"}, + {"", "zone2"}, + {"", ""}, + } { + container.SetNativeNameWithZone(c, item.name, item.zone) + + name, zone := container.GetNativeNameWithZone(c) + + require.Equal(t, item.name, name, item.name) + require.Equal(t, item.zone, zone, item.zone) + } +} + +func TestSetNativeName(t *testing.T) { + c := container.New() + + const nameDefZone = "some name" + + container.SetNativeName(c, nameDefZone) + + name, zone := container.GetNativeNameWithZone(c) + + require.Equal(t, nameDefZone, name) + require.Equal(t, containerv2.SysAttributeZoneDefault, zone) +} diff --git a/container/container.go b/container/container.go new file mode 100644 index 00000000..f1660413 --- /dev/null +++ b/container/container.go @@ -0,0 +1,191 @@ +package container + +import ( + "crypto/sha256" + + "github.com/google/uuid" + "github.com/nspcc-dev/neofs-api-go/v2/container" + cid "github.com/nspcc-dev/neofs-sdk-go/container/id" + "github.com/nspcc-dev/neofs-sdk-go/netmap" + "github.com/nspcc-dev/neofs-sdk-go/owner" + "github.com/nspcc-dev/neofs-sdk-go/session" + "github.com/nspcc-dev/neofs-sdk-go/signature" + "github.com/nspcc-dev/neofs-sdk-go/version" +) + +type Container struct { + v2 container.Container + + token *session.Token + + sig *signature.Signature +} + +// New creates, initializes and returns blank Container instance. +// +// Defaults: +// - token: nil; +// - sig: nil; +// - basicACL: acl.PrivateBasicRule; +// - version: nil; +// - nonce: random UUID; +// - attr: nil; +// - policy: nil; +// - ownerID: nil. +func New(opts ...NewOption) *Container { + cnrOptions := defaultContainerOptions() + + for i := range opts { + opts[i].apply(&cnrOptions) + } + + cnr := new(Container) + cnr.SetNonceUUID(cnrOptions.nonce) + cnr.SetBasicACL(cnrOptions.acl) + + if cnrOptions.owner != nil { + cnr.SetOwnerID(cnrOptions.owner) + } + + if cnrOptions.policy != nil { + cnr.SetPlacementPolicy(cnrOptions.policy) + } + + cnr.SetAttributes(cnrOptions.attributes) + + return cnr +} + +// ToV2 returns the v2 Container message. +// +// Nil Container converts to nil. +func (c *Container) ToV2() *container.Container { + if c == nil { + return nil + } + + return &c.v2 +} + +// NewVerifiedFromV2 constructs Container from NeoFS API V2 Container message. +// +// Does not perform if message meets NeoFS API V2 specification. To do this +// use NewVerifiedFromV2 constructor. +func NewContainerFromV2(c *container.Container) *Container { + cnr := new(Container) + + if c != nil { + cnr.v2 = *c + } + + return cnr +} + +// CalculateID calculates container identifier +// based on its structure. +func CalculateID(c *Container) *cid.ID { + data, err := c.ToV2().StableMarshal(nil) + if err != nil { + panic(err) + } + + id := cid.New() + id.SetSHA256(sha256.Sum256(data)) + + return id +} + +func (c *Container) Version() *version.Version { + return version.NewFromV2(c.v2.GetVersion()) +} + +func (c *Container) SetVersion(v *version.Version) { + c.v2.SetVersion(v.ToV2()) +} + +func (c *Container) OwnerID() *owner.ID { + return owner.NewIDFromV2(c.v2.GetOwnerID()) +} + +func (c *Container) SetOwnerID(v *owner.ID) { + c.v2.SetOwnerID(v.ToV2()) +} + +// Returns container nonce in UUID format. +// +// Returns error if container nonce is not a valid UUID. +func (c *Container) NonceUUID() (uuid.UUID, error) { + return uuid.FromBytes(c.v2.GetNonce()) +} + +// SetNonceUUID sets container nonce as UUID. +func (c *Container) SetNonceUUID(v uuid.UUID) { + data, _ := v.MarshalBinary() + c.v2.SetNonce(data) +} + +func (c *Container) BasicACL() uint32 { + return c.v2.GetBasicACL() +} + +func (c *Container) SetBasicACL(v uint32) { + c.v2.SetBasicACL(v) +} + +func (c *Container) Attributes() Attributes { + return NewAttributesFromV2(c.v2.GetAttributes()) +} + +func (c *Container) SetAttributes(v Attributes) { + c.v2.SetAttributes(v.ToV2()) +} + +func (c *Container) PlacementPolicy() *netmap.PlacementPolicy { + return netmap.NewPlacementPolicyFromV2(c.v2.GetPlacementPolicy()) +} + +func (c *Container) SetPlacementPolicy(v *netmap.PlacementPolicy) { + c.v2.SetPlacementPolicy(v.ToV2()) +} + +// SessionToken returns token of the session within +// which container was created. +func (c Container) SessionToken() *session.Token { + return c.token +} + +// SetSessionToken sets token of the session within +// which container was created. +func (c *Container) SetSessionToken(t *session.Token) { + c.token = t +} + +// Signature returns signature of the marshaled container. +func (c Container) Signature() *signature.Signature { + return c.sig +} + +// SetSignature sets signature of the marshaled container. +func (c *Container) SetSignature(sig *signature.Signature) { + c.sig = sig +} + +// Marshal marshals Container into a protobuf binary form. +func (c *Container) Marshal() ([]byte, error) { + return c.v2.StableMarshal(nil) +} + +// Unmarshal unmarshals protobuf binary representation of Container. +func (c *Container) Unmarshal(data []byte) error { + return c.v2.Unmarshal(data) +} + +// MarshalJSON encodes Container to protobuf JSON format. +func (c *Container) MarshalJSON() ([]byte, error) { + return c.v2.MarshalJSON() +} + +// UnmarshalJSON decodes Container from protobuf JSON format. +func (c *Container) UnmarshalJSON(data []byte) error { + return c.v2.UnmarshalJSON(data) +} diff --git a/container/container_test.go b/container/container_test.go new file mode 100644 index 00000000..784a284a --- /dev/null +++ b/container/container_test.go @@ -0,0 +1,135 @@ +package container_test + +import ( + "testing" + + "github.com/google/uuid" + "github.com/nspcc-dev/neofs-sdk-go/acl" + "github.com/nspcc-dev/neofs-sdk-go/container" + containertest "github.com/nspcc-dev/neofs-sdk-go/container/test" + netmaptest "github.com/nspcc-dev/neofs-sdk-go/netmap/test" + ownertest "github.com/nspcc-dev/neofs-sdk-go/owner/test" + sessiontest "github.com/nspcc-dev/neofs-sdk-go/session/test" + sigtest "github.com/nspcc-dev/neofs-sdk-go/signature/test" + versiontest "github.com/nspcc-dev/neofs-sdk-go/version/test" + "github.com/stretchr/testify/require" +) + +func TestNewContainer(t *testing.T) { + c := container.New() + + nonce := uuid.New() + + ownerID := ownertest.GenerateID() + policy := netmaptest.PlacementPolicy() + + c.SetBasicACL(acl.PublicBasicRule) + + attrs := containertest.Attributes() + c.SetAttributes(attrs) + + c.SetPlacementPolicy(policy) + c.SetNonceUUID(nonce) + c.SetOwnerID(ownerID) + + ver := versiontest.Version() + c.SetVersion(ver) + + v2 := c.ToV2() + newContainer := container.NewContainerFromV2(v2) + + require.EqualValues(t, newContainer.PlacementPolicy(), policy) + require.EqualValues(t, newContainer.Attributes(), attrs) + require.EqualValues(t, newContainer.BasicACL(), acl.PublicBasicRule) + + newNonce, err := newContainer.NonceUUID() + require.NoError(t, err) + + require.EqualValues(t, newNonce, nonce) + require.EqualValues(t, newContainer.OwnerID(), ownerID) + require.EqualValues(t, newContainer.Version(), ver) +} + +func TestContainerEncoding(t *testing.T) { + c := containertest.Container() + + t.Run("binary", func(t *testing.T) { + data, err := c.Marshal() + require.NoError(t, err) + + c2 := container.New() + require.NoError(t, c2.Unmarshal(data)) + + require.Equal(t, c, c2) + }) + + t.Run("json", func(t *testing.T) { + data, err := c.MarshalJSON() + require.NoError(t, err) + + c2 := container.New() + require.NoError(t, c2.UnmarshalJSON(data)) + + require.Equal(t, c, c2) + }) +} + +func TestContainer_SessionToken(t *testing.T) { + tok := sessiontest.Generate() + + cnr := container.New() + + cnr.SetSessionToken(tok) + + require.Equal(t, tok, cnr.SessionToken()) +} + +func TestContainer_Signature(t *testing.T) { + sig := sigtest.Signature() + + cnr := container.New() + cnr.SetSignature(sig) + + require.Equal(t, sig, cnr.Signature()) +} + +func TestContainer_ToV2(t *testing.T) { + t.Run("nil", func(t *testing.T) { + var x *container.Container + + require.Nil(t, x.ToV2()) + }) + + t.Run("default values", func(t *testing.T) { + cnt := container.New() + + // check initial values + require.Nil(t, cnt.SessionToken()) + require.Nil(t, cnt.Signature()) + require.Nil(t, cnt.Version()) + require.Nil(t, cnt.Attributes()) + require.Nil(t, cnt.PlacementPolicy()) + require.Nil(t, cnt.OwnerID()) + + require.EqualValues(t, acl.PrivateBasicRule, cnt.BasicACL()) + + nonce, err := cnt.NonceUUID() + require.NoError(t, err) + require.NotNil(t, nonce) + + // convert to v2 message + cntV2 := cnt.ToV2() + + nonceV2, err := uuid.FromBytes(cntV2.GetNonce()) + require.NoError(t, err) + + require.Equal(t, nonce.String(), nonceV2.String()) + + require.Nil(t, cntV2.GetVersion()) + require.Nil(t, cntV2.GetAttributes()) + require.Nil(t, cntV2.GetPlacementPolicy()) + require.Nil(t, cntV2.GetOwnerID()) + + require.Equal(t, uint32(acl.PrivateBasicRule), cntV2.GetBasicACL()) + }) +} diff --git a/container/opts.go b/container/opts.go new file mode 100644 index 00000000..05db06b7 --- /dev/null +++ b/container/opts.go @@ -0,0 +1,104 @@ +package container + +import ( + "github.com/google/uuid" + "github.com/nspcc-dev/neofs-sdk-go/acl" + "github.com/nspcc-dev/neofs-sdk-go/netmap" + "github.com/nspcc-dev/neofs-sdk-go/owner" +) + +type ( + NewOption interface { + apply(*containerOptions) + } + + containerOptions struct { + acl uint32 + policy *netmap.PlacementPolicy + attributes Attributes + owner *owner.ID + nonce uuid.UUID + } +) + +func defaultContainerOptions() containerOptions { + rand, err := uuid.NewRandom() + if err != nil { + panic("can't create new random " + err.Error()) + } + + return containerOptions{ + acl: acl.PrivateBasicRule, + nonce: rand, + } +} + +type funcContainerOption struct { + f func(*containerOptions) +} + +func (fco *funcContainerOption) apply(co *containerOptions) { + fco.f(co) +} + +func newFuncContainerOption(f func(option *containerOptions)) *funcContainerOption { + return &funcContainerOption{ + f: f, + } +} + +func WithPublicBasicACL() NewOption { + return newFuncContainerOption(func(option *containerOptions) { + option.acl = acl.PublicBasicRule + }) +} + +func WithReadOnlyBasicACL() NewOption { + return newFuncContainerOption(func(option *containerOptions) { + option.acl = acl.ReadOnlyBasicRule + }) +} + +func WithCustomBasicACL(acl uint32) NewOption { + return newFuncContainerOption(func(option *containerOptions) { + option.acl = acl + }) +} + +func WithNonce(nonce uuid.UUID) NewOption { + return newFuncContainerOption(func(option *containerOptions) { + option.nonce = nonce + }) +} + +func WithOwnerID(id *owner.ID) NewOption { + return newFuncContainerOption(func(option *containerOptions) { + option.owner = id + }) +} + +func WithNEO3Wallet(w *owner.NEO3Wallet) NewOption { + return newFuncContainerOption(func(option *containerOptions) { + if option.owner == nil { + option.owner = new(owner.ID) + } + + option.owner.SetNeo3Wallet(w) + }) +} + +func WithPolicy(policy *netmap.PlacementPolicy) NewOption { + return newFuncContainerOption(func(option *containerOptions) { + option.policy = policy + }) +} + +func WithAttribute(key, value string) NewOption { + return newFuncContainerOption(func(option *containerOptions) { + attr := NewAttribute() + attr.SetKey(key) + attr.SetValue(value) + + option.attributes = append(option.attributes, attr) + }) +} diff --git a/container/test/generate.go b/container/test/generate.go new file mode 100644 index 00000000..4ad99100 --- /dev/null +++ b/container/test/generate.go @@ -0,0 +1,48 @@ +package containertest + +import ( + "github.com/nspcc-dev/neofs-sdk-go/container" + cidtest "github.com/nspcc-dev/neofs-sdk-go/container/id/test" + netmaptest "github.com/nspcc-dev/neofs-sdk-go/netmap/test" + ownertest "github.com/nspcc-dev/neofs-sdk-go/owner/test" + versiontest "github.com/nspcc-dev/neofs-sdk-go/version/test" +) + +// Attribute returns random container.Attribute. +func Attribute() *container.Attribute { + x := container.NewAttribute() + + x.SetKey("key") + x.SetValue("value") + + return x +} + +// Attributes returns random container.Attributes. +func Attributes() container.Attributes { + return container.Attributes{Attribute(), Attribute()} +} + +// Container returns random container.Container. +func Container() *container.Container { + x := container.New() + + x.SetVersion(versiontest.Version()) + x.SetAttributes(Attributes()) + x.SetOwnerID(ownertest.GenerateID()) + x.SetBasicACL(123) + x.SetPlacementPolicy(netmaptest.PlacementPolicy()) + + return x +} + +// UsedSpaceAnnouncement returns random container.UsedSpaceAnnouncement. +func UsedSpaceAnnouncement() *container.UsedSpaceAnnouncement { + x := container.NewAnnouncement() + + x.SetContainerID(cidtest.GenerateID()) + x.SetEpoch(55) + x.SetUsedSpace(999) + + return x +} diff --git a/container/wellknown_attributes.go b/container/wellknown_attributes.go new file mode 100644 index 00000000..241c745f --- /dev/null +++ b/container/wellknown_attributes.go @@ -0,0 +1,11 @@ +package container + +const ( + // AttributeName is an attribute key that is commonly used to denote + // human-friendly name. + AttributeName = "Name" + + // AttributeTimestamp is an attribute key that is commonly used to denote + // user-defined local time of container creation in Unix Timestamp format. + AttributeTimestamp = "Timestamp" +) diff --git a/netmap/test/generate.go b/netmap/test/generate.go index 3f0826b0..86edb25d 100644 --- a/netmap/test/generate.go +++ b/netmap/test/generate.go @@ -2,6 +2,61 @@ package test import "github.com/nspcc-dev/neofs-sdk-go/netmap" +func filter(withInner bool) *netmap.Filter { + x := netmap.NewFilter() + + x.SetName("name") + x.SetKey("key") + x.SetValue("value") + x.SetOperation(netmap.OpAND) + + if withInner { + x.SetInnerFilters(filter(false), filter(false)) + } + + return x +} + +// Filter returns random netmap.Filter. +func Filter() *netmap.Filter { + return filter(true) +} + +// Replica returns random netmap.Replica. +func Replica() *netmap.Replica { + x := netmap.NewReplica() + + x.SetCount(666) + x.SetSelector("selector") + + return x +} + +// Selector returns random netmap.Selector. +func Selector() *netmap.Selector { + x := netmap.NewSelector() + + x.SetCount(11) + x.SetName("name") + x.SetFilter("filter") + x.SetAttribute("attribute") + x.SetClause(netmap.ClauseDistinct) + + return x +} + +// PlacementPolicy returns random netmap.PlacementPolicy. +func PlacementPolicy() *netmap.PlacementPolicy { + x := netmap.NewPlacementPolicy() + + x.SetContainerBackupFactor(9) + x.SetFilters(Filter(), Filter()) + x.SetReplicas(Replica(), Replica()) + x.SetSelectors(Selector(), Selector()) + + return x +} + // NetworkParameter returns random netmap.NetworkParameter. func NetworkParameter() *netmap.NetworkParameter { x := netmap.NewNetworkParameter()