[#509] Add tests for custom credentials
Signed-off-by: Denis Kirillov <d.kirillov@yadro.com>
This commit is contained in:
parent
4e73270b81
commit
b456e6a287
3 changed files with 294 additions and 72 deletions
|
@ -224,14 +224,27 @@ func TestGetBox(t *testing.T) {
|
|||
|
||||
var tkn bearer.Token
|
||||
gate := NewGateData(cred.PublicKey(), &tkn)
|
||||
|
||||
secret := []byte("secret")
|
||||
accessBox, _, err := PackTokens([]*GateData{gate}, secret, false)
|
||||
|
||||
t.Run("regular secret", func(t *testing.T) {
|
||||
accessBox, secrets, err := PackTokens([]*GateData{gate}, secret, false)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, hex.EncodeToString(secret), secrets.SecretKey)
|
||||
|
||||
box, err := accessBox.GetBox(cred, false)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, hex.EncodeToString(secret), box.Gate.SecretKey)
|
||||
})
|
||||
|
||||
t.Run("custom secret", func(t *testing.T) {
|
||||
accessBox, secrets, err := PackTokens([]*GateData{gate}, secret, true)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, string(secret), secrets.SecretKey)
|
||||
|
||||
box, err := accessBox.GetBox(cred, true)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, string(secret), box.Gate.SecretKey)
|
||||
})
|
||||
}
|
||||
|
||||
func TestAccessBox(t *testing.T) {
|
|
@ -16,27 +16,40 @@ import (
|
|||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object"
|
||||
oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id"
|
||||
oidtest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id/test"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/user"
|
||||
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
||||
"github.com/stretchr/testify/require"
|
||||
"go.uber.org/zap/zaptest"
|
||||
)
|
||||
|
||||
type frostfsMock struct {
|
||||
key *keys.PrivateKey
|
||||
objects map[string][]*object.Object
|
||||
errors map[string]error
|
||||
}
|
||||
|
||||
func newFrostfsMock() *frostfsMock {
|
||||
func newFrostfsMock(key *keys.PrivateKey) *frostfsMock {
|
||||
return &frostfsMock{
|
||||
objects: map[string][]*object.Object{},
|
||||
errors: map[string]error{},
|
||||
key: key,
|
||||
}
|
||||
}
|
||||
|
||||
func (f *frostfsMock) ownerID() user.ID {
|
||||
if f.key == nil {
|
||||
return user.ID{}
|
||||
}
|
||||
|
||||
var ownerID user.ID
|
||||
user.IDFromKey(&ownerID, f.key.PrivateKey.PublicKey)
|
||||
return ownerID
|
||||
}
|
||||
|
||||
func (f *frostfsMock) CreateObject(_ context.Context, prm PrmObjectCreate) (oid.ID, error) {
|
||||
var obj object.Object
|
||||
obj.SetPayload(prm.Payload)
|
||||
obj.SetOwnerID(prm.Creator)
|
||||
obj.SetOwnerID(f.ownerID())
|
||||
obj.SetContainerID(prm.Container)
|
||||
|
||||
a := object.NewAttribute()
|
||||
|
@ -81,7 +94,7 @@ func (f *frostfsMock) GetCredsObject(_ context.Context, prm PrmGetCredsObject) (
|
|||
|
||||
objects, ok := f.objects[prm.AccessKeyID]
|
||||
if !ok {
|
||||
return nil, errors.New("not found")
|
||||
return nil, ErrCustomAccessKeyIDNotFound
|
||||
}
|
||||
|
||||
return objects[len(objects)-1], nil
|
||||
|
@ -157,8 +170,9 @@ func TestGetBox(t *testing.T) {
|
|||
}}
|
||||
|
||||
secret := []byte("secret")
|
||||
accessBox, _, err := accessbox.PackTokens(gateData, secret, false)
|
||||
accessBox, secrets, err := accessbox.PackTokens(gateData, secret, false)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, hex.EncodeToString(secret), secrets.SecretKey)
|
||||
data, err := accessBox.Marshal()
|
||||
require.NoError(t, err)
|
||||
|
||||
|
@ -176,11 +190,7 @@ func TestGetBox(t *testing.T) {
|
|||
}
|
||||
|
||||
t.Run("no removing check, accessbox from cache", func(t *testing.T) {
|
||||
frostfs := newFrostfsMock()
|
||||
cfg.FrostFS = frostfs
|
||||
cfg.RemovingCheckAfterDurations = time.Hour
|
||||
cfg.Key = key
|
||||
creds := New(cfg)
|
||||
creds := newCreds(key, cfg, time.Hour)
|
||||
|
||||
cnrID := cidtest.ID()
|
||||
addr, err := creds.Put(ctx, CredentialsParam{Container: cnrID, Keys: keys.PublicKeys{key.PublicKey()}, AccessBox: accessBox})
|
||||
|
@ -191,30 +201,26 @@ func TestGetBox(t *testing.T) {
|
|||
_, _, err = creds.GetBox(ctx, addr.Container(), accessKeyID)
|
||||
require.NoError(t, err)
|
||||
|
||||
frostfs.errors[accessKeyID] = &apistatus.ObjectAlreadyRemoved{}
|
||||
creds.(*cred).frostFS.(*frostfsMock).errors[accessKeyID] = &apistatus.ObjectAlreadyRemoved{}
|
||||
_, _, err = creds.GetBox(ctx, addr.Container(), accessKeyID)
|
||||
require.NoError(t, err)
|
||||
})
|
||||
|
||||
t.Run("error while getting box from frostfs", func(t *testing.T) {
|
||||
frostfs := newFrostfsMock()
|
||||
cfg.FrostFS = frostfs
|
||||
cfg.RemovingCheckAfterDurations = 0
|
||||
cfg.Key = key
|
||||
creds := New(cfg)
|
||||
creds := newCreds(key, cfg, 0)
|
||||
|
||||
cnrID := cidtest.ID()
|
||||
addr, err := creds.Put(ctx, CredentialsParam{Container: cnrID, Keys: keys.PublicKeys{key.PublicKey()}, AccessBox: accessBox})
|
||||
require.NoError(t, err)
|
||||
|
||||
accessKeyID := getAccessKeyID(addr)
|
||||
frostfs.errors[accessKeyID] = errors.New("network error")
|
||||
creds.(*cred).frostFS.(*frostfsMock).errors[accessKeyID] = errors.New("network error")
|
||||
_, _, err = creds.GetBox(ctx, addr.Container(), accessKeyID)
|
||||
require.Error(t, err)
|
||||
})
|
||||
|
||||
t.Run("invalid key", func(t *testing.T) {
|
||||
frostfs := newFrostfsMock()
|
||||
frostfs := newFrostfsMock(key)
|
||||
|
||||
var obj object.Object
|
||||
obj.SetPayload(data)
|
||||
|
@ -232,7 +238,7 @@ func TestGetBox(t *testing.T) {
|
|||
})
|
||||
|
||||
t.Run("invalid payload", func(t *testing.T) {
|
||||
frostfs := newFrostfsMock()
|
||||
frostfs := newFrostfsMock(key)
|
||||
|
||||
var obj object.Object
|
||||
obj.SetPayload([]byte("invalid"))
|
||||
|
@ -250,11 +256,7 @@ func TestGetBox(t *testing.T) {
|
|||
})
|
||||
|
||||
t.Run("check attributes update", func(t *testing.T) {
|
||||
frostfs := newFrostfsMock()
|
||||
cfg.FrostFS = frostfs
|
||||
cfg.RemovingCheckAfterDurations = 0
|
||||
cfg.Key = key
|
||||
creds := New(cfg)
|
||||
creds := newCreds(key, cfg, 0)
|
||||
|
||||
cnrID := cidtest.ID()
|
||||
addr, err := creds.Put(ctx, CredentialsParam{Container: cnrID, Keys: keys.PublicKeys{key.PublicKey()}, AccessBox: accessBox})
|
||||
|
@ -280,11 +282,7 @@ func TestGetBox(t *testing.T) {
|
|||
})
|
||||
|
||||
t.Run("check accessbox update", func(t *testing.T) {
|
||||
frostfs := newFrostfsMock()
|
||||
cfg.FrostFS = frostfs
|
||||
cfg.RemovingCheckAfterDurations = 0
|
||||
cfg.Key = key
|
||||
creds := New(cfg)
|
||||
creds := newCreds(key, cfg, 0)
|
||||
|
||||
cnrID := cidtest.ID()
|
||||
addr, err := creds.Put(ctx, CredentialsParam{Container: cnrID, Keys: keys.PublicKeys{key.PublicKey()}, AccessBox: accessBox})
|
||||
|
@ -321,20 +319,36 @@ func TestGetBox(t *testing.T) {
|
|||
_, _, err = creds.GetBox(ctx, addr.Container(), accessKeyID)
|
||||
require.Error(t, err)
|
||||
|
||||
cfg.Key = newKey
|
||||
newCreds := New(cfg)
|
||||
newCfg := Config{
|
||||
FrostFS: creds.(*cred).frostFS,
|
||||
Key: newKey,
|
||||
CacheConfig: cfg.CacheConfig,
|
||||
}
|
||||
newCreds := New(newCfg)
|
||||
|
||||
box, _, err = newCreds.GetBox(ctx, addr.Container(), accessKeyID)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, hex.EncodeToString(newSecret), box.Gate.SecretKey)
|
||||
})
|
||||
|
||||
t.Run("check access key id uniqueness", func(t *testing.T) {
|
||||
creds := newCreds(key, cfg, 0)
|
||||
|
||||
prm := CredentialsParam{
|
||||
Container: cidtest.ID(),
|
||||
AccessBox: accessBox,
|
||||
Keys: keys.PublicKeys{key.PublicKey()},
|
||||
}
|
||||
|
||||
_, err = creds.Put(ctx, prm)
|
||||
require.NoError(t, err)
|
||||
|
||||
_, err = creds.Put(ctx, prm)
|
||||
require.NoError(t, err)
|
||||
})
|
||||
|
||||
t.Run("empty keys", func(t *testing.T) {
|
||||
frostfs := newFrostfsMock()
|
||||
cfg.FrostFS = frostfs
|
||||
cfg.RemovingCheckAfterDurations = 0
|
||||
cfg.Key = key
|
||||
creds := New(cfg)
|
||||
creds := newCreds(key, cfg, 0)
|
||||
|
||||
cnrID := cidtest.ID()
|
||||
_, err = creds.Put(ctx, CredentialsParam{Container: cnrID, AccessBox: accessBox})
|
||||
|
@ -342,11 +356,7 @@ func TestGetBox(t *testing.T) {
|
|||
})
|
||||
|
||||
t.Run("empty accessbox", func(t *testing.T) {
|
||||
frostfs := newFrostfsMock()
|
||||
cfg.FrostFS = frostfs
|
||||
cfg.RemovingCheckAfterDurations = 0
|
||||
cfg.Key = key
|
||||
creds := New(cfg)
|
||||
creds := newCreds(key, cfg, 0)
|
||||
|
||||
cnrID := cidtest.ID()
|
||||
_, err = creds.Put(ctx, CredentialsParam{Container: cnrID, Keys: keys.PublicKeys{key.PublicKey()}})
|
||||
|
@ -354,6 +364,73 @@ func TestGetBox(t *testing.T) {
|
|||
})
|
||||
}
|
||||
|
||||
func TestBoxWithCustomAccessKeyID(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
key, err := keys.NewPrivateKey()
|
||||
require.NoError(t, err)
|
||||
|
||||
gateData := []*accessbox.GateData{{
|
||||
BearerToken: &bearer.Token{},
|
||||
GateKey: key.PublicKey(),
|
||||
}}
|
||||
|
||||
secret := []byte("secret")
|
||||
accessBox, secrets, err := accessbox.PackTokens(gateData, secret, true)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, string(secret), secrets.SecretKey)
|
||||
|
||||
cfg := Config{
|
||||
CacheConfig: &cache.Config{
|
||||
Size: 10,
|
||||
Lifetime: 24 * time.Hour,
|
||||
Logger: zaptest.NewLogger(t),
|
||||
},
|
||||
}
|
||||
|
||||
t.Run("check secret format", func(t *testing.T) {
|
||||
creds := newCreds(key, cfg, 0)
|
||||
|
||||
prm := CredentialsParam{
|
||||
Container: cidtest.ID(),
|
||||
AccessKeyID: "custom-access-key-id",
|
||||
AccessBox: accessBox,
|
||||
Keys: keys.PublicKeys{key.PublicKey()},
|
||||
}
|
||||
|
||||
_, err = creds.Put(ctx, prm)
|
||||
require.NoError(t, err)
|
||||
|
||||
box, _, err := creds.GetBox(ctx, prm.Container, prm.AccessKeyID)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, string(secret), box.Gate.SecretKey)
|
||||
})
|
||||
|
||||
t.Run("check custom access key id uniqueness", func(t *testing.T) {
|
||||
creds := newCreds(key, cfg, 0)
|
||||
|
||||
prm := CredentialsParam{
|
||||
Container: cidtest.ID(),
|
||||
AccessKeyID: "custom-access-key-id",
|
||||
AccessBox: accessBox,
|
||||
Keys: keys.PublicKeys{key.PublicKey()},
|
||||
}
|
||||
|
||||
_, err = creds.Put(ctx, prm)
|
||||
require.NoError(t, err)
|
||||
|
||||
_, err = creds.Put(ctx, prm)
|
||||
require.Error(t, err)
|
||||
})
|
||||
}
|
||||
|
||||
func newCreds(key *keys.PrivateKey, cfg Config, removingCheckDuration time.Duration) Credentials {
|
||||
frostfs := newFrostfsMock(key)
|
||||
cfg.FrostFS = frostfs
|
||||
cfg.RemovingCheckAfterDurations = removingCheckDuration
|
||||
cfg.Key = key
|
||||
return New(cfg)
|
||||
}
|
||||
|
||||
func getAccessKeyID(addr oid.Address) string {
|
||||
return strings.ReplaceAll(addr.EncodeToString(), "/", "0")
|
||||
}
|
||||
|
|
|
@ -2,23 +2,27 @@ package frostfs
|
|||
|
||||
import (
|
||||
"context"
|
||||
"strconv"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
objectv2 "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/object"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/layer"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/middleware"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/authmate"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/creds/accessbox"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/creds/tokens"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/bearer"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object"
|
||||
oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id"
|
||||
oidtest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id/test"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/user"
|
||||
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
||||
"github.com/stretchr/testify/require"
|
||||
"go.uber.org/zap/zaptest"
|
||||
)
|
||||
|
||||
func TestGetCredsObject(t *testing.T) {
|
||||
func TestCredsObject(t *testing.T) {
|
||||
ctx, bktName, payload, newPayload := context.Background(), "bucket", []byte("payload"), []byte("new-payload")
|
||||
|
||||
key, err := keys.NewPrivateKey()
|
||||
|
@ -45,38 +49,166 @@ func TestGetCredsObject(t *testing.T) {
|
|||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
objID, err := frostfs.CreateObject(ctx, tokens.PrmObjectCreate{
|
||||
t.Run("regular access key", func(t *testing.T) {
|
||||
attr1 := object.NewAttribute()
|
||||
attr1.SetKey("attr1")
|
||||
attr1.SetValue("val1")
|
||||
|
||||
prm := tokens.PrmObjectCreate{
|
||||
Container: cnrID,
|
||||
Filepath: "regular-obj",
|
||||
ExpirationEpoch: 10,
|
||||
Payload: payload,
|
||||
})
|
||||
CustomAttributes: []object.Attribute{*attr1},
|
||||
}
|
||||
|
||||
objID, err := frostfs.CreateObject(ctx, prm)
|
||||
require.NoError(t, err)
|
||||
|
||||
var addr oid.Address
|
||||
addr.SetContainer(cnrID)
|
||||
addr.SetObject(objID)
|
||||
accessKeyID := cnrID.EncodeToString() + "0" + objID.EncodeToString()
|
||||
|
||||
accessKeyID := getAccessKeyID(addr)
|
||||
obj, err := frostfs.GetCredsObject(ctx, tokens.PrmGetCredsObject{Container: cnrID, AccessKeyID: accessKeyID})
|
||||
require.NoError(t, err)
|
||||
assertParamsSet(t, prm, obj, userID)
|
||||
|
||||
obj, err := frostfs.GetCredsObject(ctx, tokens.PrmGetCredsObject{
|
||||
t.Run("update existing", func(t *testing.T) {
|
||||
attr2 := object.NewAttribute()
|
||||
attr2.SetKey("attr2")
|
||||
attr2.SetValue("val2")
|
||||
|
||||
prmNew := tokens.PrmObjectCreate{
|
||||
Container: cnrID,
|
||||
AccessKeyID: accessKeyID,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, payload, obj.Payload())
|
||||
Filepath: "regular-obj-new",
|
||||
ExpirationEpoch: 11,
|
||||
Payload: newPayload,
|
||||
CustomAttributes: []object.Attribute{*attr2},
|
||||
NewVersionForAccessKeyID: accessKeyID,
|
||||
}
|
||||
|
||||
_, err = frostfs.CreateObject(ctx, tokens.PrmObjectCreate{
|
||||
_, err = frostfs.CreateObject(ctx, prmNew)
|
||||
require.NoError(t, err)
|
||||
|
||||
obj, err = frostfs.GetCredsObject(ctx, tokens.PrmGetCredsObject{Container: cnrID, AccessKeyID: accessKeyID})
|
||||
require.NoError(t, err)
|
||||
assertParamsSet(t, prmNew, obj, userID)
|
||||
})
|
||||
|
||||
t.Run("update not existing", func(t *testing.T) {
|
||||
attr2 := object.NewAttribute()
|
||||
attr2.SetKey("attr2")
|
||||
attr2.SetValue("val2")
|
||||
|
||||
addr := oidtest.Address()
|
||||
accessKeyIDNotExisting := getAccessKeyID(addr)
|
||||
|
||||
prmNew := tokens.PrmObjectCreate{
|
||||
Container: cnrID,
|
||||
Filepath: "regular-obj-new",
|
||||
ExpirationEpoch: 11,
|
||||
Payload: newPayload,
|
||||
CustomAttributes: []object.Attribute{*attr2},
|
||||
NewVersionForAccessKeyID: accessKeyIDNotExisting,
|
||||
}
|
||||
|
||||
_, err = frostfs.CreateObject(ctx, prmNew)
|
||||
require.NoError(t, err)
|
||||
|
||||
obj, err = frostfs.GetCredsObject(ctx, tokens.PrmGetCredsObject{Container: cnrID, AccessKeyID: accessKeyIDNotExisting})
|
||||
require.NoError(t, err)
|
||||
assertParamsSet(t, prmNew, obj, userID)
|
||||
})
|
||||
})
|
||||
|
||||
t.Run("custom access key", func(t *testing.T) {
|
||||
attr1 := object.NewAttribute()
|
||||
attr1.SetKey("attr1")
|
||||
attr1.SetValue("val1")
|
||||
|
||||
accessKeyID := "custom-access-key-id"
|
||||
|
||||
prm := tokens.PrmObjectCreate{
|
||||
Container: cnrID,
|
||||
Filepath: "custom-obj",
|
||||
ExpirationEpoch: 10,
|
||||
Payload: payload,
|
||||
CustomAccessKey: accessKeyID,
|
||||
CustomAttributes: []object.Attribute{*attr1},
|
||||
}
|
||||
|
||||
_, err = frostfs.CreateObject(ctx, prm)
|
||||
require.NoError(t, err)
|
||||
|
||||
obj, err := frostfs.GetCredsObject(ctx, tokens.PrmGetCredsObject{Container: cnrID, AccessKeyID: accessKeyID})
|
||||
require.NoError(t, err)
|
||||
assertParamsSet(t, prm, obj, userID)
|
||||
|
||||
t.Run("update", func(t *testing.T) {
|
||||
attr2 := object.NewAttribute()
|
||||
attr2.SetKey("attr2")
|
||||
attr2.SetValue("val2")
|
||||
|
||||
prmNew := tokens.PrmObjectCreate{
|
||||
Container: cnrID,
|
||||
Filepath: "custom-obj-new",
|
||||
ExpirationEpoch: 11,
|
||||
Payload: newPayload,
|
||||
CustomAttributes: []object.Attribute{*attr2},
|
||||
NewVersionForAccessKeyID: accessKeyID,
|
||||
}
|
||||
|
||||
_, err = frostfs.CreateObject(ctx, prmNew)
|
||||
require.NoError(t, err)
|
||||
|
||||
obj, err = frostfs.GetCredsObject(ctx, tokens.PrmGetCredsObject{Container: cnrID, AccessKeyID: accessKeyID})
|
||||
require.NoError(t, err)
|
||||
assertParamsSet(t, prmNew, obj, userID)
|
||||
})
|
||||
|
||||
t.Run("update not existing", func(t *testing.T) {
|
||||
accessKeyIDNotExisting := "unknown"
|
||||
|
||||
prmNew := tokens.PrmObjectCreate{
|
||||
Container: cnrID,
|
||||
Payload: newPayload,
|
||||
NewVersionForAccessKeyID: accessKeyID,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
NewVersionForAccessKeyID: accessKeyIDNotExisting,
|
||||
}
|
||||
|
||||
obj, err = frostfs.GetCredsObject(ctx, tokens.PrmGetCredsObject{
|
||||
Container: cnrID,
|
||||
AccessKeyID: getAccessKeyID(addr),
|
||||
_, err = frostfs.CreateObject(ctx, prmNew)
|
||||
require.Error(t, err)
|
||||
})
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, newPayload, obj.Payload())
|
||||
})
|
||||
}
|
||||
|
||||
func assertParamsSet(t *testing.T, prm tokens.PrmObjectCreate, obj *object.Object, userID user.ID) {
|
||||
require.Equal(t, prm.Payload, obj.Payload())
|
||||
require.True(t, userID.Equals(obj.OwnerID()), "owners not matched")
|
||||
|
||||
require.True(t, containerAttribute(obj.Attributes(), object.AttributeFilePath, prm.Filepath), "missing FilePath")
|
||||
require.True(t, containerAttribute(obj.Attributes(), objectv2.SysAttributeExpEpoch, strconv.FormatUint(prm.ExpirationEpoch, 10)), "missing expiration epoch")
|
||||
|
||||
var crdtName string
|
||||
if prm.CustomAccessKey != "" {
|
||||
crdtName = prm.CustomAccessKey
|
||||
} else if prm.NewVersionForAccessKeyID != "" {
|
||||
crdtName = prm.NewVersionForAccessKeyID
|
||||
}
|
||||
if crdtName != "" {
|
||||
require.Truef(t, containerAttribute(obj.Attributes(), accessBoxCRDTNameAttr, crdtName), "wrong crdt name '%s'", crdtName)
|
||||
}
|
||||
|
||||
for _, attr := range prm.CustomAttributes {
|
||||
require.True(t, containerAttribute(obj.Attributes(), attr.Key(), attr.Value()), "missing custom attribute")
|
||||
}
|
||||
}
|
||||
|
||||
func containerAttribute(attrs []object.Attribute, key, val string) bool {
|
||||
for _, attr := range attrs {
|
||||
if attr.Key() == key && attr.Value() == val {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func getAccessKeyID(addr oid.Address) string {
|
||||
|
|
Loading…
Reference in a new issue