All checks were successful
Vulncheck / Vulncheck (push) Successful in 1m9s
Build / Build Components (push) Successful in 1m59s
Pre-commit hooks / Pre-commit (push) Successful in 2m2s
Tests and linters / Run gofumpt (push) Successful in 3m4s
Tests and linters / Lint (push) Successful in 3m26s
Tests and linters / Staticcheck (push) Successful in 3m39s
Tests and linters / Tests (push) Successful in 3m44s
Tests and linters / gopls check (push) Successful in 4m2s
Tests and linters / Tests with -race (push) Successful in 4m23s
OCI image / Build container images (push) Successful in 4m15s
If request has no tag, but request's public key is netmap node's key or one of allowed internal tag keys from config, then request must use internal IO tag. Change-Id: Iff93b626941a81b088d8999b3f2947f9501dcdf8 Signed-off-by: Dmitrii Stepanov <d.stepanov@yadro.com>
599 lines
15 KiB
Go
599 lines
15 KiB
Go
package object
|
|
|
|
import (
|
|
"context"
|
|
"crypto/ecdsa"
|
|
"fmt"
|
|
"strconv"
|
|
"testing"
|
|
|
|
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/core/container"
|
|
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/util/logger"
|
|
utilTesting "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/util/testing"
|
|
objectV2 "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/api/object"
|
|
containerSDK "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container"
|
|
cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
|
|
cidtest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id/test"
|
|
frostfsecdsa "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/crypto/ecdsa"
|
|
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/netmap"
|
|
objectSDK "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"
|
|
sessiontest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/session/test"
|
|
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/user"
|
|
"github.com/google/uuid"
|
|
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
|
"github.com/stretchr/testify/require"
|
|
"go.uber.org/zap/zaptest"
|
|
)
|
|
|
|
func blankValidObject(key *ecdsa.PrivateKey) *objectSDK.Object {
|
|
var idOwner user.ID
|
|
user.IDFromKey(&idOwner, key.PublicKey)
|
|
|
|
obj := objectSDK.New()
|
|
obj.SetContainerID(cidtest.ID())
|
|
obj.SetOwnerID(idOwner)
|
|
|
|
return obj
|
|
}
|
|
|
|
type testNetState struct {
|
|
epoch uint64
|
|
}
|
|
|
|
func (s testNetState) CurrentEpoch() uint64 {
|
|
return s.epoch
|
|
}
|
|
|
|
type testLockSource struct {
|
|
m map[oid.Address]bool
|
|
}
|
|
|
|
func (t testLockSource) IsLocked(_ context.Context, address oid.Address) (bool, error) {
|
|
return t.m[address], nil
|
|
}
|
|
|
|
func TestFormatValidator_Validate(t *testing.T) {
|
|
const curEpoch = 13
|
|
|
|
ls := testLockSource{
|
|
m: make(map[oid.Address]bool),
|
|
}
|
|
|
|
v := NewFormatValidator(
|
|
WithNetState(testNetState{
|
|
epoch: curEpoch,
|
|
}),
|
|
WithLockSource(ls),
|
|
WithLogger(logger.NewLoggerWrapper(zaptest.NewLogger(t))),
|
|
)
|
|
|
|
ownerKey, err := keys.NewPrivateKey()
|
|
require.NoError(t, err)
|
|
|
|
t.Run("nil input", func(t *testing.T) {
|
|
require.Error(t, v.Validate(context.Background(), nil, true))
|
|
})
|
|
|
|
t.Run("nil identifier", func(t *testing.T) {
|
|
obj := objectSDK.New()
|
|
|
|
require.ErrorIs(t, v.Validate(context.Background(), obj, false), errNilID)
|
|
})
|
|
|
|
t.Run("nil container identifier", func(t *testing.T) {
|
|
obj := objectSDK.New()
|
|
obj.SetID(oidtest.ID())
|
|
|
|
require.ErrorIs(t, v.Validate(context.Background(), obj, true), errNilCID)
|
|
})
|
|
|
|
t.Run("unsigned object", func(t *testing.T) {
|
|
obj := objectSDK.New()
|
|
obj.SetContainerID(cidtest.ID())
|
|
obj.SetID(oidtest.ID())
|
|
|
|
require.Error(t, v.Validate(context.Background(), obj, false))
|
|
})
|
|
|
|
t.Run("correct w/ session token", func(t *testing.T) {
|
|
var idOwner user.ID
|
|
user.IDFromKey(&idOwner, ownerKey.PrivateKey.PublicKey)
|
|
|
|
tok := sessiontest.Object()
|
|
err := tok.Sign(ownerKey.PrivateKey)
|
|
require.NoError(t, err)
|
|
|
|
obj := objectSDK.New()
|
|
obj.SetContainerID(cidtest.ID())
|
|
obj.SetSessionToken(tok)
|
|
obj.SetOwnerID(idOwner)
|
|
|
|
require.NoError(t, objectSDK.SetIDWithSignature(ownerKey.PrivateKey, obj))
|
|
|
|
require.NoError(t, v.Validate(context.Background(), obj, false))
|
|
})
|
|
|
|
t.Run("correct w/o session token", func(t *testing.T) {
|
|
obj := blankValidObject(&ownerKey.PrivateKey)
|
|
|
|
require.NoError(t, objectSDK.SetIDWithSignature(ownerKey.PrivateKey, obj))
|
|
|
|
require.NoError(t, v.Validate(context.Background(), obj, false))
|
|
})
|
|
|
|
t.Run("tombstone content", func(t *testing.T) {
|
|
obj := objectSDK.New()
|
|
obj.SetType(objectSDK.TypeTombstone)
|
|
obj.SetContainerID(cidtest.ID())
|
|
|
|
_, err := v.ValidateContent(obj)
|
|
require.Error(t, err) // no tombstone content
|
|
|
|
content := objectSDK.NewTombstone()
|
|
content.SetMembers([]oid.ID{oidtest.ID()})
|
|
|
|
data, err := content.Marshal()
|
|
require.NoError(t, err)
|
|
|
|
obj.SetPayload(data)
|
|
|
|
_, err = v.ValidateContent(obj)
|
|
require.Error(t, err) // no members in tombstone
|
|
|
|
content.SetMembers([]oid.ID{oidtest.ID()})
|
|
|
|
data, err = content.Marshal()
|
|
require.NoError(t, err)
|
|
|
|
obj.SetPayload(data)
|
|
|
|
_, err = v.ValidateContent(obj)
|
|
require.Error(t, err) // no expiration epoch in tombstone
|
|
|
|
var expirationAttribute objectSDK.Attribute
|
|
expirationAttribute.SetKey(objectV2.SysAttributeExpEpoch)
|
|
expirationAttribute.SetValue(strconv.Itoa(10))
|
|
|
|
obj.SetAttributes(expirationAttribute)
|
|
|
|
_, err = v.ValidateContent(obj)
|
|
require.Error(t, err) // different expiration values
|
|
|
|
id := oidtest.ID()
|
|
|
|
content.SetExpirationEpoch(10)
|
|
content.SetMembers([]oid.ID{id})
|
|
data, err = content.Marshal()
|
|
require.NoError(t, err)
|
|
|
|
obj.SetPayload(data)
|
|
|
|
contentGot, err := v.ValidateContent(obj)
|
|
require.NoError(t, err) // all good
|
|
|
|
require.EqualValues(t, []oid.ID{id}, contentGot.Objects())
|
|
require.Equal(t, objectSDK.TypeTombstone, contentGot.Type())
|
|
})
|
|
|
|
t.Run("expiration", func(t *testing.T) {
|
|
fn := func(val string) *objectSDK.Object {
|
|
obj := blankValidObject(&ownerKey.PrivateKey)
|
|
|
|
var a objectSDK.Attribute
|
|
a.SetKey(objectV2.SysAttributeExpEpoch)
|
|
a.SetValue(val)
|
|
|
|
obj.SetAttributes(a)
|
|
|
|
require.NoError(t, objectSDK.SetIDWithSignature(ownerKey.PrivateKey, obj))
|
|
|
|
return obj
|
|
}
|
|
|
|
t.Run("invalid attribute value", func(t *testing.T) {
|
|
val := "text"
|
|
err := v.Validate(context.Background(), fn(val), false)
|
|
require.Error(t, err)
|
|
err = v.Validate(context.Background(), fn(val), true)
|
|
require.Error(t, err)
|
|
})
|
|
|
|
t.Run("expired object", func(t *testing.T) {
|
|
val := strconv.FormatUint(curEpoch-1, 10)
|
|
obj := fn(val)
|
|
|
|
t.Run("non-locked", func(t *testing.T) {
|
|
err := v.Validate(context.Background(), obj, false)
|
|
require.ErrorIs(t, err, errExpired)
|
|
})
|
|
|
|
t.Run("locked", func(t *testing.T) {
|
|
var addr oid.Address
|
|
oID, _ := obj.ID()
|
|
cID, _ := obj.ContainerID()
|
|
|
|
addr.SetContainer(cID)
|
|
addr.SetObject(oID)
|
|
ls.m[addr] = true
|
|
|
|
err := v.Validate(context.Background(), obj, false)
|
|
require.NoError(t, err)
|
|
})
|
|
})
|
|
|
|
t.Run("alive object", func(t *testing.T) {
|
|
val := strconv.FormatUint(curEpoch, 10)
|
|
err := v.Validate(context.Background(), fn(val), true)
|
|
require.NoError(t, err)
|
|
})
|
|
})
|
|
|
|
t.Run("attributes", func(t *testing.T) {
|
|
t.Run("duplication", func(t *testing.T) {
|
|
obj := blankValidObject(&ownerKey.PrivateKey)
|
|
|
|
var a1 objectSDK.Attribute
|
|
a1.SetKey("key1")
|
|
a1.SetValue("val1")
|
|
|
|
var a2 objectSDK.Attribute
|
|
a2.SetKey("key2")
|
|
a2.SetValue("val2")
|
|
|
|
obj.SetAttributes(a1, a2)
|
|
|
|
err := v.checkAttributes(obj)
|
|
require.NoError(t, err)
|
|
|
|
a2.SetKey(a1.Key())
|
|
obj.SetAttributes(a1, a2)
|
|
|
|
err = v.checkAttributes(obj)
|
|
require.Equal(t, errDuplAttr, err)
|
|
})
|
|
|
|
t.Run("empty value", func(t *testing.T) {
|
|
obj := blankValidObject(&ownerKey.PrivateKey)
|
|
|
|
var a objectSDK.Attribute
|
|
a.SetKey("key")
|
|
|
|
obj.SetAttributes(a)
|
|
|
|
err := v.checkAttributes(obj)
|
|
require.Equal(t, errEmptyAttrVal, err)
|
|
})
|
|
})
|
|
}
|
|
|
|
func TestFormatValidator_ValidateTokenIssuer(t *testing.T) {
|
|
const curEpoch = 13
|
|
|
|
ls := testLockSource{
|
|
m: make(map[oid.Address]bool),
|
|
}
|
|
|
|
signer, err := keys.NewPrivateKey()
|
|
require.NoError(t, err)
|
|
|
|
var owner user.ID
|
|
ownerPrivKey, err := keys.NewPrivateKey()
|
|
require.NoError(t, err)
|
|
user.IDFromKey(&owner, ownerPrivKey.PrivateKey.PublicKey)
|
|
|
|
t.Run("different issuer and owner, verify issuer disabled", func(t *testing.T) {
|
|
t.Parallel()
|
|
v := NewFormatValidator(
|
|
WithNetState(testNetState{
|
|
epoch: curEpoch,
|
|
}),
|
|
WithLockSource(ls),
|
|
WithVerifySessionTokenIssuer(false),
|
|
WithLogger(logger.NewLoggerWrapper(zaptest.NewLogger(t))),
|
|
)
|
|
|
|
tok := sessiontest.Object()
|
|
fsPubKey := frostfsecdsa.PublicKey(*signer.PublicKey())
|
|
tok.SetID(uuid.New())
|
|
tok.SetAuthKey(&fsPubKey)
|
|
tok.SetExp(100500)
|
|
tok.SetIat(1)
|
|
tok.SetNbf(1)
|
|
require.NoError(t, tok.Sign(signer.PrivateKey))
|
|
|
|
obj := objectSDK.New()
|
|
obj.SetContainerID(cidtest.ID())
|
|
obj.SetSessionToken(tok)
|
|
obj.SetOwnerID(owner)
|
|
require.NoError(t, objectSDK.SetIDWithSignature(signer.PrivateKey, obj))
|
|
|
|
require.NoError(t, v.Validate(context.Background(), obj, false))
|
|
})
|
|
|
|
t.Run("different issuer and owner, issuer is IR node, verify issuer enabled", func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
cnrID := cidtest.ID()
|
|
cont := containerSDK.Container{}
|
|
cont.Init()
|
|
pp := netmap.PlacementPolicy{}
|
|
require.NoError(t, pp.DecodeString("REP 1"))
|
|
cont.SetPlacementPolicy(pp)
|
|
|
|
v := NewFormatValidator(
|
|
WithNetState(testNetState{
|
|
epoch: curEpoch,
|
|
}),
|
|
WithLockSource(ls),
|
|
WithVerifySessionTokenIssuer(true),
|
|
WithInnerRing(&testIRSource{
|
|
irNodes: [][]byte{signer.PublicKey().Bytes()},
|
|
}),
|
|
WithContainersSource(
|
|
&testContainerSource{
|
|
containers: map[cid.ID]*container.Container{
|
|
cnrID: {
|
|
Value: cont,
|
|
},
|
|
},
|
|
},
|
|
),
|
|
WithLogger(logger.NewLoggerWrapper(zaptest.NewLogger(t))),
|
|
)
|
|
|
|
tok := sessiontest.Object()
|
|
fsPubKey := frostfsecdsa.PublicKey(*signer.PublicKey())
|
|
tok.SetID(uuid.New())
|
|
tok.SetAuthKey(&fsPubKey)
|
|
tok.SetExp(100500)
|
|
tok.SetIat(1)
|
|
tok.SetNbf(1)
|
|
require.NoError(t, tok.Sign(signer.PrivateKey))
|
|
|
|
obj := objectSDK.New()
|
|
obj.SetContainerID(cnrID)
|
|
obj.SetSessionToken(tok)
|
|
obj.SetOwnerID(owner)
|
|
require.NoError(t, objectSDK.SetIDWithSignature(signer.PrivateKey, obj))
|
|
|
|
require.NoError(t, v.Validate(context.Background(), obj, false))
|
|
})
|
|
|
|
t.Run("different issuer and owner, issuer is container node in current epoch, verify issuer enabled", func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
tok := sessiontest.Object()
|
|
fsPubKey := frostfsecdsa.PublicKey(*signer.PublicKey())
|
|
tok.SetID(uuid.New())
|
|
tok.SetAuthKey(&fsPubKey)
|
|
tok.SetExp(100500)
|
|
tok.SetIat(1)
|
|
tok.SetNbf(1)
|
|
require.NoError(t, tok.Sign(signer.PrivateKey))
|
|
|
|
cnrID := cidtest.ID()
|
|
cont := containerSDK.Container{}
|
|
cont.Init()
|
|
pp := netmap.PlacementPolicy{}
|
|
require.NoError(t, pp.DecodeString("REP 1"))
|
|
cont.SetPlacementPolicy(pp)
|
|
|
|
var node netmap.NodeInfo
|
|
node.SetPublicKey(signer.PublicKey().Bytes())
|
|
currentEpochNM := &netmap.NetMap{}
|
|
currentEpochNM.SetEpoch(curEpoch)
|
|
currentEpochNM.SetNodes([]netmap.NodeInfo{node})
|
|
|
|
obj := objectSDK.New()
|
|
obj.SetContainerID(cnrID)
|
|
obj.SetSessionToken(tok)
|
|
obj.SetOwnerID(owner)
|
|
require.NoError(t, objectSDK.SetIDWithSignature(signer.PrivateKey, obj))
|
|
|
|
v := NewFormatValidator(
|
|
WithNetState(testNetState{
|
|
epoch: curEpoch,
|
|
}),
|
|
WithLockSource(ls),
|
|
WithVerifySessionTokenIssuer(true),
|
|
WithInnerRing(&testIRSource{
|
|
irNodes: [][]byte{},
|
|
}),
|
|
WithContainersSource(
|
|
&testContainerSource{
|
|
containers: map[cid.ID]*container.Container{
|
|
cnrID: {
|
|
Value: cont,
|
|
},
|
|
},
|
|
},
|
|
),
|
|
WithNetmapSource(
|
|
&utilTesting.TestNetmapSource{
|
|
Netmaps: map[uint64]*netmap.NetMap{
|
|
curEpoch: currentEpochNM,
|
|
},
|
|
CurrentEpoch: curEpoch,
|
|
},
|
|
),
|
|
WithLogger(logger.NewLoggerWrapper(zaptest.NewLogger(t))),
|
|
)
|
|
|
|
require.NoError(t, v.Validate(context.Background(), obj, false))
|
|
})
|
|
|
|
t.Run("different issuer and owner, issuer is container node in previous epoch, verify issuer enabled", func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
tok := sessiontest.Object()
|
|
fsPubKey := frostfsecdsa.PublicKey(*signer.PublicKey())
|
|
tok.SetID(uuid.New())
|
|
tok.SetAuthKey(&fsPubKey)
|
|
tok.SetExp(100500)
|
|
tok.SetIat(1)
|
|
tok.SetNbf(1)
|
|
require.NoError(t, tok.Sign(signer.PrivateKey))
|
|
|
|
cnrID := cidtest.ID()
|
|
cont := containerSDK.Container{}
|
|
cont.Init()
|
|
pp := netmap.PlacementPolicy{}
|
|
require.NoError(t, pp.DecodeString("REP 1"))
|
|
cont.SetPlacementPolicy(pp)
|
|
|
|
var issuerNode netmap.NodeInfo
|
|
issuerNode.SetPublicKey(signer.PublicKey().Bytes())
|
|
|
|
var nonIssuerNode netmap.NodeInfo
|
|
nonIssuerKey, err := keys.NewPrivateKey()
|
|
require.NoError(t, err)
|
|
nonIssuerNode.SetPublicKey(nonIssuerKey.PublicKey().Bytes())
|
|
|
|
currentEpochNM := &netmap.NetMap{}
|
|
currentEpochNM.SetEpoch(curEpoch)
|
|
currentEpochNM.SetNodes([]netmap.NodeInfo{nonIssuerNode})
|
|
|
|
previousEpochNM := &netmap.NetMap{}
|
|
previousEpochNM.SetEpoch(curEpoch - 1)
|
|
previousEpochNM.SetNodes([]netmap.NodeInfo{issuerNode})
|
|
|
|
obj := objectSDK.New()
|
|
obj.SetContainerID(cnrID)
|
|
obj.SetSessionToken(tok)
|
|
obj.SetOwnerID(owner)
|
|
require.NoError(t, objectSDK.SetIDWithSignature(signer.PrivateKey, obj))
|
|
|
|
v := NewFormatValidator(
|
|
WithNetState(testNetState{
|
|
epoch: curEpoch,
|
|
}),
|
|
WithLockSource(ls),
|
|
WithVerifySessionTokenIssuer(true),
|
|
WithInnerRing(&testIRSource{
|
|
irNodes: [][]byte{},
|
|
}),
|
|
WithContainersSource(
|
|
&testContainerSource{
|
|
containers: map[cid.ID]*container.Container{
|
|
cnrID: {
|
|
Value: cont,
|
|
},
|
|
},
|
|
},
|
|
),
|
|
WithNetmapSource(
|
|
&utilTesting.TestNetmapSource{
|
|
Netmaps: map[uint64]*netmap.NetMap{
|
|
curEpoch: currentEpochNM,
|
|
curEpoch - 1: previousEpochNM,
|
|
},
|
|
CurrentEpoch: curEpoch,
|
|
},
|
|
),
|
|
WithLogger(logger.NewLoggerWrapper(zaptest.NewLogger(t))),
|
|
)
|
|
|
|
require.NoError(t, v.Validate(context.Background(), obj, false))
|
|
})
|
|
|
|
t.Run("different issuer and owner, issuer is unknown, verify issuer enabled", func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
tok := sessiontest.Object()
|
|
fsPubKey := frostfsecdsa.PublicKey(*signer.PublicKey())
|
|
tok.SetID(uuid.New())
|
|
tok.SetAuthKey(&fsPubKey)
|
|
tok.SetExp(100500)
|
|
tok.SetIat(1)
|
|
tok.SetNbf(1)
|
|
require.NoError(t, tok.Sign(signer.PrivateKey))
|
|
|
|
cnrID := cidtest.ID()
|
|
cont := containerSDK.Container{}
|
|
cont.Init()
|
|
pp := netmap.PlacementPolicy{}
|
|
require.NoError(t, pp.DecodeString("REP 1"))
|
|
cont.SetPlacementPolicy(pp)
|
|
|
|
var nonIssuerNode1 netmap.NodeInfo
|
|
nonIssuerKey1, err := keys.NewPrivateKey()
|
|
require.NoError(t, err)
|
|
nonIssuerNode1.SetPublicKey(nonIssuerKey1.PublicKey().Bytes())
|
|
|
|
var nonIssuerNode2 netmap.NodeInfo
|
|
nonIssuerKey2, err := keys.NewPrivateKey()
|
|
require.NoError(t, err)
|
|
nonIssuerNode2.SetPublicKey(nonIssuerKey2.PublicKey().Bytes())
|
|
|
|
currentEpochNM := &netmap.NetMap{}
|
|
currentEpochNM.SetEpoch(curEpoch)
|
|
currentEpochNM.SetNodes([]netmap.NodeInfo{nonIssuerNode1})
|
|
|
|
previousEpochNM := &netmap.NetMap{}
|
|
previousEpochNM.SetEpoch(curEpoch - 1)
|
|
previousEpochNM.SetNodes([]netmap.NodeInfo{nonIssuerNode2})
|
|
|
|
obj := objectSDK.New()
|
|
obj.SetContainerID(cnrID)
|
|
obj.SetSessionToken(tok)
|
|
obj.SetOwnerID(owner)
|
|
require.NoError(t, objectSDK.SetIDWithSignature(signer.PrivateKey, obj))
|
|
|
|
v := NewFormatValidator(
|
|
WithNetState(testNetState{
|
|
epoch: curEpoch,
|
|
}),
|
|
WithLockSource(ls),
|
|
WithVerifySessionTokenIssuer(true),
|
|
WithInnerRing(&testIRSource{
|
|
irNodes: [][]byte{},
|
|
}),
|
|
WithContainersSource(
|
|
&testContainerSource{
|
|
containers: map[cid.ID]*container.Container{
|
|
cnrID: {
|
|
Value: cont,
|
|
},
|
|
},
|
|
},
|
|
),
|
|
WithNetmapSource(
|
|
&utilTesting.TestNetmapSource{
|
|
Netmaps: map[uint64]*netmap.NetMap{
|
|
curEpoch: currentEpochNM,
|
|
curEpoch - 1: previousEpochNM,
|
|
},
|
|
CurrentEpoch: curEpoch,
|
|
},
|
|
),
|
|
WithLogger(logger.NewLoggerWrapper(zaptest.NewLogger(t))),
|
|
)
|
|
|
|
require.Error(t, v.Validate(context.Background(), obj, false))
|
|
})
|
|
}
|
|
|
|
type testIRSource struct {
|
|
irNodes [][]byte
|
|
}
|
|
|
|
func (s *testIRSource) InnerRingKeys(_ context.Context) ([][]byte, error) {
|
|
return s.irNodes, nil
|
|
}
|
|
|
|
type testContainerSource struct {
|
|
containers map[cid.ID]*container.Container
|
|
}
|
|
|
|
func (s *testContainerSource) Get(ctx context.Context, cnrID cid.ID) (*container.Container, error) {
|
|
if cnr, found := s.containers[cnrID]; found {
|
|
return cnr, nil
|
|
}
|
|
return nil, fmt.Errorf("container not found")
|
|
}
|
|
|
|
func (s *testContainerSource) DeletionInfo(context.Context, cid.ID) (*container.DelInfo, error) {
|
|
return nil, nil
|
|
}
|