forked from TrueCloudLab/frostfs-node
[#1490] node: Require SG members to be unique
Signed-off-by: Pavel Karpy <carpawell@nspcc.ru>
This commit is contained in:
parent
0504c3e0c6
commit
5d5d21e3d0
2 changed files with 45 additions and 10 deletions
|
@ -61,6 +61,8 @@ var errNoExpirationEpoch = errors.New("missing expiration epoch attribute")
|
||||||
|
|
||||||
var errTombstoneExpiration = errors.New("tombstone body and header contain different expiration values")
|
var errTombstoneExpiration = errors.New("tombstone body and header contain different expiration values")
|
||||||
|
|
||||||
|
var errEmptySGMembers = errors.New("storage group with empty members list")
|
||||||
|
|
||||||
func defaultCfg() *cfg {
|
func defaultCfg() *cfg {
|
||||||
return new(cfg)
|
return new(cfg)
|
||||||
}
|
}
|
||||||
|
@ -227,6 +229,22 @@ func (v *FormatValidator) ValidateContent(o *object.Object) error {
|
||||||
if err := sg.Unmarshal(o.Payload()); err != nil {
|
if err := sg.Unmarshal(o.Payload()); err != nil {
|
||||||
return fmt.Errorf("(%T) could not unmarshal SG content: %w", v, err)
|
return fmt.Errorf("(%T) could not unmarshal SG content: %w", v, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mm := sg.Members()
|
||||||
|
lenMM := len(mm)
|
||||||
|
if lenMM == 0 {
|
||||||
|
return errEmptySGMembers
|
||||||
|
}
|
||||||
|
|
||||||
|
uniqueFilter := make(map[oid.ID]struct{}, lenMM)
|
||||||
|
|
||||||
|
for i := 0; i < lenMM; i++ {
|
||||||
|
if _, alreadySeen := uniqueFilter[mm[i]]; alreadySeen {
|
||||||
|
return fmt.Errorf("storage group contains non-unique member: %s", mm[i])
|
||||||
|
}
|
||||||
|
|
||||||
|
uniqueFilter[mm[i]] = struct{}{}
|
||||||
|
}
|
||||||
case object.TypeLock:
|
case object.TypeLock:
|
||||||
if len(o.Payload()) == 0 {
|
if len(o.Payload()) == 0 {
|
||||||
return errors.New("empty payload in lock")
|
return errors.New("empty payload in lock")
|
||||||
|
|
|
@ -156,26 +156,43 @@ func TestFormatValidator_Validate(t *testing.T) {
|
||||||
obj := object.New()
|
obj := object.New()
|
||||||
obj.SetType(object.TypeStorageGroup)
|
obj.SetType(object.TypeStorageGroup)
|
||||||
|
|
||||||
|
t.Run("empty payload", func(t *testing.T) {
|
||||||
require.Error(t, v.ValidateContent(obj))
|
require.Error(t, v.ValidateContent(obj))
|
||||||
|
})
|
||||||
|
|
||||||
var content storagegroup.StorageGroup
|
var content storagegroup.StorageGroup
|
||||||
|
content.SetExpirationEpoch(1) // some non-default value
|
||||||
|
|
||||||
|
t.Run("empty members", func(t *testing.T) {
|
||||||
|
data, err := content.Marshal()
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
obj.SetPayload(data)
|
||||||
|
require.ErrorIs(t, v.ValidateContent(obj), errEmptySGMembers)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("non-unique members", func(t *testing.T) {
|
||||||
|
id := oidtest.ID()
|
||||||
|
|
||||||
|
content.SetMembers([]oid.ID{id, id})
|
||||||
|
|
||||||
data, err := content.Marshal()
|
data, err := content.Marshal()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
obj.SetPayload(data)
|
obj.SetPayload(data)
|
||||||
|
|
||||||
require.Error(t, v.ValidateContent(obj))
|
require.Error(t, v.ValidateContent(obj))
|
||||||
|
})
|
||||||
|
|
||||||
content.SetMembers([]oid.ID{oidtest.ID()})
|
t.Run("correct SG", func(t *testing.T) {
|
||||||
|
content.SetMembers([]oid.ID{oidtest.ID(), oidtest.ID()})
|
||||||
|
|
||||||
data, err = content.Marshal()
|
data, err := content.Marshal()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
obj.SetPayload(data)
|
obj.SetPayload(data)
|
||||||
|
|
||||||
require.NoError(t, v.ValidateContent(obj))
|
require.NoError(t, v.ValidateContent(obj))
|
||||||
})
|
})
|
||||||
|
})
|
||||||
|
|
||||||
t.Run("expiration", func(t *testing.T) {
|
t.Run("expiration", func(t *testing.T) {
|
||||||
fn := func(val string) *object.Object {
|
fn := func(val string) *object.Object {
|
||||||
|
|
Loading…
Reference in a new issue