forked from TrueCloudLab/frostfs-node
[#11] Trim the old functionality
Signed-off-by: Leonard Lyubich <leonard@nspcc.ru>
This commit is contained in:
parent
783ec72d56
commit
a87fdab324
235 changed files with 39 additions and 36211 deletions
0
pkg/core/container/.gitkeep
Normal file
0
pkg/core/container/.gitkeep
Normal file
|
@ -1,24 +0,0 @@
|
|||
package basic
|
||||
|
||||
const (
|
||||
// OpGetRangeHash is an index of GetRangeHash operation in basic ACL bitmask order.
|
||||
OpGetRangeHash uint8 = iota
|
||||
|
||||
// OpGetRange is an index of GetRange operation in basic ACL bitmask order.
|
||||
OpGetRange
|
||||
|
||||
// OpSearch is an index of Search operation in basic ACL bitmask order.
|
||||
OpSearch
|
||||
|
||||
// OpDelete is an index of Delete operation in basic ACL bitmask order.
|
||||
OpDelete
|
||||
|
||||
// OpPut is an index of Put operation in basic ACL bitmask order.
|
||||
OpPut
|
||||
|
||||
// OpHead is an index of Head operation in basic ACL bitmask order.
|
||||
OpHead
|
||||
|
||||
// OpGet is an index of Get operation in basic ACL bitmask order.
|
||||
OpGet
|
||||
)
|
|
@ -1,159 +0,0 @@
|
|||
package basic
|
||||
|
||||
// ACL represents a container's
|
||||
// basic permission bits.
|
||||
type ACL uint32
|
||||
|
||||
const (
|
||||
reservedBitNumber = 2 // first left bits are reserved
|
||||
|
||||
stickyBitPos = reservedBitNumber // X-bit after reserved bits
|
||||
|
||||
finalBitPos = stickyBitPos + 1 // F-bit after X-bit
|
||||
)
|
||||
|
||||
const (
|
||||
opOffset = finalBitPos + 1 // offset of operation bits
|
||||
|
||||
bitsPerOp = 4 // number of bits per operation
|
||||
|
||||
opNumber = 7 // number of operation bit sections
|
||||
)
|
||||
|
||||
const (
|
||||
bitUser uint8 = iota
|
||||
bitSystem
|
||||
bitOthers
|
||||
bitBearer
|
||||
)
|
||||
|
||||
const leftACLBitPos = opOffset + bitsPerOp*opNumber - 1
|
||||
|
||||
// returns true if n-th left bit is set (starting at 0).
|
||||
func isLeftBitSet(value ACL, n uint8) bool {
|
||||
bitMask := ACL(1 << (leftACLBitPos - n))
|
||||
return bitMask != 0 && value&bitMask == bitMask
|
||||
}
|
||||
|
||||
// sets n-th left bit (starting at 0).
|
||||
func setLeftBit(value *ACL, n uint8) {
|
||||
*value |= ACL(1 << (leftACLBitPos - n))
|
||||
}
|
||||
|
||||
// resets n-th left bit (starting at 0).
|
||||
func resetLeftBit(value *ACL, n uint8) {
|
||||
*value &= ^ACL(1 << (leftACLBitPos - n))
|
||||
}
|
||||
|
||||
// Reserved returns true if n-th reserved option is enabled in basic ACL.
|
||||
func (a ACL) Reserved(n uint8) bool {
|
||||
return n < reservedBitNumber && isLeftBitSet(a, n)
|
||||
}
|
||||
|
||||
// SetReserved enables the n-th reserved option in basic ACL.
|
||||
func (a *ACL) SetReserved(bit uint8) {
|
||||
if bit < reservedBitNumber {
|
||||
setLeftBit(a, bit)
|
||||
}
|
||||
}
|
||||
|
||||
// ResetReserved disables the n-th reserved option in basic ACL.
|
||||
func (a *ACL) ResetReserved(bit uint8) {
|
||||
if bit < reservedBitNumber {
|
||||
resetLeftBit(a, bit)
|
||||
}
|
||||
}
|
||||
|
||||
// Final returns true if final option is enabled in basic ACL.
|
||||
func (a ACL) Final() bool {
|
||||
return isLeftBitSet(a, finalBitPos)
|
||||
}
|
||||
|
||||
// SetFinal enables final option in basic ACL.
|
||||
func (a *ACL) SetFinal() {
|
||||
setLeftBit(a, finalBitPos)
|
||||
}
|
||||
|
||||
// ResetFinal disables final option in basic ACL.
|
||||
func (a *ACL) ResetFinal() {
|
||||
resetLeftBit(a, finalBitPos)
|
||||
}
|
||||
|
||||
// Sticky returns true if sticky option is enabled in basic ACL.
|
||||
func (a ACL) Sticky() bool {
|
||||
return isLeftBitSet(a, stickyBitPos)
|
||||
}
|
||||
|
||||
// SetSticky enables the sticky option in basic ACL.
|
||||
func (a *ACL) SetSticky() {
|
||||
setLeftBit(a, stickyBitPos)
|
||||
}
|
||||
|
||||
// ResetSticky disables the sticky option in basic ACL.
|
||||
func (a *ACL) ResetSticky() {
|
||||
resetLeftBit(a, stickyBitPos)
|
||||
}
|
||||
|
||||
// UserAllowed returns true if user allowed the n-th operation in basic ACL.
|
||||
func (a ACL) UserAllowed(n uint8) bool {
|
||||
return isLeftBitSet(a, opOffset+n*bitsPerOp+bitUser)
|
||||
}
|
||||
|
||||
// AllowUser allows user the n-th operation in basic ACL.
|
||||
func (a *ACL) AllowUser(n uint8) {
|
||||
setLeftBit(a, opOffset+n*bitsPerOp+bitUser)
|
||||
}
|
||||
|
||||
// ForbidUser forbids user the n-th operation in basic ACL.
|
||||
func (a *ACL) ForbidUser(n uint8) {
|
||||
resetLeftBit(a, opOffset+n*bitsPerOp+bitUser)
|
||||
}
|
||||
|
||||
// SystemAllowed returns true if System group allowed the n-th operation is set in basic ACL.
|
||||
func (a ACL) SystemAllowed(n uint8) bool {
|
||||
if n != OpDelete && n != OpGetRange {
|
||||
return true
|
||||
}
|
||||
|
||||
return isLeftBitSet(a, opOffset+n*bitsPerOp+bitSystem)
|
||||
}
|
||||
|
||||
// AllowSystem allows System group the n-th operation in basic ACL.
|
||||
func (a *ACL) AllowSystem(op uint8) {
|
||||
setLeftBit(a, opOffset+op*bitsPerOp+bitSystem)
|
||||
}
|
||||
|
||||
// ForbidSystem forbids System group the n-th operation in basic ACL.
|
||||
func (a *ACL) ForbidSystem(op uint8) {
|
||||
resetLeftBit(a, opOffset+op*bitsPerOp+bitSystem)
|
||||
}
|
||||
|
||||
// OthersAllowed returns true if Others group allowed the n-th operation is set in basic ACL.
|
||||
func (a ACL) OthersAllowed(op uint8) bool {
|
||||
return isLeftBitSet(a, opOffset+op*bitsPerOp+bitOthers)
|
||||
}
|
||||
|
||||
// AllowOthers allows Others group the n-th operation in basic ACL.
|
||||
func (a *ACL) AllowOthers(op uint8) {
|
||||
setLeftBit(a, opOffset+op*bitsPerOp+bitOthers)
|
||||
}
|
||||
|
||||
// ForbidOthers forbids Others group the n-th operation in basic ACL.
|
||||
func (a *ACL) ForbidOthers(op uint8) {
|
||||
resetLeftBit(a, opOffset+op*bitsPerOp+bitOthers)
|
||||
}
|
||||
|
||||
// BearerAllowed returns true if Bearer token usage is allowed for n-th operation in basic ACL.
|
||||
func (a ACL) BearerAllowed(op uint8) bool {
|
||||
return isLeftBitSet(a, opOffset+op*bitsPerOp+bitBearer)
|
||||
}
|
||||
|
||||
// AllowBearer allows Bearer token usage for n-th operation in basic ACL.
|
||||
func (a *ACL) AllowBearer(op uint8) {
|
||||
setLeftBit(a, opOffset+op*bitsPerOp+bitBearer)
|
||||
}
|
||||
|
||||
// ForbidBearer forbids Bearer token usage for n-th operation in basic ACL.
|
||||
func (a *ACL) ForbidBearer(op uint8) {
|
||||
resetLeftBit(a, opOffset+op*bitsPerOp+bitBearer)
|
||||
}
|
|
@ -1,189 +0,0 @@
|
|||
package basic
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestACLValues(t *testing.T) {
|
||||
t.Run("private", func(t *testing.T) {
|
||||
acl := FromUint32(0x1C8C8CCC)
|
||||
|
||||
require.False(t, acl.Reserved(0))
|
||||
require.False(t, acl.Reserved(1))
|
||||
require.False(t, acl.Sticky())
|
||||
require.True(t, acl.Final())
|
||||
|
||||
require.True(t, acl.UserAllowed(OpGetRangeHash))
|
||||
require.True(t, acl.SystemAllowed(OpGetRangeHash))
|
||||
require.False(t, acl.OthersAllowed(OpGetRangeHash))
|
||||
require.False(t, acl.BearerAllowed(OpGetRangeHash))
|
||||
|
||||
require.True(t, acl.UserAllowed(OpGetRange))
|
||||
require.False(t, acl.SystemAllowed(OpGetRange))
|
||||
require.False(t, acl.OthersAllowed(OpGetRange))
|
||||
require.False(t, acl.BearerAllowed(OpGetRange))
|
||||
|
||||
require.True(t, acl.UserAllowed(OpSearch))
|
||||
require.True(t, acl.SystemAllowed(OpSearch))
|
||||
require.False(t, acl.OthersAllowed(OpSearch))
|
||||
require.False(t, acl.BearerAllowed(OpSearch))
|
||||
|
||||
require.True(t, acl.UserAllowed(OpDelete))
|
||||
require.False(t, acl.SystemAllowed(OpDelete))
|
||||
require.False(t, acl.OthersAllowed(OpDelete))
|
||||
require.False(t, acl.BearerAllowed(OpDelete))
|
||||
|
||||
require.True(t, acl.UserAllowed(OpPut))
|
||||
require.True(t, acl.SystemAllowed(OpPut))
|
||||
require.False(t, acl.OthersAllowed(OpPut))
|
||||
require.False(t, acl.BearerAllowed(OpPut))
|
||||
|
||||
require.True(t, acl.UserAllowed(OpHead))
|
||||
require.True(t, acl.SystemAllowed(OpHead))
|
||||
require.False(t, acl.OthersAllowed(OpHead))
|
||||
require.False(t, acl.BearerAllowed(OpHead))
|
||||
|
||||
require.True(t, acl.UserAllowed(OpGet))
|
||||
require.True(t, acl.SystemAllowed(OpGet))
|
||||
require.False(t, acl.OthersAllowed(OpGet))
|
||||
require.False(t, acl.BearerAllowed(OpGet))
|
||||
})
|
||||
|
||||
t.Run("public with X-bit", func(t *testing.T) {
|
||||
acl := FromUint32(0x3FFFFFFF)
|
||||
|
||||
require.False(t, acl.Reserved(0))
|
||||
require.False(t, acl.Reserved(1))
|
||||
require.True(t, acl.Sticky())
|
||||
require.True(t, acl.Final())
|
||||
|
||||
require.True(t, acl.UserAllowed(OpGetRangeHash))
|
||||
require.True(t, acl.SystemAllowed(OpGetRangeHash))
|
||||
require.True(t, acl.OthersAllowed(OpGetRangeHash))
|
||||
require.True(t, acl.BearerAllowed(OpGetRangeHash))
|
||||
|
||||
require.True(t, acl.UserAllowed(OpGetRange))
|
||||
require.True(t, acl.SystemAllowed(OpGetRange))
|
||||
require.True(t, acl.OthersAllowed(OpGetRange))
|
||||
require.True(t, acl.BearerAllowed(OpGetRange))
|
||||
|
||||
require.True(t, acl.UserAllowed(OpSearch))
|
||||
require.True(t, acl.SystemAllowed(OpSearch))
|
||||
require.True(t, acl.OthersAllowed(OpSearch))
|
||||
require.True(t, acl.BearerAllowed(OpSearch))
|
||||
|
||||
require.True(t, acl.UserAllowed(OpDelete))
|
||||
require.True(t, acl.SystemAllowed(OpDelete))
|
||||
require.True(t, acl.OthersAllowed(OpDelete))
|
||||
require.True(t, acl.BearerAllowed(OpDelete))
|
||||
|
||||
require.True(t, acl.UserAllowed(OpPut))
|
||||
require.True(t, acl.SystemAllowed(OpPut))
|
||||
require.True(t, acl.OthersAllowed(OpPut))
|
||||
require.True(t, acl.BearerAllowed(OpPut))
|
||||
|
||||
require.True(t, acl.UserAllowed(OpHead))
|
||||
require.True(t, acl.SystemAllowed(OpHead))
|
||||
require.True(t, acl.OthersAllowed(OpHead))
|
||||
require.True(t, acl.BearerAllowed(OpHead))
|
||||
|
||||
require.True(t, acl.UserAllowed(OpGet))
|
||||
require.True(t, acl.SystemAllowed(OpGet))
|
||||
require.True(t, acl.OthersAllowed(OpGet))
|
||||
require.True(t, acl.BearerAllowed(OpGet))
|
||||
})
|
||||
|
||||
t.Run("read only", func(t *testing.T) {
|
||||
acl := FromUint32(0x1FFFCCFF)
|
||||
|
||||
require.False(t, acl.Reserved(0))
|
||||
require.False(t, acl.Reserved(1))
|
||||
require.False(t, acl.Sticky())
|
||||
require.True(t, acl.Final())
|
||||
|
||||
require.True(t, acl.UserAllowed(OpGetRangeHash))
|
||||
require.True(t, acl.SystemAllowed(OpGetRangeHash))
|
||||
require.True(t, acl.OthersAllowed(OpGetRangeHash))
|
||||
require.True(t, acl.BearerAllowed(OpGetRangeHash))
|
||||
|
||||
require.True(t, acl.UserAllowed(OpGetRange))
|
||||
require.True(t, acl.SystemAllowed(OpGetRange))
|
||||
require.True(t, acl.OthersAllowed(OpGetRange))
|
||||
require.True(t, acl.BearerAllowed(OpGetRange))
|
||||
|
||||
require.True(t, acl.UserAllowed(OpSearch))
|
||||
require.True(t, acl.SystemAllowed(OpSearch))
|
||||
require.True(t, acl.OthersAllowed(OpSearch))
|
||||
require.True(t, acl.BearerAllowed(OpSearch))
|
||||
|
||||
require.True(t, acl.UserAllowed(OpDelete))
|
||||
require.True(t, acl.SystemAllowed(OpDelete))
|
||||
require.False(t, acl.OthersAllowed(OpDelete))
|
||||
require.False(t, acl.BearerAllowed(OpDelete))
|
||||
|
||||
require.True(t, acl.UserAllowed(OpPut))
|
||||
require.True(t, acl.SystemAllowed(OpPut))
|
||||
require.False(t, acl.OthersAllowed(OpPut))
|
||||
require.False(t, acl.BearerAllowed(OpPut))
|
||||
|
||||
require.True(t, acl.UserAllowed(OpHead))
|
||||
require.True(t, acl.SystemAllowed(OpHead))
|
||||
require.True(t, acl.OthersAllowed(OpHead))
|
||||
require.True(t, acl.BearerAllowed(OpHead))
|
||||
|
||||
require.True(t, acl.UserAllowed(OpGet))
|
||||
require.True(t, acl.SystemAllowed(OpGet))
|
||||
require.True(t, acl.OthersAllowed(OpGet))
|
||||
require.True(t, acl.BearerAllowed(OpGet))
|
||||
})
|
||||
}
|
||||
|
||||
func TestACLMethods(t *testing.T) {
|
||||
acl := new(ACL)
|
||||
|
||||
for i := uint8(0); i < reservedBitNumber; i++ {
|
||||
acl.SetReserved(i)
|
||||
require.True(t, acl.Reserved(i))
|
||||
acl.ResetReserved(i)
|
||||
require.False(t, acl.Reserved(i))
|
||||
}
|
||||
|
||||
acl.SetSticky()
|
||||
require.True(t, acl.Sticky())
|
||||
acl.ResetSticky()
|
||||
require.False(t, acl.Sticky())
|
||||
|
||||
acl.SetFinal()
|
||||
require.True(t, acl.Final())
|
||||
acl.ResetFinal()
|
||||
require.False(t, acl.Final())
|
||||
|
||||
for i := OpGetRangeHash; i <= OpGet; i++ {
|
||||
acl.AllowUser(i)
|
||||
require.True(t, acl.UserAllowed(i))
|
||||
acl.ForbidUser(i)
|
||||
require.False(t, acl.UserAllowed(i))
|
||||
|
||||
acl.AllowOthers(i)
|
||||
require.True(t, acl.OthersAllowed(i))
|
||||
acl.ForbidOthers(i)
|
||||
require.False(t, acl.OthersAllowed(i))
|
||||
|
||||
acl.AllowBearer(i)
|
||||
require.True(t, acl.BearerAllowed(i))
|
||||
acl.ForbidBearer(i)
|
||||
require.False(t, acl.BearerAllowed(i))
|
||||
|
||||
acl.AllowSystem(i)
|
||||
require.True(t, acl.SystemAllowed(i))
|
||||
acl.ForbidSystem(i)
|
||||
|
||||
if i == OpDelete || i == OpGetRange {
|
||||
require.False(t, acl.SystemAllowed(i))
|
||||
} else {
|
||||
require.True(t, acl.SystemAllowed(i))
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,64 +0,0 @@
|
|||
package basic
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"io"
|
||||
)
|
||||
|
||||
// Size is a size of ACL
|
||||
// in a binary form.
|
||||
const Size = 4
|
||||
|
||||
// FromUint32 converts builtin
|
||||
// uint32 value to ACL.
|
||||
//
|
||||
// Try to avoid direct cast for
|
||||
// better portability.
|
||||
func FromUint32(v uint32) ACL {
|
||||
return ACL(v)
|
||||
}
|
||||
|
||||
// ToUint32 converts ACL value
|
||||
// to builtin uint32.
|
||||
//
|
||||
// Try to avoid direct cast for
|
||||
// better portability.
|
||||
func ToUint32(v ACL) uint32 {
|
||||
return uint32(v)
|
||||
}
|
||||
|
||||
// Equal reports whether e and e2 are the same ACL.
|
||||
//
|
||||
// Function defines the relation of equality
|
||||
// between two ACL. Try to avoid comparison through
|
||||
// "==" operator for better portability.
|
||||
func Equal(a, b ACL) bool {
|
||||
return ToUint32(a) == ToUint32(b)
|
||||
}
|
||||
|
||||
// Marshal encodes ACL into a
|
||||
// binary form and returns the result.
|
||||
//
|
||||
// Result slice has Size length.
|
||||
func Marshal(a ACL) []byte {
|
||||
d := make([]byte, Size)
|
||||
|
||||
binary.BigEndian.PutUint32(d, ToUint32(a))
|
||||
|
||||
return d
|
||||
}
|
||||
|
||||
// UnmarshalBinary unmarshals ACL from
|
||||
// a binary representation.
|
||||
//
|
||||
// If buffer size is insufficient,
|
||||
// io.ErrUnexpectedEOF is returned.
|
||||
func (a *ACL) UnmarshalBinary(data []byte) error {
|
||||
if len(data) < Size {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
|
||||
*a = FromUint32(binary.BigEndian.Uint32(data))
|
||||
|
||||
return nil
|
||||
}
|
|
@ -1,36 +0,0 @@
|
|||
package basic
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestEqual(t *testing.T) {
|
||||
require.True(t,
|
||||
Equal(
|
||||
FromUint32(1),
|
||||
FromUint32(1),
|
||||
),
|
||||
)
|
||||
|
||||
require.False(t,
|
||||
Equal(
|
||||
FromUint32(1),
|
||||
FromUint32(2),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
func TestMarshal(t *testing.T) {
|
||||
a := FromUint32(1)
|
||||
a2 := new(ACL)
|
||||
|
||||
require.NoError(t,
|
||||
a2.UnmarshalBinary(
|
||||
Marshal(a),
|
||||
),
|
||||
)
|
||||
|
||||
require.True(t, Equal(a, *a2))
|
||||
}
|
|
@ -1,53 +0,0 @@
|
|||
package storage
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
eacl "github.com/nspcc-dev/neofs-api-go/acl/extended"
|
||||
"github.com/nspcc-dev/neofs-node/pkg/core/container"
|
||||
)
|
||||
|
||||
// CID represents the container identifier.
|
||||
//
|
||||
// It is a type alias of
|
||||
// github.com/nspcc-dev/neofs-node/pkg/core/container.ID.
|
||||
type CID = container.ID
|
||||
|
||||
// Table represents extended ACL rule table.
|
||||
//
|
||||
// It is a type alias of
|
||||
// github.com/nspcc-dev/neofs-node/pkg/core/container/eacl/extended.Table.
|
||||
type Table = eacl.Table
|
||||
|
||||
// Storage is the interface that wraps
|
||||
// basic methods of extended ACL table storage.
|
||||
type Storage interface {
|
||||
// GetEACL reads the table from the storage by identifier.
|
||||
// It returns any error encountered.
|
||||
//
|
||||
// GetEACL must return exactly one non-nil value.
|
||||
// GetEACL must return ErrNotFound if the table is not in storage.
|
||||
//
|
||||
// Implementations must not retain or modify the table
|
||||
// (even temporarily).
|
||||
GetEACL(CID) (Table, error)
|
||||
|
||||
// PutEACL saves the table to the underlying storage.
|
||||
// It returns any error encountered that caused the saving to interrupt.
|
||||
//
|
||||
// PutEACL must return extended.ErrNilTable on nil table.
|
||||
//
|
||||
// Implementations must not retain or modify the table (even temporarily).
|
||||
//
|
||||
// Table rewriting behavior is dictated by implementation.
|
||||
PutEACL(CID, Table, []byte) error
|
||||
}
|
||||
|
||||
// ErrNotFound is the error returned when eACL table
|
||||
// was not found in storage.
|
||||
var ErrNotFound = errors.New("container not found")
|
||||
|
||||
// ErrNilStorage is the error returned by functions that
|
||||
// expect a non-nil eACL table storage implementation,
|
||||
// but received nil.
|
||||
var ErrNilStorage = errors.New("eACL storage is nil")
|
|
@ -1,48 +0,0 @@
|
|||
package test
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"github.com/nspcc-dev/neofs-node/pkg/core/container"
|
||||
"github.com/nspcc-dev/neofs-node/pkg/core/container/acl/extended"
|
||||
"github.com/nspcc-dev/neofs-node/pkg/core/container/acl/extended/storage"
|
||||
)
|
||||
|
||||
type testStorage struct {
|
||||
*sync.RWMutex
|
||||
|
||||
items map[container.ID]storage.Table
|
||||
}
|
||||
|
||||
func (s *testStorage) GetEACL(cid storage.CID) (storage.Table, error) {
|
||||
s.RLock()
|
||||
table, ok := s.items[cid]
|
||||
s.RUnlock()
|
||||
|
||||
if !ok {
|
||||
return nil, storage.ErrNotFound
|
||||
}
|
||||
|
||||
return table, nil
|
||||
}
|
||||
|
||||
func (s *testStorage) PutEACL(cid storage.CID, table storage.Table, _ []byte) error {
|
||||
if table == nil {
|
||||
return extended.ErrNilTable
|
||||
}
|
||||
|
||||
s.Lock()
|
||||
s.items[cid] = table
|
||||
s.Unlock()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// New creates new eACL table storage
|
||||
// that stores table in go-builtin map.
|
||||
func New() storage.Storage {
|
||||
return &testStorage{
|
||||
RWMutex: new(sync.RWMutex),
|
||||
items: make(map[container.ID]storage.Table),
|
||||
}
|
||||
}
|
|
@ -1,102 +0,0 @@
|
|||
package extended
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
eacl "github.com/nspcc-dev/neofs-api-go/acl/extended"
|
||||
"github.com/nspcc-dev/neofs-node/pkg/core/container"
|
||||
)
|
||||
|
||||
// FIXME: do not duplicate constants
|
||||
|
||||
// OperationType represents the enumeration
|
||||
// of different destination types of request.
|
||||
//
|
||||
// It is a type alias of
|
||||
// github.com/nspcc-dev/neofs-api-go/eacl.OperationType.
|
||||
// FIXME: operation type should be defined in core lib.
|
||||
type OperationType = eacl.OperationType
|
||||
|
||||
// Group represents the enumeration
|
||||
// of different authorization groups.
|
||||
//
|
||||
// It is a type alias of
|
||||
// github.com/nspcc-dev/neofs-api-go/acl/extended.Group.
|
||||
// FIXME: target should be defined in core lib.
|
||||
type Group = eacl.Group
|
||||
|
||||
// HeaderType represents the enumeration
|
||||
// of different types of header.
|
||||
//
|
||||
// It is a type alias of
|
||||
// github.com/nspcc-dev/neofs-api-go/eacl.HeaderType.
|
||||
// FIXME: header type enum should be defined in core lib.
|
||||
type HeaderType = eacl.HeaderType
|
||||
|
||||
// CID represents the container identifier.
|
||||
//
|
||||
// It is a type alias of
|
||||
// github.com/nspcc-dev/neofs-node/pkg/core/container.ID.
|
||||
type CID = container.ID
|
||||
|
||||
// Header is an interface that wraps
|
||||
// methods of string key-value header.
|
||||
//
|
||||
// It is a type alias of
|
||||
// github.com/nspcc-dev/neofs-api-go/eacl.Header.
|
||||
// FIXME: header should be defined in core lib.
|
||||
type Header = eacl.Header
|
||||
|
||||
// Table represents extended ACL rule table.
|
||||
//
|
||||
// It is a type alias of
|
||||
// github.com/nspcc-dev/neofs-api-go/eacl.ExtendedACLTable.
|
||||
// FIXME: eacl table should be defined in core package.
|
||||
// type Table = eacl.ExtendedACLTable
|
||||
|
||||
// TypedHeaderSource is the interface that wraps
|
||||
// method for selecting typed headers by type.
|
||||
type TypedHeaderSource interface {
|
||||
// HeadersOfType returns the list of key-value headers
|
||||
// of particular type.
|
||||
//
|
||||
// It returns any problem encountered through the boolean
|
||||
// false value.
|
||||
HeadersOfType(HeaderType) ([]Header, bool)
|
||||
}
|
||||
|
||||
// RequestInfo is an interface that wraps
|
||||
// request with authority methods.
|
||||
type RequestInfo interface {
|
||||
TypedHeaderSource
|
||||
|
||||
// CID returns container identifier from request context.
|
||||
CID() CID
|
||||
|
||||
// Key returns the binary representation of
|
||||
// author's public key.
|
||||
//
|
||||
// Any problem encountered can be reflected
|
||||
// through an empty slice.
|
||||
//
|
||||
// Binary key format is dictated by implementation.
|
||||
Key() []byte
|
||||
|
||||
// OperationType returns the type of request destination.
|
||||
//
|
||||
// Any problem encountered can be reflected
|
||||
// through OpTypeUnknown value. Caller should handle
|
||||
// OpTypeUnknown value according to its internal logic.
|
||||
OperationType() OperationType
|
||||
|
||||
// Group returns the authority group type.
|
||||
//
|
||||
// Any problem encountered can be reflected
|
||||
// through GroupUnknown value. Caller should handle
|
||||
// TargetUnknown value according to its internal logic.
|
||||
Group() Group
|
||||
}
|
||||
|
||||
// ErrNilTable is the error returned by functions that
|
||||
// expect a non-nil eACL table, but received nil.
|
||||
var ErrNilTable = errors.New("eACL table is nil")
|
|
@ -1,82 +0,0 @@
|
|||
package container
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/nspcc-dev/neofs-node/pkg/core/container/acl/basic"
|
||||
"github.com/nspcc-dev/netmap"
|
||||
)
|
||||
|
||||
// BasicACL represents the basic
|
||||
// ACL rules.
|
||||
//
|
||||
// It is a type alias of
|
||||
// github.com/nspcc-dev/neofs-node/pkg/core/container/basic.ACL.
|
||||
type BasicACL = basic.ACL
|
||||
|
||||
// PlacementRule represents placement
|
||||
// rules of the container.
|
||||
//
|
||||
// It is a type alias of
|
||||
// github.com/nspcc-dev/netmap.PlacementRule.
|
||||
// FIXME: container placement rules should be defined in core lib.
|
||||
type PlacementRule = netmap.PlacementRule
|
||||
|
||||
// Container represents NeoFS container.
|
||||
type Container struct {
|
||||
basicACL BasicACL // basic ACL mask
|
||||
|
||||
ownerID OwnerID // the identifier of container's owner
|
||||
|
||||
salt []byte // unique container bytes
|
||||
|
||||
placementRule PlacementRule // placement rules
|
||||
}
|
||||
|
||||
// ErrNilContainer is the error returned by functions that
|
||||
// expect a non-nil container pointer, but received nil.
|
||||
var ErrNilContainer = errors.New("container is nil")
|
||||
|
||||
// OwnerID returns an ID of the container's owner.
|
||||
func (c *Container) OwnerID() OwnerID {
|
||||
return c.ownerID
|
||||
}
|
||||
|
||||
// SetOwnerID sets the ID of the container's owner.
|
||||
func (c *Container) SetOwnerID(v OwnerID) {
|
||||
c.ownerID = v
|
||||
}
|
||||
|
||||
// Salt returns the container salt.
|
||||
//
|
||||
// Slice is returned by reference without copying.
|
||||
func (c *Container) Salt() []byte {
|
||||
return c.salt
|
||||
}
|
||||
|
||||
// SetSalt sets the container salt.
|
||||
//
|
||||
// Slice is assigned by reference without copying.
|
||||
func (c *Container) SetSalt(v []byte) {
|
||||
c.salt = v
|
||||
}
|
||||
|
||||
// BasicACL returns the mask of basic container permissions.
|
||||
func (c *Container) BasicACL() BasicACL {
|
||||
return c.basicACL
|
||||
}
|
||||
|
||||
// SetBasicACL sets the mask of basic container permissions.
|
||||
func (c *Container) SetBasicACL(v BasicACL) {
|
||||
c.basicACL = v
|
||||
}
|
||||
|
||||
// PlacementRule returns placement rule of the container.
|
||||
func (c *Container) PlacementRule() PlacementRule {
|
||||
return c.placementRule
|
||||
}
|
||||
|
||||
// SetPlacementRule sets placement rule of the container.
|
||||
func (c *Container) SetPlacementRule(v PlacementRule) {
|
||||
c.placementRule = v
|
||||
}
|
|
@ -1,30 +0,0 @@
|
|||
package container
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/nspcc-dev/neofs-node/pkg/core/container/acl/basic"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestContainerMethods(t *testing.T) {
|
||||
c := new(Container)
|
||||
|
||||
acl := basic.FromUint32(1)
|
||||
c.SetBasicACL(acl)
|
||||
require.True(t, basic.Equal(acl, c.BasicACL()))
|
||||
|
||||
ownerID := OwnerID{1, 2, 3}
|
||||
c.SetOwnerID(ownerID)
|
||||
require.Equal(t, ownerID, c.OwnerID())
|
||||
|
||||
salt := []byte{4, 5, 6}
|
||||
c.SetSalt(salt)
|
||||
require.Equal(t, salt, c.Salt())
|
||||
|
||||
rule := PlacementRule{
|
||||
ReplFactor: 1,
|
||||
}
|
||||
c.SetPlacementRule(rule)
|
||||
require.Equal(t, rule, c.PlacementRule())
|
||||
}
|
|
@ -1,49 +0,0 @@
|
|||
package container
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
|
||||
"github.com/nspcc-dev/neofs-api-go/refs"
|
||||
)
|
||||
|
||||
// ID represents the
|
||||
// container identifier.
|
||||
//
|
||||
// It is a type alias of
|
||||
// github.com/nspcc-dev/neofs-api-go/refs.CID.
|
||||
// FIXME: container id should be defined in core package.
|
||||
type ID = refs.CID
|
||||
|
||||
// OwnerID represents the
|
||||
// container owner identifier.
|
||||
//
|
||||
// It is a type alias of
|
||||
// github.com/nspcc-dev/neofs-api-go/refs.OwnerID.
|
||||
// FIXME: owner ID should be defined in core lib.
|
||||
type OwnerID = refs.OwnerID
|
||||
|
||||
// OwnerIDSize is a size of OwnerID
|
||||
// in a binary form.
|
||||
const OwnerIDSize = refs.OwnerIDSize
|
||||
|
||||
// CalculateID calculates container identifier
|
||||
// as SHA256 checksum of the binary form.
|
||||
//
|
||||
// If container is nil, ErrNilContainer is returned.
|
||||
func CalculateID(cnr *Container) (*ID, error) {
|
||||
if cnr == nil {
|
||||
return nil, ErrNilContainer
|
||||
}
|
||||
|
||||
data, err := cnr.MarshalBinary()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
res := new(ID)
|
||||
sh := sha256.Sum256(data)
|
||||
|
||||
copy(res[:], sh[:])
|
||||
|
||||
return res, nil
|
||||
}
|
|
@ -1,38 +0,0 @@
|
|||
package container
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"testing"
|
||||
|
||||
"github.com/nspcc-dev/neofs-node/pkg/core/container/acl/basic"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestCalculateID(t *testing.T) {
|
||||
_, err := CalculateID(nil)
|
||||
require.True(t, errors.Is(err, ErrNilContainer))
|
||||
|
||||
cnr := new(Container)
|
||||
cnr.SetBasicACL(basic.FromUint32(1))
|
||||
cnr.SetOwnerID(OwnerID{1, 2, 3})
|
||||
cnr.SetSalt([]byte{4, 5, 6})
|
||||
|
||||
id1, err := CalculateID(cnr)
|
||||
require.NoError(t, err)
|
||||
|
||||
data, err := cnr.MarshalBinary()
|
||||
require.NoError(t, err)
|
||||
|
||||
sh := sha256.Sum256(data)
|
||||
|
||||
require.Equal(t, id1.Bytes(), sh[:])
|
||||
|
||||
// change the container
|
||||
cnr.SetSalt(append(cnr.Salt(), 1))
|
||||
|
||||
id2, err := CalculateID(cnr)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.NotEqual(t, id1, id2)
|
||||
}
|
|
@ -1,75 +0,0 @@
|
|||
package container
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"io"
|
||||
|
||||
"github.com/nspcc-dev/neofs-node/pkg/core/container/acl/basic"
|
||||
)
|
||||
|
||||
const (
|
||||
saltLenSize = 2
|
||||
|
||||
fixedSize = 0 +
|
||||
basic.Size +
|
||||
OwnerIDSize +
|
||||
saltLenSize
|
||||
)
|
||||
|
||||
// MarshalBinary encodes the container into a binary form
|
||||
// and returns the result.
|
||||
func (c *Container) MarshalBinary() ([]byte, error) {
|
||||
data := make([]byte, binaryContainerSize(c))
|
||||
|
||||
off := copy(data, basic.Marshal(c.basicACL))
|
||||
|
||||
off += copy(data[off:], c.ownerID.Bytes())
|
||||
|
||||
binary.BigEndian.PutUint16(data[off:], uint16(len(c.salt)))
|
||||
off += saltLenSize
|
||||
|
||||
off += copy(data[off:], c.salt)
|
||||
|
||||
if _, err := c.placementRule.MarshalTo(data[off:]); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return data, nil
|
||||
}
|
||||
|
||||
// UnmarshalBinary unmarshals container from a binary
|
||||
// representation.
|
||||
//
|
||||
// If buffer size is insufficient, io.ErrUnexpectedEOF is returned.
|
||||
func (c *Container) UnmarshalBinary(data []byte) error {
|
||||
if len(data) < binaryContainerSize(c) {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
|
||||
if err := c.basicACL.UnmarshalBinary(data); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
off := basic.Size
|
||||
|
||||
off += copy(c.ownerID[:], data[off:])
|
||||
|
||||
saltLen := binary.BigEndian.Uint16(data[off:])
|
||||
off += saltLenSize
|
||||
|
||||
c.salt = make([]byte, saltLen)
|
||||
off += copy(c.salt, data[off:])
|
||||
|
||||
if err := c.placementRule.Unmarshal(data[off:]); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// returns the length of the container in binary form.
|
||||
func binaryContainerSize(cnr *Container) int {
|
||||
return fixedSize +
|
||||
len(cnr.salt) +
|
||||
cnr.placementRule.Size()
|
||||
}
|
|
@ -1,26 +0,0 @@
|
|||
package container
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/nspcc-dev/neofs-node/pkg/core/container/acl/basic"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestContainerMarshal(t *testing.T) {
|
||||
srcCnr := new(Container)
|
||||
srcCnr.SetBasicACL(basic.FromUint32(1))
|
||||
srcCnr.SetOwnerID(OwnerID{1, 2, 3})
|
||||
srcCnr.SetSalt([]byte{4, 5, 6})
|
||||
srcCnr.SetPlacementRule(PlacementRule{
|
||||
ReplFactor: 3,
|
||||
})
|
||||
|
||||
data, err := srcCnr.MarshalBinary()
|
||||
require.NoError(t, err)
|
||||
|
||||
dstCnr := new(Container)
|
||||
require.NoError(t, dstCnr.UnmarshalBinary(data))
|
||||
|
||||
require.Equal(t, srcCnr, dstCnr)
|
||||
}
|
|
@ -1,79 +0,0 @@
|
|||
package storage
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/nspcc-dev/neofs-node/pkg/core/container"
|
||||
)
|
||||
|
||||
// Container represents the NeoFS container.
|
||||
//
|
||||
// It is a type alias of
|
||||
// github.com/nspcc-dev/neofs-node/pkg/core/container.Container.
|
||||
type Container = container.Container
|
||||
|
||||
// OwnerID represents the container
|
||||
// owner identifier.
|
||||
//
|
||||
// It is a type alias of
|
||||
// github.com/nspcc-dev/neofs-node/pkg/core/container.OwnerID.
|
||||
type OwnerID = container.OwnerID
|
||||
|
||||
// CID represents the container identifier.
|
||||
//
|
||||
// It is a type alias of
|
||||
// github.com/nspcc-dev/neofs-node/pkg/core/container.ID.
|
||||
type CID = container.ID
|
||||
|
||||
// Storage is an interface that wraps
|
||||
// basic container storage methods.
|
||||
type Storage interface {
|
||||
// Put saves pointed container to the underlying storage.
|
||||
// It returns calculated container identifier and any error
|
||||
// encountered that caused the saving to interrupt.
|
||||
//
|
||||
// Put must return container.ErrNilContainer on nil-pointer.
|
||||
//
|
||||
// Implementations must not modify the container through the pointer (even temporarily).
|
||||
// Implementations must not retain the container pointer.
|
||||
//
|
||||
// Container rewriting behavior is dictated by implementation.
|
||||
Put(*Container) (*CID, error)
|
||||
|
||||
// Get reads the container from the storage by identifier.
|
||||
// It returns the pointer to requested container and any error encountered.
|
||||
//
|
||||
// Get must return exactly one non-nil value.
|
||||
// Get must return ErrNotFound if the container is not in storage.
|
||||
//
|
||||
// Implementations must not retain the container pointer and modify
|
||||
// the container through it.
|
||||
Get(CID) (*Container, error)
|
||||
|
||||
// Delete removes the container from the storage.
|
||||
// It returns any error encountered that caused the deletion to interrupt.
|
||||
//
|
||||
// Delete must return nil if container was successfully deleted.
|
||||
//
|
||||
// Behavior when deleting a nonexistent container is dictated by implementation.
|
||||
Delete(CID) error
|
||||
|
||||
// List returns a list of container identifiers belonging to the specified owner.
|
||||
// It returns any error encountered that caused the listing to interrupt.
|
||||
//
|
||||
// List must return the identifiers of all stored containers if owner pointer is nil.
|
||||
// List must return the empty list and no error in the absence of containers in storage.
|
||||
//
|
||||
// Result slice can be either empty slice or nil, so empty list should be checked
|
||||
// by comparing with zero length (not nil).
|
||||
//
|
||||
// Callers should carefully handle the incomplete list in case of interrupt error.
|
||||
List(*OwnerID) ([]CID, error)
|
||||
}
|
||||
|
||||
// ErrNotFound is the error returned when container was not found in storage.
|
||||
var ErrNotFound = errors.New("container not found")
|
||||
|
||||
// ErrNilStorage is the error returned by functions that
|
||||
// expect a non-nil container storage implementation, but received nil.
|
||||
var ErrNilStorage = errors.New("container storage is nil")
|
|
@ -1,127 +0,0 @@
|
|||
package test
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"testing"
|
||||
|
||||
"github.com/nspcc-dev/neofs-node/pkg/core/container"
|
||||
"github.com/nspcc-dev/neofs-node/pkg/core/container/storage"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
type testStorage struct {
|
||||
*sync.RWMutex
|
||||
|
||||
items map[container.ID]*container.Container
|
||||
}
|
||||
|
||||
func (s *testStorage) Put(cnr *storage.Container) (*storage.CID, error) {
|
||||
if cnr == nil {
|
||||
return nil, container.ErrNilContainer
|
||||
}
|
||||
|
||||
cid, err := container.CalculateID(cnr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
s.Lock()
|
||||
s.items[*cid] = cnr
|
||||
s.Unlock()
|
||||
|
||||
return cid, nil
|
||||
}
|
||||
|
||||
func (s *testStorage) Get(cid storage.CID) (*storage.Container, error) {
|
||||
s.RLock()
|
||||
cnr, ok := s.items[cid]
|
||||
s.RUnlock()
|
||||
|
||||
if !ok {
|
||||
return nil, storage.ErrNotFound
|
||||
}
|
||||
|
||||
return cnr, nil
|
||||
}
|
||||
|
||||
func (s *testStorage) Delete(cid storage.CID) error {
|
||||
s.Lock()
|
||||
delete(s.items, cid)
|
||||
s.Unlock()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *testStorage) List(ownerID *storage.OwnerID) ([]storage.CID, error) {
|
||||
s.RLock()
|
||||
defer s.RUnlock()
|
||||
|
||||
res := make([]storage.CID, 0)
|
||||
|
||||
for cid, cnr := range s.items {
|
||||
if ownerID == nil || ownerID.Equal(cnr.OwnerID()) {
|
||||
res = append(res, cid)
|
||||
}
|
||||
}
|
||||
|
||||
return res, nil
|
||||
}
|
||||
|
||||
// New creates new container storage
|
||||
// that stores containers in go-builtin map.
|
||||
func New() storage.Storage {
|
||||
return &testStorage{
|
||||
RWMutex: new(sync.RWMutex),
|
||||
items: make(map[container.ID]*container.Container),
|
||||
}
|
||||
}
|
||||
|
||||
// Storage conducts testing of container
|
||||
// storage for interface specification.
|
||||
//
|
||||
// Storage must be empty.
|
||||
func Storage(t *testing.T, s storage.Storage) {
|
||||
list, err := s.List(nil)
|
||||
require.NoError(t, err)
|
||||
require.Empty(t, list)
|
||||
|
||||
cnr1 := new(container.Container)
|
||||
cnr1.SetOwnerID(container.OwnerID{1, 2, 3})
|
||||
|
||||
id1, err := s.Put(cnr1)
|
||||
require.NoError(t, err)
|
||||
|
||||
res, err := s.Get(*id1)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, cnr1, res)
|
||||
|
||||
cnr2 := new(container.Container)
|
||||
owner1 := cnr1.OwnerID()
|
||||
owner1[0]++
|
||||
cnr2.SetOwnerID(owner1)
|
||||
|
||||
id2, err := s.Put(cnr2)
|
||||
require.NoError(t, err)
|
||||
|
||||
res, err = s.Get(*id2)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, cnr2, res)
|
||||
|
||||
list, err = s.List(nil)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, list, 2)
|
||||
require.Contains(t, list, *id1)
|
||||
require.Contains(t, list, *id2)
|
||||
|
||||
owner1 = cnr1.OwnerID()
|
||||
list, err = s.List(&owner1)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, list, 1)
|
||||
require.Equal(t, *id1, list[0])
|
||||
|
||||
owner2 := cnr2.OwnerID()
|
||||
list, err = s.List(&owner2)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, list, 1)
|
||||
require.Equal(t, *id2, list[0])
|
||||
}
|
|
@ -1,11 +0,0 @@
|
|||
package test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestNewStorage(t *testing.T) {
|
||||
s := New()
|
||||
|
||||
Storage(t, s)
|
||||
}
|
0
pkg/core/netmap/.gitkeep
Normal file
0
pkg/core/netmap/.gitkeep
Normal file
|
@ -1,37 +0,0 @@
|
|||
package epoch
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"io"
|
||||
)
|
||||
|
||||
// Size is a size of Epoch
|
||||
// in a binary form.
|
||||
const Size = 8
|
||||
|
||||
// Marshal encodes Epoch into a
|
||||
// binary form and returns the result.
|
||||
//
|
||||
// Result slice has Size length.
|
||||
func Marshal(e Epoch) []byte {
|
||||
d := make([]byte, Size)
|
||||
|
||||
binary.BigEndian.PutUint64(d, ToUint64(e))
|
||||
|
||||
return d
|
||||
}
|
||||
|
||||
// UnmarshalBinary unmarshals Epoch from
|
||||
// a binary representation.
|
||||
//
|
||||
// If buffer size is insufficient,
|
||||
// io.ErrUnexpectedEOF is returned.
|
||||
func (e *Epoch) UnmarshalBinary(data []byte) error {
|
||||
if len(data) < Size {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
|
||||
*e = FromUint64(binary.BigEndian.Uint64(data))
|
||||
|
||||
return nil
|
||||
}
|
|
@ -1,20 +0,0 @@
|
|||
package epoch
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestEpochMarshal(t *testing.T) {
|
||||
e := FromUint64(1)
|
||||
e2 := new(Epoch)
|
||||
|
||||
require.NoError(t,
|
||||
e2.UnmarshalBinary(
|
||||
Marshal(e),
|
||||
),
|
||||
)
|
||||
|
||||
require.True(t, EQ(e, *e2))
|
||||
}
|
|
@ -1,12 +0,0 @@
|
|||
package epoch
|
||||
|
||||
// Sum returns the result of
|
||||
// summing up two Epoch.
|
||||
//
|
||||
// Function defines a binary
|
||||
// operation of summing two Epoch.
|
||||
// Try to avoid using operator
|
||||
// "+" for better portability.
|
||||
func Sum(a, b Epoch) Epoch {
|
||||
return FromUint64(ToUint64(a) + ToUint64(b))
|
||||
}
|
|
@ -1,28 +0,0 @@
|
|||
package epoch
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestEpochMath(t *testing.T) {
|
||||
items := []struct {
|
||||
mathFn func(Epoch, Epoch) Epoch
|
||||
|
||||
a, b, c uint64
|
||||
}{
|
||||
{
|
||||
mathFn: Sum, a: 1, b: 2, c: 3},
|
||||
}
|
||||
|
||||
for _, item := range items {
|
||||
require.Equal(t,
|
||||
item.mathFn(
|
||||
FromUint64(item.a),
|
||||
FromUint64(item.b),
|
||||
),
|
||||
FromUint64(item.c),
|
||||
)
|
||||
}
|
||||
}
|
|
@ -1,55 +0,0 @@
|
|||
package epoch
|
||||
|
||||
// EQ reports whether e and e2 are the same Epoch.
|
||||
//
|
||||
// Function defines the relation of equality
|
||||
// between two Epoch. Try to avoid comparison through
|
||||
// "==" operator for better portability.
|
||||
func EQ(e1, e2 Epoch) bool {
|
||||
return ToUint64(e1) == ToUint64(e2)
|
||||
}
|
||||
|
||||
// NE reports whether e1 and e2 are the different Epoch.
|
||||
//
|
||||
// Method defines the relation of inequality
|
||||
// between two Epoch. Try to avoid comparison through
|
||||
// "!=" operator for better portability.
|
||||
func NE(e1, e2 Epoch) bool {
|
||||
return ToUint64(e1) != ToUint64(e2)
|
||||
}
|
||||
|
||||
// LT reports whether e1 is less Epoch than e2.
|
||||
//
|
||||
// Method defines the "less than" relation
|
||||
// between two Epoch. Try to avoid comparison through
|
||||
// "<" operator for better portability.
|
||||
func LT(e1, e2 Epoch) bool {
|
||||
return ToUint64(e1) < ToUint64(e2)
|
||||
}
|
||||
|
||||
// GT reports whether e1 is greater Epoch than e2.
|
||||
//
|
||||
// Method defines the "greater than" relation
|
||||
// between two Epoch. Try to avoid comparison through
|
||||
// ">" operator for better portability.
|
||||
func GT(e1, e2 Epoch) bool {
|
||||
return ToUint64(e1) > ToUint64(e2)
|
||||
}
|
||||
|
||||
// LE reports whether e1 is less or equal Epoch than e2.
|
||||
//
|
||||
// Method defines the "less or equal" relation
|
||||
// between two Epoch. Try to avoid comparison through
|
||||
// "<=" operator for better portability.
|
||||
func LE(e1, e2 Epoch) bool {
|
||||
return ToUint64(e1) <= ToUint64(e2)
|
||||
}
|
||||
|
||||
// GE reports whether e1 is greater or equal Epoch than e2.
|
||||
//
|
||||
// Method defines the "greater or equal" relation
|
||||
// between two Epoch. Try to avoid comparison through
|
||||
// ">=" operator for better portability.
|
||||
func GE(e1, e2 Epoch) bool {
|
||||
return ToUint64(e1) >= ToUint64(e2)
|
||||
}
|
|
@ -1,40 +0,0 @@
|
|||
package epoch
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestEpochRelations(t *testing.T) {
|
||||
items := []struct {
|
||||
relFn func(Epoch, Epoch) bool
|
||||
|
||||
base, ok, fail uint64
|
||||
}{
|
||||
{relFn: EQ, base: 1, ok: 1, fail: 2},
|
||||
{relFn: NE, base: 1, ok: 2, fail: 1},
|
||||
{relFn: LT, base: 1, ok: 2, fail: 0},
|
||||
{relFn: GT, base: 1, ok: 0, fail: 2},
|
||||
{relFn: LE, base: 1, ok: 1, fail: 0},
|
||||
{relFn: LE, base: 1, ok: 2, fail: 0},
|
||||
{relFn: GE, base: 1, ok: 0, fail: 2},
|
||||
{relFn: GE, base: 1, ok: 1, fail: 2},
|
||||
}
|
||||
|
||||
for _, item := range items {
|
||||
require.True(t,
|
||||
item.relFn(
|
||||
FromUint64(item.base),
|
||||
FromUint64(item.ok),
|
||||
),
|
||||
)
|
||||
|
||||
require.False(t,
|
||||
item.relFn(
|
||||
FromUint64(item.base),
|
||||
FromUint64(item.fail),
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
|
@ -1,23 +0,0 @@
|
|||
package epoch
|
||||
|
||||
// Epoch represents the
|
||||
// number of NeoFS epoch.
|
||||
type Epoch uint64
|
||||
|
||||
// FromUint64 converts builtin
|
||||
// uint64 value to Epoch.
|
||||
//
|
||||
// Try to avoid direct cast for
|
||||
// better portability.
|
||||
func FromUint64(e uint64) Epoch {
|
||||
return Epoch(e)
|
||||
}
|
||||
|
||||
// ToUint64 converts Epoch value
|
||||
// to builtin uint64.
|
||||
//
|
||||
// Try to avoid direct cast for
|
||||
// better portability.
|
||||
func ToUint64(e Epoch) uint64 {
|
||||
return uint64(e)
|
||||
}
|
|
@ -1,119 +0,0 @@
|
|||
package netmap
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"sync"
|
||||
|
||||
"github.com/nspcc-dev/neofs-node/pkg/core/netmap/node"
|
||||
"github.com/nspcc-dev/netmap"
|
||||
)
|
||||
|
||||
// Info represent node information.
|
||||
//
|
||||
// It is a type alias of
|
||||
// github.com/nspcc-dev/neofs-node/pkg/core/netmap/node.Info.
|
||||
type Info = node.Info
|
||||
|
||||
// Bucket represents NeoFS network map as a graph.
|
||||
//
|
||||
// If is a type alias of
|
||||
// github.com/nspcc-dev/netmap.Bucket.
|
||||
type Bucket = netmap.Bucket
|
||||
|
||||
// NetMap represents NeoFS network map
|
||||
// with concurrent access support.
|
||||
type NetMap struct {
|
||||
mtx *sync.RWMutex
|
||||
|
||||
root *Bucket
|
||||
|
||||
items []Info
|
||||
}
|
||||
|
||||
// New creates and initializes a new NetMap.
|
||||
//
|
||||
// Using the NetMap that has been created with new(NetMap)
|
||||
// expression (or just declaring a NetMap variable) is unsafe
|
||||
// and can lead to panic.
|
||||
func New() *NetMap {
|
||||
return &NetMap{
|
||||
mtx: new(sync.RWMutex),
|
||||
root: new(Bucket),
|
||||
}
|
||||
}
|
||||
|
||||
// Root returns the root bucket of the network map.
|
||||
//
|
||||
// Changing the result is unsafe and
|
||||
// affects the network map.
|
||||
func (n NetMap) Root() *Bucket {
|
||||
n.mtx.RLock()
|
||||
defer n.mtx.RUnlock()
|
||||
|
||||
return n.root
|
||||
}
|
||||
|
||||
// SetRoot sets the root bucket of the network map.
|
||||
//
|
||||
// Subsequent changing the source bucket
|
||||
// is unsafe and affects the network map.
|
||||
func (n *NetMap) SetRoot(v *Bucket) {
|
||||
n.mtx.Lock()
|
||||
n.root = v
|
||||
n.mtx.Unlock()
|
||||
}
|
||||
|
||||
// Nodes returns node list of the network map.
|
||||
//
|
||||
// Changing the result is unsafe and
|
||||
// affects the network map.
|
||||
func (n NetMap) Nodes() []Info {
|
||||
n.mtx.RLock()
|
||||
defer n.mtx.RUnlock()
|
||||
|
||||
return n.items
|
||||
}
|
||||
|
||||
// SetNodes sets node list of the network map.
|
||||
//
|
||||
// Subsequent changing the source slice
|
||||
// is unsafe and affects the network map.
|
||||
func (n *NetMap) SetNodes(v []Info) {
|
||||
n.mtx.Lock()
|
||||
n.items = v
|
||||
n.mtx.Unlock()
|
||||
}
|
||||
|
||||
// AddNode adds node information to the network map
|
||||
//
|
||||
// If node with provided information is already presented
|
||||
// in network map, nothing happens,
|
||||
func (n *NetMap) AddNode(nodeInfo Info) error {
|
||||
n.mtx.Lock()
|
||||
defer n.mtx.Unlock()
|
||||
|
||||
num := -1
|
||||
|
||||
// looking for existed node info item
|
||||
for i := range n.items {
|
||||
if bytes.Equal(
|
||||
n.items[i].PublicKey(),
|
||||
nodeInfo.PublicKey(),
|
||||
) {
|
||||
num = i
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// add node if it does not exist
|
||||
if num < 0 {
|
||||
n.items = append(n.items, nodeInfo)
|
||||
num = len(n.items) - 1
|
||||
}
|
||||
|
||||
return n.root.AddStrawNode(netmap.Node{
|
||||
N: uint32(num),
|
||||
C: n.items[num].Capacity(),
|
||||
P: n.items[num].Price(),
|
||||
}, nodeInfo.Options()...)
|
||||
}
|
|
@ -1,39 +0,0 @@
|
|||
package netmap
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestNetMap_Nodes(t *testing.T) {
|
||||
nm := New()
|
||||
|
||||
info1 := Info{}
|
||||
info1.SetPublicKey([]byte{1, 2, 3})
|
||||
|
||||
info2 := Info{}
|
||||
info2.SetPublicKey([]byte{4, 5, 6})
|
||||
|
||||
nodes := []Info{
|
||||
info1,
|
||||
info2,
|
||||
}
|
||||
|
||||
nm.SetNodes(nodes)
|
||||
|
||||
require.Equal(t, nodes, nm.Nodes())
|
||||
}
|
||||
|
||||
func TestNetMap_Root(t *testing.T) {
|
||||
nm := New()
|
||||
|
||||
bucket := &Bucket{
|
||||
Key: "key",
|
||||
Value: "value",
|
||||
}
|
||||
|
||||
nm.SetRoot(bucket)
|
||||
|
||||
require.Equal(t, bucket, nm.Root())
|
||||
}
|
|
@ -1,154 +0,0 @@
|
|||
package node
|
||||
|
||||
import (
|
||||
"errors"
|
||||
)
|
||||
|
||||
// Info represents the information
|
||||
// about NeoFS storage node.
|
||||
type Info struct {
|
||||
address string // net address
|
||||
|
||||
key []byte // public key
|
||||
|
||||
opts []string // options
|
||||
|
||||
status Status // status bits
|
||||
}
|
||||
|
||||
// ErrNilInfo is returned by functions that expect
|
||||
// a non-nil Info pointer, but received nil.
|
||||
var ErrNilInfo = errors.New("node info is nil")
|
||||
|
||||
// Address returns node network address.
|
||||
//
|
||||
// Address format is dictated by
|
||||
// application architecture.
|
||||
func (i Info) Address() string {
|
||||
return i.address
|
||||
}
|
||||
|
||||
// SetAddress sets node network address.
|
||||
func (i *Info) SetAddress(v string) {
|
||||
i.address = v
|
||||
}
|
||||
|
||||
// Status returns the node status.
|
||||
func (i Info) Status() Status {
|
||||
return i.status
|
||||
}
|
||||
|
||||
// SetStatus sets the node status.
|
||||
func (i *Info) SetStatus(v Status) {
|
||||
i.status = v
|
||||
}
|
||||
|
||||
// PublicKey returns node public key in
|
||||
// a binary format.
|
||||
//
|
||||
// Changing the result is unsafe and
|
||||
// affects the node info. In order to
|
||||
// prevent state mutations, use
|
||||
// CopyPublicKey.
|
||||
//
|
||||
// Key format is dictated by
|
||||
// application architecture.
|
||||
func (i Info) PublicKey() []byte {
|
||||
return i.key
|
||||
}
|
||||
|
||||
// CopyPublicKey returns the copy of
|
||||
// node public key.
|
||||
//
|
||||
// Changing the result is safe and
|
||||
// does not affect the node info.
|
||||
func CopyPublicKey(i Info) []byte {
|
||||
res := make([]byte, len(i.key))
|
||||
|
||||
copy(res, i.key)
|
||||
|
||||
return res
|
||||
}
|
||||
|
||||
// SetPublicKey sets node public key
|
||||
// in a binary format.
|
||||
//
|
||||
// Subsequent changing the source slice
|
||||
// is unsafe and affects node info.
|
||||
// In order to prevent state mutations,
|
||||
// use SetPublicKeyCopy.
|
||||
func (i *Info) SetPublicKey(v []byte) {
|
||||
i.key = v
|
||||
}
|
||||
|
||||
// SetPublicKeyCopy copies public key and
|
||||
// sets the copy as node public key.
|
||||
//
|
||||
// Subsequent changing the source slice
|
||||
// is safe and does not affect node info.
|
||||
//
|
||||
// Returns ErrNilInfo on nil node info.
|
||||
func SetPublicKeyCopy(i *Info, key []byte) error {
|
||||
if i == nil {
|
||||
return ErrNilInfo
|
||||
}
|
||||
|
||||
i.key = make([]byte, len(key))
|
||||
|
||||
copy(i.key, key)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Options returns node option list.
|
||||
//
|
||||
// Changing the result is unsafe and
|
||||
// affects the node info. In order to
|
||||
// prevent state mutations, use
|
||||
// CopyOptions.
|
||||
//
|
||||
// Option format is dictated by
|
||||
// application architecture.
|
||||
func (i Info) Options() []string {
|
||||
return i.opts
|
||||
}
|
||||
|
||||
// CopyOptions returns the copy of
|
||||
// node options list.
|
||||
//
|
||||
// Changing the result is safe and
|
||||
// does not affect the node info.
|
||||
func CopyOptions(i Info) []string {
|
||||
res := make([]string, len(i.opts))
|
||||
|
||||
copy(res, i.opts)
|
||||
|
||||
return res
|
||||
}
|
||||
|
||||
// SetOptions sets node option list.
|
||||
//
|
||||
// Subsequent changing the source slice
|
||||
// is unsafe and affects node info.
|
||||
// In order to prevent state mutations,
|
||||
// use SetOptionsCopy.
|
||||
func (i *Info) SetOptions(v []string) {
|
||||
i.opts = v
|
||||
}
|
||||
|
||||
// SetOptionsCopy copies option list and sets
|
||||
// the copy as node options list.
|
||||
//
|
||||
// Subsequent changing the source slice
|
||||
// is safe and does not affect node info.
|
||||
//
|
||||
// SetOptionsCopy does nothing if Info is nil.
|
||||
func SetOptionsCopy(i *Info, opts []string) {
|
||||
if i == nil {
|
||||
return
|
||||
}
|
||||
|
||||
i.opts = make([]string, len(opts))
|
||||
|
||||
copy(i.opts, opts)
|
||||
}
|
|
@ -1,133 +0,0 @@
|
|||
package node
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestInfo_Address(t *testing.T) {
|
||||
i := new(Info)
|
||||
|
||||
addr := "address"
|
||||
i.SetAddress(addr)
|
||||
|
||||
require.Equal(t, addr, i.Address())
|
||||
}
|
||||
|
||||
func TestInfo_Status(t *testing.T) {
|
||||
i := new(Info)
|
||||
|
||||
st := StatusFromUint64(1)
|
||||
i.SetStatus(st)
|
||||
|
||||
require.Equal(t, st, i.Status())
|
||||
}
|
||||
|
||||
func TestInfo_PublicKey(t *testing.T) {
|
||||
i := new(Info)
|
||||
|
||||
key := []byte{1, 2, 3}
|
||||
i.SetPublicKey(key)
|
||||
|
||||
require.Equal(t, key, i.PublicKey())
|
||||
}
|
||||
|
||||
func TestCopyPublicKey(t *testing.T) {
|
||||
i := Info{}
|
||||
|
||||
// set initial node key
|
||||
initKey := []byte{1, 2, 3}
|
||||
i.SetPublicKey(initKey)
|
||||
|
||||
// get node key copy
|
||||
keyCopy := CopyPublicKey(i)
|
||||
|
||||
// change the copy
|
||||
keyCopy[0]++
|
||||
|
||||
// check that node key has not changed
|
||||
require.Equal(t, initKey, i.PublicKey())
|
||||
}
|
||||
|
||||
func TestSetPublicKeyCopy(t *testing.T) {
|
||||
require.EqualError(t,
|
||||
SetPublicKeyCopy(nil, nil),
|
||||
ErrNilInfo.Error(),
|
||||
)
|
||||
|
||||
i := new(Info)
|
||||
|
||||
// create source key
|
||||
srcKey := []byte{1, 2, 3}
|
||||
|
||||
// copy and set node key
|
||||
require.NoError(t, SetPublicKeyCopy(i, srcKey))
|
||||
|
||||
// get node key
|
||||
nodeKey := i.PublicKey()
|
||||
|
||||
// change the source key
|
||||
srcKey[0]++
|
||||
|
||||
// check that node key has not changed
|
||||
require.Equal(t, nodeKey, i.PublicKey())
|
||||
}
|
||||
|
||||
func TestInfo_Options(t *testing.T) {
|
||||
i := new(Info)
|
||||
|
||||
opts := []string{
|
||||
"opt1",
|
||||
"opt2",
|
||||
}
|
||||
i.SetOptions(opts)
|
||||
|
||||
require.Equal(t, opts, i.Options())
|
||||
}
|
||||
|
||||
func TestCopyOptions(t *testing.T) {
|
||||
i := Info{}
|
||||
|
||||
// set initial node options
|
||||
initOpts := []string{
|
||||
"opt1",
|
||||
"opt2",
|
||||
}
|
||||
i.SetOptions(initOpts)
|
||||
|
||||
// get node options copy
|
||||
optsCopy := CopyOptions(i)
|
||||
|
||||
// change the copy
|
||||
optsCopy[0] = "some other opt"
|
||||
|
||||
// check that node options have not changed
|
||||
require.Equal(t, initOpts, i.Options())
|
||||
}
|
||||
|
||||
func TestSetOptionsCopy(t *testing.T) {
|
||||
require.NotPanics(t, func() {
|
||||
SetOptionsCopy(nil, nil)
|
||||
})
|
||||
|
||||
i := new(Info)
|
||||
|
||||
// create source options
|
||||
srcOpts := []string{
|
||||
"opt1",
|
||||
"opt2",
|
||||
}
|
||||
|
||||
// copy and set node options
|
||||
SetOptionsCopy(i, srcOpts)
|
||||
|
||||
// get node options
|
||||
nodeOpts := i.Options()
|
||||
|
||||
// change the source options
|
||||
srcOpts[0] = "some other opt"
|
||||
|
||||
// check that node options have not changed
|
||||
require.Equal(t, nodeOpts, i.Options())
|
||||
}
|
|
@ -1,46 +0,0 @@
|
|||
package node
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/nspcc-dev/neofs-api-go/object"
|
||||
)
|
||||
|
||||
const optionPrice = "/Price:"
|
||||
|
||||
const optionCapacity = "/Capacity:"
|
||||
|
||||
// Price parses node options and returns the price in 1e-8*GAS/Megabyte per month.
|
||||
//
|
||||
// User sets the price in GAS/Terabyte per month.
|
||||
func (i Info) Price() uint64 {
|
||||
for j := range i.opts {
|
||||
if strings.HasPrefix(i.opts[j], optionPrice) {
|
||||
n, err := strconv.ParseFloat(i.opts[j][len(optionPrice):], 64)
|
||||
if err != nil {
|
||||
return 0
|
||||
}
|
||||
|
||||
return uint64(n*1e8) / uint64(object.UnitsMB) // UnitsMB == megabytes in 1 terabyte
|
||||
}
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
// Capacity parses node options and returns the capacity .
|
||||
func (i Info) Capacity() uint64 {
|
||||
for j := range i.opts {
|
||||
if strings.HasPrefix(i.opts[j], optionCapacity) {
|
||||
n, err := strconv.ParseUint(i.opts[j][len(optionCapacity):], 10, 64)
|
||||
if err != nil {
|
||||
return 0
|
||||
}
|
||||
|
||||
return n
|
||||
}
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
|
@ -1,24 +0,0 @@
|
|||
package node
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/nspcc-dev/neofs-api-go/object"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestInfo_Price(t *testing.T) {
|
||||
var info Info
|
||||
|
||||
// too small value
|
||||
info.opts = []string{"/Price:0.01048575"}
|
||||
require.Equal(t, uint64(0), info.Price())
|
||||
|
||||
// min value
|
||||
info.opts = []string{"/Price:0.01048576"}
|
||||
require.Equal(t, uint64(1), info.Price())
|
||||
|
||||
// big value
|
||||
info.opts = []string{"/Price:1000000000.666"}
|
||||
require.Equal(t, uint64(1000000000.666*1e8/object.UnitsMB), info.Price())
|
||||
}
|
|
@ -1,63 +0,0 @@
|
|||
package node
|
||||
|
||||
// Status represents a node
|
||||
// status bits.
|
||||
type Status uint64
|
||||
|
||||
const leftBitPos = 64
|
||||
|
||||
const (
|
||||
bitFullStorage = 1
|
||||
)
|
||||
|
||||
// returns true if n-th left bit is set (starting at 0).
|
||||
func isLeftBitSet(value Status, n uint8) bool {
|
||||
bitMask := Status(1 << (leftBitPos - n))
|
||||
return bitMask != 0 && value&bitMask == bitMask
|
||||
}
|
||||
|
||||
// sets n-th left bit (starting at 0).
|
||||
func setLeftBit(value *Status, n uint8) {
|
||||
*value |= Status(1 << (leftBitPos - n))
|
||||
}
|
||||
|
||||
// resets n-th left bit (starting at 0).
|
||||
func resetLeftBit(value *Status, n uint8) {
|
||||
*value &= ^Status(1 << (leftBitPos - n))
|
||||
}
|
||||
|
||||
// Full returns true if node is in Full status.
|
||||
//
|
||||
// Full status marks node has enough space
|
||||
// for storing users objects.
|
||||
func (n Status) Full() bool {
|
||||
return isLeftBitSet(n, bitFullStorage)
|
||||
}
|
||||
|
||||
// SetFull sets Full status of node.
|
||||
func (n *Status) SetFull() {
|
||||
setLeftBit(n, bitFullStorage)
|
||||
}
|
||||
|
||||
// ResetFull resets Full status of node.
|
||||
func (n *Status) ResetFull() {
|
||||
resetLeftBit(n, bitFullStorage)
|
||||
}
|
||||
|
||||
// StatusFromUint64 converts builtin
|
||||
// uint64 value to Status.
|
||||
//
|
||||
// Try to avoid direct cast for
|
||||
// better portability.
|
||||
func StatusFromUint64(v uint64) Status {
|
||||
return Status(v)
|
||||
}
|
||||
|
||||
// StatusToUint64 converts Status value
|
||||
// to builtin uint64.
|
||||
//
|
||||
// Try to avoid direct cast for
|
||||
// better portability.
|
||||
func StatusToUint64(s Status) uint64 {
|
||||
return uint64(s)
|
||||
}
|
|
@ -1,17 +0,0 @@
|
|||
package node
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestStatus_Full(t *testing.T) {
|
||||
st := new(Status)
|
||||
|
||||
st.SetFull()
|
||||
require.True(t, st.Full())
|
||||
|
||||
st.ResetFull()
|
||||
require.False(t, st.Full())
|
||||
}
|
0
pkg/core/object/.gitkeep
Normal file
0
pkg/core/object/.gitkeep
Normal file
|
@ -1,94 +0,0 @@
|
|||
package object
|
||||
|
||||
// ExtendedHeaderType represents the enumeration
|
||||
// of extended header types of the NeoFS object.
|
||||
type ExtendedHeaderType uint32
|
||||
|
||||
// ExtendedHeader represents the extended
|
||||
// header of NeoFS object.
|
||||
type ExtendedHeader struct {
|
||||
typ ExtendedHeaderType
|
||||
|
||||
val interface{}
|
||||
}
|
||||
|
||||
// Type returns the extended header type.
|
||||
func (h ExtendedHeader) Type() ExtendedHeaderType {
|
||||
return h.typ
|
||||
}
|
||||
|
||||
// SetType sets the extended header type.
|
||||
func (h *ExtendedHeader) SetType(v ExtendedHeaderType) {
|
||||
h.typ = v
|
||||
}
|
||||
|
||||
// Value returns the extended header value.
|
||||
//
|
||||
// In the case of a reference type, the value is
|
||||
// returned by reference, so value mutations affect
|
||||
// header state. Therefore, callers must first copy
|
||||
// the value before changing manipulations.
|
||||
func (h ExtendedHeader) Value() interface{} {
|
||||
return h.val
|
||||
}
|
||||
|
||||
// SetValue sets the extended header value.
|
||||
//
|
||||
// Caller must take into account that each type of
|
||||
// header usually has a limited set of expected
|
||||
// value types.
|
||||
//
|
||||
// In the case of a reference type, the value is set
|
||||
// by reference, so source value mutations affect
|
||||
// header state. Therefore, callers must first copy
|
||||
// the source value before changing manipulations.
|
||||
func (h *ExtendedHeader) SetValue(v interface{}) {
|
||||
h.val = v
|
||||
}
|
||||
|
||||
// TypeFromUint32 converts builtin
|
||||
// uint32 value to Epoch.
|
||||
//
|
||||
// Try to avoid direct cast for
|
||||
// better portability.
|
||||
func TypeFromUint32(v uint32) ExtendedHeaderType {
|
||||
return ExtendedHeaderType(v)
|
||||
}
|
||||
|
||||
// TypeToUint32 converts Epoch value
|
||||
// to builtin uint32.
|
||||
//
|
||||
// Try to avoid direct cast for
|
||||
// better portability.
|
||||
func TypeToUint32(v ExtendedHeaderType) uint32 {
|
||||
return uint32(v)
|
||||
}
|
||||
|
||||
// TypesEQ reports whether t1 and t2 are the same ExtendedHeaderType.
|
||||
//
|
||||
// Function defines the relation of equality
|
||||
// between two ExtendedHeaderType. Try to avoid comparison through
|
||||
// "==" operator for better portability.
|
||||
func TypesEQ(t1, t2 ExtendedHeaderType) bool {
|
||||
return TypeToUint32(t1) == TypeToUint32(t2)
|
||||
}
|
||||
|
||||
// TypesLT reports whether t1 ExtendedHeaderType
|
||||
// is less than t2.
|
||||
//
|
||||
// Function defines the "less than" relation
|
||||
// between two ExtendedHeaderType. Try to avoid
|
||||
// comparison through "<" operator for better portability.
|
||||
func TypesLT(t1, t2 ExtendedHeaderType) bool {
|
||||
return TypeToUint32(t1) < TypeToUint32(t2)
|
||||
}
|
||||
|
||||
// TypesGT reports whether t1 ExtendedHeaderType
|
||||
// is greater than t2.
|
||||
//
|
||||
// Function defines the "greater than" relation
|
||||
// between two ExtendedHeaderType. Try to avoid
|
||||
// comparison through ">" operator for better portability.
|
||||
func TypesGT(t1, t2 ExtendedHeaderType) bool {
|
||||
return TypeToUint32(t1) > TypeToUint32(t2)
|
||||
}
|
|
@ -1,25 +0,0 @@
|
|||
package object
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestExtendedHeader_Type(t *testing.T) {
|
||||
h := new(ExtendedHeader)
|
||||
|
||||
ht := TypeFromUint32(3)
|
||||
h.SetType(ht)
|
||||
|
||||
require.True(t, TypesEQ(ht, h.Type()))
|
||||
}
|
||||
|
||||
func TestExtendedHeader_Value(t *testing.T) {
|
||||
h := new(ExtendedHeader)
|
||||
|
||||
val := 100
|
||||
h.SetValue(val)
|
||||
|
||||
require.Equal(t, val, h.Value())
|
||||
}
|
|
@ -1,73 +0,0 @@
|
|||
package object
|
||||
|
||||
import (
|
||||
"errors"
|
||||
)
|
||||
|
||||
// Header represents NeoFS object header.
|
||||
type Header struct {
|
||||
// SystemHeader is an obligatory part of any object header.
|
||||
// It is used to set the identity and basic parameters of
|
||||
// the object.
|
||||
//
|
||||
// Header must inherit all the methods of SystemHeader,
|
||||
// so the SystemHeader is embedded in Header.
|
||||
SystemHeader
|
||||
|
||||
extendedHeaders []ExtendedHeader // extended headers
|
||||
}
|
||||
|
||||
// ErrNilHeader is returned by functions that expect
|
||||
// a non-nil Header pointer, but received nil.
|
||||
var ErrNilHeader = errors.New("object header is nil")
|
||||
|
||||
// ExtendedHeaders returns the extended headers of header.
|
||||
//
|
||||
// Changing the result is unsafe and affects the header.
|
||||
// In order to prevent state mutations, use CopyExtendedHeaders.
|
||||
func (h *Header) ExtendedHeaders() []ExtendedHeader {
|
||||
return h.extendedHeaders
|
||||
}
|
||||
|
||||
// CopyExtendedHeaders returns the copy of extended headers.
|
||||
//
|
||||
// Changing the result is safe and does not affect the header.
|
||||
//
|
||||
// Returns nil if header is nil.
|
||||
func CopyExtendedHeaders(h *Header) []ExtendedHeader {
|
||||
if h == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
res := make([]ExtendedHeader, len(h.extendedHeaders))
|
||||
|
||||
copy(res, h.extendedHeaders)
|
||||
|
||||
return res
|
||||
}
|
||||
|
||||
// SetExtendedHeaders sets the extended headers of the header.
|
||||
//
|
||||
// Subsequent changing the source slice is unsafe and affects
|
||||
// the header. In order to prevent state mutations, use
|
||||
// SetExtendedHeadersCopy.
|
||||
func (h *Header) SetExtendedHeaders(v []ExtendedHeader) {
|
||||
h.extendedHeaders = v
|
||||
}
|
||||
|
||||
// SetExtendedHeadersCopy copies extended headers and sets the copy
|
||||
// as the object extended headers.
|
||||
//
|
||||
// Subsequent changing the source slice is safe and does not affect
|
||||
// the header.
|
||||
//
|
||||
// SetExtendedHeadersCopy does nothing if Header is nil.
|
||||
func SetExtendedHeadersCopy(h *Header, hs []ExtendedHeader) {
|
||||
if h == nil {
|
||||
return
|
||||
}
|
||||
|
||||
h.extendedHeaders = make([]ExtendedHeader, len(hs))
|
||||
|
||||
copy(h.extendedHeaders, hs)
|
||||
}
|
|
@ -1,98 +0,0 @@
|
|||
package object
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func testHeaders(num uint32) []ExtendedHeader {
|
||||
res := make([]ExtendedHeader, num)
|
||||
|
||||
for i := uint32(0); i < num; i++ {
|
||||
res[i].SetType(TypeFromUint32(i))
|
||||
res[i].SetValue(i)
|
||||
}
|
||||
|
||||
return res
|
||||
}
|
||||
|
||||
func TestObject_ExtendedHeaders(t *testing.T) {
|
||||
h := new(Header)
|
||||
|
||||
hs := testHeaders(2)
|
||||
|
||||
h.SetExtendedHeaders(hs)
|
||||
|
||||
require.Equal(t, hs, h.ExtendedHeaders())
|
||||
}
|
||||
|
||||
func TestCopyExtendedHeaders(t *testing.T) {
|
||||
require.Nil(t, CopyExtendedHeaders(nil))
|
||||
|
||||
h := new(Header)
|
||||
|
||||
// set initial headers
|
||||
initHs := testHeaders(2)
|
||||
h.SetExtendedHeaders(initHs)
|
||||
|
||||
// get extended headers copy
|
||||
hsCopy := CopyExtendedHeaders(h)
|
||||
|
||||
// change the copy
|
||||
hsCopy[0] = hsCopy[1]
|
||||
|
||||
// check that extended headers have not changed
|
||||
require.Equal(t, initHs, h.ExtendedHeaders())
|
||||
}
|
||||
|
||||
func TestSetExtendedHeadersCopy(t *testing.T) {
|
||||
require.NotPanics(t, func() {
|
||||
SetExtendedHeadersCopy(nil, nil)
|
||||
})
|
||||
|
||||
h := new(Header)
|
||||
|
||||
// create source headers
|
||||
srcHs := testHeaders(2)
|
||||
|
||||
// copy and set headers
|
||||
SetExtendedHeadersCopy(h, srcHs)
|
||||
|
||||
// get extended headers
|
||||
objHs := h.ExtendedHeaders()
|
||||
|
||||
// change the source headers
|
||||
srcHs[0] = srcHs[1]
|
||||
|
||||
// check that headeres have not changed
|
||||
require.Equal(t, objHs, h.ExtendedHeaders())
|
||||
}
|
||||
|
||||
func TestHeaderRelations(t *testing.T) {
|
||||
items := []struct {
|
||||
relFn func(ExtendedHeaderType, ExtendedHeaderType) bool
|
||||
|
||||
base, ok, fail uint32
|
||||
}{
|
||||
{relFn: TypesEQ, base: 1, ok: 1, fail: 2},
|
||||
{relFn: TypesLT, base: 1, ok: 2, fail: 0},
|
||||
{relFn: TypesGT, base: 1, ok: 0, fail: 2},
|
||||
}
|
||||
|
||||
for _, item := range items {
|
||||
require.True(t,
|
||||
item.relFn(
|
||||
TypeFromUint32(item.base),
|
||||
TypeFromUint32(item.ok),
|
||||
),
|
||||
)
|
||||
|
||||
require.False(t,
|
||||
item.relFn(
|
||||
TypeFromUint32(item.base),
|
||||
TypeFromUint32(item.fail),
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
|
@ -1,63 +0,0 @@
|
|||
package headers
|
||||
|
||||
import (
|
||||
"github.com/nspcc-dev/neofs-node/pkg/core/object"
|
||||
)
|
||||
|
||||
// Header represents object extended header.
|
||||
//
|
||||
// It is a type alias of
|
||||
// github.com/nspcc-dev/neofs-node/pkg/core/object.ExtendedHeader.
|
||||
type Header = object.ExtendedHeader
|
||||
|
||||
// Type represents extended header type.
|
||||
//
|
||||
// It is a type alias of
|
||||
// github.com/nspcc-dev/neofs-node/pkg/core/object.ExtendedHeaderType.
|
||||
type Type = object.ExtendedHeaderType
|
||||
|
||||
const (
|
||||
// this is the only place where this cast is appropriate,
|
||||
// use object.TypeFromUint32 instead.
|
||||
lowerUndefined = Type(iota) // lower unsupported Type value
|
||||
|
||||
// TypeLink is the type of object reference header.
|
||||
TypeLink
|
||||
|
||||
// TypeUser is the of user key-value string header.
|
||||
TypeUser
|
||||
|
||||
// TypeTransform is the type of transformation mark header.
|
||||
TypeTransform
|
||||
|
||||
// TypeTombstone is the type of tombstone mark header.
|
||||
TypeTombstone
|
||||
|
||||
// TypeSessionToken is the type of session token header.
|
||||
TypeSessionToken
|
||||
|
||||
// TypeHomomorphicHash is the type of homomorphic hash header.
|
||||
TypeHomomorphicHash
|
||||
|
||||
// TypePayloadChecksum is the type of payload checksum header.
|
||||
TypePayloadChecksum
|
||||
|
||||
// TypeIntegrity is the type of integrity header.
|
||||
TypeIntegrity
|
||||
|
||||
// TypeStorageGroup is the type of storage group header.
|
||||
TypeStorageGroup
|
||||
|
||||
// TypePublicKey is the type of public key header.
|
||||
TypePublicKey
|
||||
|
||||
upperUndefined // upper unsupported Type value
|
||||
)
|
||||
|
||||
// SupportedType returns true if Type is
|
||||
// the known type of extended header. Each
|
||||
// supported type has named constant.
|
||||
func SupportedType(t Type) bool {
|
||||
return object.TypesGT(t, lowerUndefined) &&
|
||||
object.TypesLT(t, upperUndefined)
|
||||
}
|
|
@ -1,34 +0,0 @@
|
|||
package headers
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/nspcc-dev/neofs-node/pkg/core/object"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestSupportedType(t *testing.T) {
|
||||
for _, typ := range []Type{
|
||||
TypeLink,
|
||||
TypeUser,
|
||||
TypeTransform,
|
||||
TypeTombstone,
|
||||
TypeSessionToken,
|
||||
TypeHomomorphicHash,
|
||||
TypePayloadChecksum,
|
||||
TypeIntegrity,
|
||||
TypeStorageGroup,
|
||||
TypePublicKey,
|
||||
} {
|
||||
require.True(t, SupportedType(typ))
|
||||
}
|
||||
|
||||
for _, typ := range []Type{
|
||||
lowerUndefined,
|
||||
upperUndefined,
|
||||
object.TypeFromUint32(object.TypeToUint32(lowerUndefined) - 1),
|
||||
object.TypeFromUint32(object.TypeToUint32(upperUndefined) + 1),
|
||||
} {
|
||||
require.False(t, SupportedType(typ))
|
||||
}
|
||||
}
|
|
@ -1,45 +0,0 @@
|
|||
package headers
|
||||
|
||||
// UserHeader is a value of object extended header
|
||||
// that carries user string key-value pairs.
|
||||
//
|
||||
// All user headers must be type of TypeUser.
|
||||
// All user header must have UserHeader pointer value.
|
||||
type UserHeader struct {
|
||||
key, val string
|
||||
}
|
||||
|
||||
// NewUserHeader creates, initialized and returns
|
||||
// the user extended header.
|
||||
func NewUserHeader(key, val string) *Header {
|
||||
res := new(Header)
|
||||
|
||||
res.SetType(TypeUser)
|
||||
|
||||
res.SetValue(&UserHeader{
|
||||
key: key,
|
||||
val: val,
|
||||
})
|
||||
|
||||
return res
|
||||
}
|
||||
|
||||
// Key returns the user header key.
|
||||
func (u UserHeader) Key() string {
|
||||
return u.key
|
||||
}
|
||||
|
||||
// SetKey sets the user header key.
|
||||
func (u *UserHeader) SetKey(key string) {
|
||||
u.key = key
|
||||
}
|
||||
|
||||
// Value returns the user header value.
|
||||
func (u UserHeader) Value() string {
|
||||
return u.val
|
||||
}
|
||||
|
||||
// SetValue sets the user header value.
|
||||
func (u *UserHeader) SetValue(val string) {
|
||||
u.val = val
|
||||
}
|
|
@ -1,45 +0,0 @@
|
|||
package headers
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/nspcc-dev/neofs-node/pkg/core/object"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestUserHeader_Key(t *testing.T) {
|
||||
h := new(UserHeader)
|
||||
|
||||
key := "random key"
|
||||
h.SetKey(key)
|
||||
|
||||
require.Equal(t, key, h.Key())
|
||||
}
|
||||
|
||||
func TestUserHeader_Value(t *testing.T) {
|
||||
h := new(UserHeader)
|
||||
|
||||
val := "random value"
|
||||
h.SetValue(val)
|
||||
|
||||
require.Equal(t, val, h.Value())
|
||||
}
|
||||
|
||||
func TestNewUserHeader(t *testing.T) {
|
||||
key := "user key"
|
||||
val := "user val"
|
||||
|
||||
h := NewUserHeader(key, val)
|
||||
|
||||
require.True(t,
|
||||
object.TypesEQ(
|
||||
TypeUser,
|
||||
h.Type(),
|
||||
),
|
||||
)
|
||||
|
||||
uh := h.Value().(*UserHeader)
|
||||
|
||||
require.Equal(t, key, uh.Key())
|
||||
require.Equal(t, val, uh.Value())
|
||||
}
|
|
@ -1,61 +0,0 @@
|
|||
package object
|
||||
|
||||
import (
|
||||
"github.com/nspcc-dev/neofs-api-go/refs"
|
||||
)
|
||||
|
||||
// ID represents the object identifier.
|
||||
//
|
||||
// It is a type alias of
|
||||
// github.com/nspcc-dev/neofs-api-go/refs.ObjectID.
|
||||
// FIXME: object ID should be defined in core package.
|
||||
type ID = refs.ObjectID
|
||||
|
||||
// Address represents NeoFS Object address.
|
||||
// Acts as a reference to the object.
|
||||
type Address struct {
|
||||
cid CID
|
||||
|
||||
id ID
|
||||
}
|
||||
|
||||
// CID return the identifier of the container
|
||||
// that the object belongs to.
|
||||
func (a Address) CID() CID {
|
||||
return a.cid
|
||||
}
|
||||
|
||||
// SetCID sets the identifier of the container
|
||||
// that the object belongs to.
|
||||
func (a *Address) SetCID(v CID) {
|
||||
a.cid = v
|
||||
}
|
||||
|
||||
// ID returns the unique identifier of the
|
||||
// object in container.
|
||||
func (a Address) ID() ID {
|
||||
return a.id
|
||||
}
|
||||
|
||||
// SetID sets the unique identifier of the
|
||||
// object in container.
|
||||
func (a *Address) SetID(v ID) {
|
||||
a.id = v
|
||||
}
|
||||
|
||||
// AddressFromObject returns an address based
|
||||
// on the object's header.
|
||||
//
|
||||
// Returns nil on nil object.
|
||||
func AddressFromObject(o *Object) *Address {
|
||||
if o == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
a := new(Address)
|
||||
|
||||
a.SetCID(o.CID())
|
||||
a.SetID(o.ID())
|
||||
|
||||
return a
|
||||
}
|
|
@ -1,42 +0,0 @@
|
|||
package object
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestAddress_CID(t *testing.T) {
|
||||
a := new(Address)
|
||||
|
||||
cid := CID{1, 2, 3}
|
||||
a.SetCID(cid)
|
||||
|
||||
require.Equal(t, cid, a.CID())
|
||||
}
|
||||
|
||||
func TestAddress_ID(t *testing.T) {
|
||||
a := new(Address)
|
||||
|
||||
id := ID{1, 2, 3}
|
||||
a.SetID(id)
|
||||
|
||||
require.Equal(t, id, a.ID())
|
||||
}
|
||||
|
||||
func TestAddressFromObject(t *testing.T) {
|
||||
require.Nil(t, AddressFromObject(nil))
|
||||
|
||||
o := new(Object)
|
||||
|
||||
cid := CID{4, 5, 6}
|
||||
o.SetCID(cid)
|
||||
|
||||
id := ID{1, 2, 3}
|
||||
o.SetID(id)
|
||||
|
||||
a := AddressFromObject(o)
|
||||
|
||||
require.Equal(t, cid, a.CID())
|
||||
require.Equal(t, id, a.ID())
|
||||
}
|
|
@ -1,76 +0,0 @@
|
|||
package object
|
||||
|
||||
import (
|
||||
"errors"
|
||||
)
|
||||
|
||||
// Object represents NeoFS Object.
|
||||
type Object struct {
|
||||
// Header is an obligatory part of any object.
|
||||
// It is used to carry any additional information
|
||||
// besides payload.
|
||||
//
|
||||
// Object must inherit all the methods of Header,
|
||||
// so the Header is embedded in Object.
|
||||
Header
|
||||
|
||||
payload []byte // payload bytes
|
||||
}
|
||||
|
||||
// ErrNilObject is returned by functions that expect
|
||||
// a non-nil Object pointer, but received nil.
|
||||
var ErrNilObject = errors.New("object is nil")
|
||||
|
||||
// Payload returns payload bytes of the object.
|
||||
//
|
||||
// Changing the result is unsafe and affects
|
||||
// the object. In order to prevent state
|
||||
// mutations, use CopyPayload.
|
||||
func (o *Object) Payload() []byte {
|
||||
return o.payload
|
||||
}
|
||||
|
||||
// CopyPayload returns the copy of
|
||||
// object payload.
|
||||
//
|
||||
// Changing the result is safe and
|
||||
// does not affect the object.
|
||||
//
|
||||
// CopyPayload returns nil if object is nil.
|
||||
func CopyPayload(o *Object) []byte {
|
||||
if o == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
res := make([]byte, len(o.payload))
|
||||
copy(res, o.payload)
|
||||
|
||||
return res
|
||||
}
|
||||
|
||||
// SetPayload sets objecyt payload bytes.
|
||||
//
|
||||
// Subsequent changing the source slice
|
||||
// is unsafe and affects the object.
|
||||
// In order to prevent state mutations,
|
||||
// use SetPayloadCopy.
|
||||
func (o *Object) SetPayload(v []byte) {
|
||||
o.payload = v
|
||||
}
|
||||
|
||||
// SetPayloadCopy copies slice bytes and sets
|
||||
// the copy as object payload.
|
||||
//
|
||||
// Subsequent changing the source slice
|
||||
// is safe and does not affect the object.
|
||||
//
|
||||
// SetPayloadCopy does nothing if object is nil.
|
||||
func SetPayloadCopy(o *Object, payload []byte) {
|
||||
if o == nil {
|
||||
return
|
||||
}
|
||||
|
||||
o.payload = make([]byte, len(payload))
|
||||
|
||||
copy(o.payload, payload)
|
||||
}
|
|
@ -1,58 +0,0 @@
|
|||
package object
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestObject_Payload(t *testing.T) {
|
||||
o := new(Object)
|
||||
|
||||
payload := []byte{1, 2, 3}
|
||||
o.SetPayload(payload)
|
||||
|
||||
require.Equal(t, payload, o.Payload())
|
||||
}
|
||||
|
||||
func TestCopyPayload(t *testing.T) {
|
||||
require.Nil(t, CopyPayload(nil))
|
||||
|
||||
o := new(Object)
|
||||
|
||||
// set initial node key
|
||||
initPayload := []byte{1, 2, 3}
|
||||
o.SetPayload(initPayload)
|
||||
|
||||
// get payload copy
|
||||
pCopy := CopyPayload(o)
|
||||
|
||||
// change the copy
|
||||
pCopy[0]++
|
||||
|
||||
// check that payload has not changed
|
||||
require.Equal(t, initPayload, o.Payload())
|
||||
}
|
||||
|
||||
func TestSetPayloadCopy(t *testing.T) {
|
||||
require.NotPanics(t, func() {
|
||||
SetExtendedHeadersCopy(nil, nil)
|
||||
})
|
||||
|
||||
o := new(Object)
|
||||
|
||||
// create source payload
|
||||
srcPayload := []byte{1, 2, 3}
|
||||
|
||||
// copy and set payload
|
||||
SetPayloadCopy(o, srcPayload)
|
||||
|
||||
// get payload
|
||||
objPayload := o.Payload()
|
||||
|
||||
// change the source payload
|
||||
srcPayload[0]++
|
||||
|
||||
// check that payload has not changed
|
||||
require.Equal(t, objPayload, o.Payload())
|
||||
}
|
|
@ -1,61 +0,0 @@
|
|||
package storage
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/nspcc-dev/neofs-node/pkg/core/object"
|
||||
)
|
||||
|
||||
// Object represents the NeoFS Object.
|
||||
//
|
||||
// It is a type alias of
|
||||
// github.com/nspcc-dev/neofs-node/pkg/core/object.Object.
|
||||
type Object = object.Object
|
||||
|
||||
// Address represents the address of
|
||||
// NeoFS Object.
|
||||
//
|
||||
// It is a type alias of
|
||||
// github.com/nspcc-dev/neofs-node/pkg/core/object.Address.
|
||||
type Address = object.Address
|
||||
|
||||
// Storage is an interface that wraps
|
||||
// basic object storage methods.
|
||||
type Storage interface {
|
||||
// Put saves pointed object to the underlying storage.
|
||||
// It returns object address for reference and any error
|
||||
// encountered that caused the saving to interrupt.
|
||||
//
|
||||
// Put must return object.ErrNilObject on nil-pointer.
|
||||
//
|
||||
// Implementations must not modify the object through the pointer (even temporarily).
|
||||
// Implementations must not retain the object pointer.
|
||||
//
|
||||
// Object rewriting behavior is dictated by implementation.
|
||||
Put(*Object) (*Address, error)
|
||||
|
||||
// Get reads the object from the storage by address.
|
||||
// It returns the pointer to requested object and any error encountered.
|
||||
//
|
||||
// Get must return exactly one non-nil value.
|
||||
// Get must return ErrNotFound if the object is not in storage.
|
||||
//
|
||||
// Implementations must not retain the object pointer and modify
|
||||
// the object through it.
|
||||
Get(Address) (*Object, error)
|
||||
|
||||
// Delete removes the object from the storage.
|
||||
// It returns any error encountered that caused the deletion to interrupt.
|
||||
//
|
||||
// Delete must return nil if object was successfully deleted.
|
||||
//
|
||||
// Behavior when deleting a nonexistent object is dictated by implementation.
|
||||
Delete(Address) error
|
||||
}
|
||||
|
||||
// ErrNotFound is the error returned when object was not found in storage.
|
||||
var ErrNotFound = errors.New("object not found")
|
||||
|
||||
// ErrNilStorage is the error returned by functions that
|
||||
// expect a non-nil object storage implementation, but received nil.
|
||||
var ErrNilStorage = errors.New("object storage is nil")
|
|
@ -1,88 +0,0 @@
|
|||
package test
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"testing"
|
||||
|
||||
"github.com/nspcc-dev/neofs-node/pkg/core/object"
|
||||
"github.com/nspcc-dev/neofs-node/pkg/core/object/storage"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
type testStorage struct {
|
||||
*sync.RWMutex
|
||||
|
||||
items map[storage.Address]*storage.Object
|
||||
}
|
||||
|
||||
func (s *testStorage) Put(o *storage.Object) (*storage.Address, error) {
|
||||
if o == nil {
|
||||
return nil, object.ErrNilObject
|
||||
}
|
||||
|
||||
a := object.AddressFromObject(o)
|
||||
|
||||
s.Lock()
|
||||
s.items[*a] = o
|
||||
s.Unlock()
|
||||
|
||||
return a, nil
|
||||
}
|
||||
|
||||
func (s *testStorage) Get(a storage.Address) (*storage.Object, error) {
|
||||
s.RLock()
|
||||
o, ok := s.items[a]
|
||||
s.RUnlock()
|
||||
|
||||
if !ok {
|
||||
return nil, storage.ErrNotFound
|
||||
}
|
||||
|
||||
return o, nil
|
||||
}
|
||||
|
||||
func (s *testStorage) Delete(a storage.Address) error {
|
||||
s.Lock()
|
||||
delete(s.items, a)
|
||||
s.Unlock()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// New creates new container storage
|
||||
// that stores containers in go-builtin map.
|
||||
func New() storage.Storage {
|
||||
return &testStorage{
|
||||
RWMutex: new(sync.RWMutex),
|
||||
items: make(map[storage.Address]*storage.Object),
|
||||
}
|
||||
}
|
||||
|
||||
// Storage conducts testing of object
|
||||
// storage for interface specification.
|
||||
//
|
||||
// Storage must be empty.
|
||||
func Storage(t *testing.T, s storage.Storage) {
|
||||
_, err := s.Put(nil)
|
||||
require.True(t, errors.Is(err, object.ErrNilObject))
|
||||
|
||||
a := new(object.Address)
|
||||
_, err = s.Get(*a)
|
||||
require.True(t, errors.Is(err, storage.ErrNotFound))
|
||||
|
||||
o := new(object.Object)
|
||||
o.SetID(object.ID{1, 2, 3})
|
||||
|
||||
a, err = s.Put(o)
|
||||
require.NoError(t, err)
|
||||
|
||||
o2, err := s.Get(*a)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.Equal(t, o, o2)
|
||||
|
||||
require.NoError(t, s.Delete(*a))
|
||||
_, err = s.Get(*a)
|
||||
require.True(t, errors.Is(err, storage.ErrNotFound))
|
||||
}
|
|
@ -1,11 +0,0 @@
|
|||
package test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestNewStorage(t *testing.T) {
|
||||
s := New()
|
||||
|
||||
Storage(t, s)
|
||||
}
|
|
@ -1,107 +0,0 @@
|
|||
package object
|
||||
|
||||
import (
|
||||
"github.com/nspcc-dev/neofs-node/pkg/core/container"
|
||||
"github.com/nspcc-dev/neofs-node/pkg/core/netmap/epoch"
|
||||
)
|
||||
|
||||
// CID represents the container identifier.
|
||||
//
|
||||
// It is a type alias of
|
||||
// github.com/nspcc-dev/neofs-node/pkg/core/container.ID.
|
||||
type CID = container.ID
|
||||
|
||||
// OwnerID represents the container
|
||||
// owner identifier.
|
||||
//
|
||||
// It is a type alias of
|
||||
// github.com/nspcc-dev/neofs-node/pkg/core/container.OwnerID.
|
||||
type OwnerID = container.OwnerID
|
||||
|
||||
// Epoch represents the NeoFS epoch number.
|
||||
//
|
||||
// It is a type alias of
|
||||
// github.com/nspcc-dev/neofs-node/pkg/core/netmap/epoch.Epoch.
|
||||
type Epoch = epoch.Epoch
|
||||
|
||||
// SystemHeader represents the
|
||||
// system header of NeoFS Object.
|
||||
type SystemHeader struct {
|
||||
version uint64 // object version
|
||||
|
||||
payloadLen uint64 // length of the payload bytes
|
||||
|
||||
id ID // object ID
|
||||
|
||||
cid CID // container ID
|
||||
|
||||
ownerID OwnerID // object owner ID
|
||||
|
||||
creatEpoch Epoch // creation epoch number
|
||||
}
|
||||
|
||||
// Version returns the object version number.
|
||||
func (s *SystemHeader) Version() uint64 {
|
||||
return s.version
|
||||
}
|
||||
|
||||
// SetVersion sets the object version number.
|
||||
func (s *SystemHeader) SetVersion(v uint64) {
|
||||
s.version = v
|
||||
}
|
||||
|
||||
// PayloadLength returns the length of the
|
||||
// object payload bytes.
|
||||
func (s *SystemHeader) PayloadLength() uint64 {
|
||||
return s.payloadLen
|
||||
}
|
||||
|
||||
// SetPayloadLength sets the length of the object
|
||||
// payload bytes.
|
||||
func (s *SystemHeader) SetPayloadLength(v uint64) {
|
||||
s.payloadLen = v
|
||||
}
|
||||
|
||||
// ID returns the object identifier.
|
||||
func (s *SystemHeader) ID() ID {
|
||||
return s.id
|
||||
}
|
||||
|
||||
// SetID sets the object identifier.
|
||||
func (s *SystemHeader) SetID(v ID) {
|
||||
s.id = v
|
||||
}
|
||||
|
||||
// CID returns the container identifier
|
||||
// to which the object belongs.
|
||||
func (s *SystemHeader) CID() CID {
|
||||
return s.cid
|
||||
}
|
||||
|
||||
// SetCID sets the container identifier
|
||||
// to which the object belongs.
|
||||
func (s *SystemHeader) SetCID(v CID) {
|
||||
s.cid = v
|
||||
}
|
||||
|
||||
// OwnerID returns the object owner identifier.
|
||||
func (s *SystemHeader) OwnerID() OwnerID {
|
||||
return s.ownerID
|
||||
}
|
||||
|
||||
// SetOwnerID sets the object owner identifier.
|
||||
func (s *SystemHeader) SetOwnerID(v OwnerID) {
|
||||
s.ownerID = v
|
||||
}
|
||||
|
||||
// CreationEpoch returns the epoch number
|
||||
// in which the object was created.
|
||||
func (s *SystemHeader) CreationEpoch() Epoch {
|
||||
return s.creatEpoch
|
||||
}
|
||||
|
||||
// SetCreationEpoch sets the epoch number
|
||||
// in which the object was created.
|
||||
func (s *SystemHeader) SetCreationEpoch(v Epoch) {
|
||||
s.creatEpoch = v
|
||||
}
|
|
@ -1,62 +0,0 @@
|
|||
package object
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/nspcc-dev/neofs-node/pkg/core/netmap/epoch"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestSystemHeader_Version(t *testing.T) {
|
||||
h := new(SystemHeader)
|
||||
|
||||
v := uint64(7)
|
||||
h.SetVersion(v)
|
||||
|
||||
require.Equal(t, v, h.Version())
|
||||
}
|
||||
|
||||
func TestSystemHeader_PayloadLength(t *testing.T) {
|
||||
h := new(SystemHeader)
|
||||
|
||||
ln := uint64(3)
|
||||
h.SetPayloadLength(ln)
|
||||
|
||||
require.Equal(t, ln, h.PayloadLength())
|
||||
}
|
||||
|
||||
func TestSystemHeader_ID(t *testing.T) {
|
||||
h := new(SystemHeader)
|
||||
|
||||
id := ID{1, 2, 3}
|
||||
h.SetID(id)
|
||||
|
||||
require.Equal(t, id, h.ID())
|
||||
}
|
||||
|
||||
func TestSystemHeader_CID(t *testing.T) {
|
||||
h := new(SystemHeader)
|
||||
|
||||
cid := CID{1, 2, 3}
|
||||
h.SetCID(cid)
|
||||
|
||||
require.Equal(t, cid, h.CID())
|
||||
}
|
||||
|
||||
func TestSystemHeader_OwnerID(t *testing.T) {
|
||||
h := new(SystemHeader)
|
||||
|
||||
ownerID := OwnerID{1, 2, 3}
|
||||
h.SetOwnerID(ownerID)
|
||||
|
||||
require.Equal(t, ownerID, h.OwnerID())
|
||||
}
|
||||
|
||||
func TestSystemHeader_CreationEpoch(t *testing.T) {
|
||||
h := new(SystemHeader)
|
||||
|
||||
ep := epoch.FromUint64(1)
|
||||
h.SetCreationEpoch(ep)
|
||||
|
||||
require.True(t, epoch.EQ(ep, h.CreationEpoch()))
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue