344 lines
8.2 KiB
Go
344 lines
8.2 KiB
Go
package lifecycle
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"encoding/xml"
|
|
"errors"
|
|
"io"
|
|
"testing"
|
|
|
|
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/data"
|
|
cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
|
|
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/netmap"
|
|
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/pool"
|
|
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/user"
|
|
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
|
"github.com/nspcc-dev/neo-go/pkg/util"
|
|
"github.com/stretchr/testify/require"
|
|
"go.uber.org/zap/zaptest"
|
|
)
|
|
|
|
var _ UserFetcher = (*userFetcherMock)(nil)
|
|
|
|
type userFetcherMock struct {
|
|
users map[util.Uint160]*keys.PrivateKey
|
|
}
|
|
|
|
func newUserFetcherMock(users map[util.Uint160]*keys.PrivateKey) *userFetcherMock {
|
|
if users == nil {
|
|
users = map[util.Uint160]*keys.PrivateKey{}
|
|
}
|
|
return &userFetcherMock{
|
|
users: users,
|
|
}
|
|
}
|
|
|
|
func (u *userFetcherMock) Users() ([]util.Uint160, error) {
|
|
res := make([]util.Uint160, 0, len(u.users))
|
|
|
|
for hash := range u.users {
|
|
res = append(res, hash)
|
|
}
|
|
|
|
return res, nil
|
|
}
|
|
|
|
func (u *userFetcherMock) UserKey(hash util.Uint160) (*keys.PublicKey, error) {
|
|
key, ok := u.users[hash]
|
|
if !ok {
|
|
return nil, errors.New("userFetcherMock: hash not found")
|
|
}
|
|
|
|
return key.PublicKey(), nil
|
|
}
|
|
|
|
var _ ContainerFetcher = (*containerFetcherMock)(nil)
|
|
|
|
type containerFetcherMock struct {
|
|
containers map[util.Uint160][]cid.ID
|
|
}
|
|
|
|
func newContainerFetcherMock(containers map[util.Uint160][]cid.ID) *containerFetcherMock {
|
|
if containers == nil {
|
|
containers = map[util.Uint160][]cid.ID{}
|
|
}
|
|
return &containerFetcherMock{
|
|
containers: containers,
|
|
}
|
|
}
|
|
|
|
func (c *containerFetcherMock) Containers(owner user.ID) ([]cid.ID, error) {
|
|
hash, err := owner.ScriptHash()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
containers, ok := c.containers[hash]
|
|
if !ok {
|
|
return nil, errors.New("containerFetcherMock: hash not found")
|
|
}
|
|
|
|
return containers, nil
|
|
}
|
|
|
|
var _ FrostFSFetcher = (*frostfsFetcherMock)(nil)
|
|
|
|
type frostfsFetcherMock struct {
|
|
configurations map[oid.Address]*data.LifecycleConfiguration
|
|
}
|
|
|
|
func newFrostFSFetcherMock(configs map[oid.Address]*data.LifecycleConfiguration) *frostfsFetcherMock {
|
|
if configs == nil {
|
|
configs = map[oid.Address]*data.LifecycleConfiguration{}
|
|
}
|
|
return &frostfsFetcherMock{
|
|
configurations: configs,
|
|
}
|
|
}
|
|
|
|
func (c *frostfsFetcherMock) GetObject(ctx context.Context, addr oid.Address) (pool.ResGetObject, error) {
|
|
val, ok := c.configurations[addr]
|
|
if !ok {
|
|
return pool.ResGetObject{}, errors.New("configurationFetcherMock: hash not found")
|
|
}
|
|
|
|
raw, err := xml.Marshal(val)
|
|
if err != nil {
|
|
return pool.ResGetObject{}, err
|
|
}
|
|
|
|
return pool.ResGetObject{
|
|
Payload: &payloadReader{bytes.NewReader(raw)},
|
|
}, nil
|
|
}
|
|
|
|
type payloadReader struct {
|
|
io.Reader
|
|
}
|
|
|
|
func (p *payloadReader) Close() error { return nil }
|
|
|
|
func (c *frostfsFetcherMock) NetworkInfo(ctx context.Context) (*netmap.NetworkInfo, error) {
|
|
//TODO implement me
|
|
panic("implement me")
|
|
}
|
|
|
|
func (c *frostfsFetcherMock) DeleteObject(ctx context.Context, addr oid.Address) error {
|
|
//TODO implement me
|
|
panic("implement me")
|
|
}
|
|
|
|
var _ CredentialSource = (*credentialSourceMock)(nil)
|
|
|
|
type credentialSourceMock struct {
|
|
users map[util.Uint160]*keys.PrivateKey
|
|
}
|
|
|
|
func newCredentialSourceMock(users map[util.Uint160]*keys.PrivateKey) *credentialSourceMock {
|
|
if users == nil {
|
|
users = map[util.Uint160]*keys.PrivateKey{}
|
|
}
|
|
return &credentialSourceMock{
|
|
users: users,
|
|
}
|
|
}
|
|
|
|
func (c *credentialSourceMock) Credentials(_ context.Context, pk *keys.PublicKey) (*keys.PrivateKey, error) {
|
|
key, ok := c.users[pk.GetScriptHash()]
|
|
if !ok {
|
|
return nil, errors.New("credentialSourceMock: hash not found")
|
|
}
|
|
|
|
return key, nil
|
|
}
|
|
|
|
var _ TreeFetcher = (*treeFetcherMock)(nil)
|
|
|
|
type treeFetcherMock struct {
|
|
configurations map[cid.ID]oid.ID
|
|
}
|
|
|
|
func newTreeFetcherMock(configs map[cid.ID]oid.ID) *treeFetcherMock {
|
|
if configs == nil {
|
|
configs = map[cid.ID]oid.ID{}
|
|
}
|
|
return &treeFetcherMock{
|
|
configurations: configs,
|
|
}
|
|
}
|
|
|
|
func (t *treeFetcherMock) GetBucketLifecycleConfiguration(_ context.Context, bktInfo *data.BucketInfo) (oid.ID, error) {
|
|
val, ok := t.configurations[bktInfo.CID]
|
|
if !ok {
|
|
return oid.ID{}, errors.New("treeFetcherMock: hash not found")
|
|
}
|
|
|
|
return val, nil
|
|
}
|
|
|
|
func (t *treeFetcherMock) GetMultipartUploadsByPrefix(ctx context.Context, bktInfo *data.BucketInfo, prefix string) ([]*data.MultipartInfo, error) {
|
|
//TODO implement me
|
|
panic("implement me")
|
|
}
|
|
|
|
func (t *treeFetcherMock) DeleteMultipartUpload(ctx context.Context, bktInfo *data.BucketInfo, info *data.MultipartInfo) error {
|
|
//TODO implement me
|
|
panic("implement me")
|
|
}
|
|
|
|
func (t *treeFetcherMock) GetParts(ctx context.Context, bktInfo *data.BucketInfo, multipartNodeID uint64) ([]*data.PartInfo, error) {
|
|
//TODO implement me
|
|
panic("implement me")
|
|
}
|
|
|
|
var _ Settings = (*settingsMock)(nil)
|
|
|
|
type settingsMock struct{}
|
|
|
|
func (s *settingsMock) ServicesKeys() keys.PublicKeys {
|
|
return nil
|
|
}
|
|
|
|
func TestFetcherBase(t *testing.T) {
|
|
ctx := context.Background()
|
|
log := zaptest.NewLogger(t)
|
|
|
|
key, err := keys.NewPrivateKey()
|
|
require.NoError(t, err)
|
|
|
|
mocks, err := initMocks(2, 1)
|
|
require.NoError(t, err)
|
|
|
|
epochCh := make(chan uint64)
|
|
go func() {
|
|
epochCh <- 1
|
|
close(epochCh)
|
|
}()
|
|
|
|
cfg := Config{
|
|
UserFetcher: mocks.userFetcher,
|
|
ContainerFetcher: mocks.containerFetcher,
|
|
FrostFSFetcher: mocks.configurationFetcher,
|
|
CredentialSource: mocks.credentialSource,
|
|
TreeFetcher: mocks.treeFetcher,
|
|
Settings: &settingsMock{},
|
|
CurrentLifecycler: key,
|
|
Logger: log,
|
|
EpochChannel: epochCh,
|
|
}
|
|
|
|
f := NewJobProvider(ctx, cfg)
|
|
|
|
var res []Job
|
|
for job := range f.Jobs() {
|
|
res = append(res, job)
|
|
}
|
|
|
|
require.Len(t, res, 2)
|
|
}
|
|
|
|
func TestFetcherCancel(t *testing.T) {
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
defer cancel()
|
|
log := zaptest.NewLogger(t)
|
|
|
|
key, err := keys.NewPrivateKey()
|
|
require.NoError(t, err)
|
|
|
|
mocks, err := initMocks(1, 1)
|
|
require.NoError(t, err)
|
|
|
|
epochCh := make(chan uint64)
|
|
go func() {
|
|
epochCh <- 1
|
|
epochCh <- 2
|
|
close(epochCh)
|
|
}()
|
|
|
|
cfg := Config{
|
|
UserFetcher: mocks.userFetcher,
|
|
ContainerFetcher: mocks.containerFetcher,
|
|
FrostFSFetcher: mocks.configurationFetcher,
|
|
CredentialSource: mocks.credentialSource,
|
|
TreeFetcher: mocks.treeFetcher,
|
|
Settings: &settingsMock{},
|
|
CurrentLifecycler: key,
|
|
Logger: log,
|
|
EpochChannel: epochCh,
|
|
}
|
|
|
|
f := NewJobProvider(ctx, cfg)
|
|
|
|
var res []Job
|
|
for job := range f.Jobs() {
|
|
res = append(res, job)
|
|
}
|
|
|
|
require.Len(t, res, 1)
|
|
}
|
|
|
|
type fetchersMock struct {
|
|
userFetcher *userFetcherMock
|
|
containerFetcher *containerFetcherMock
|
|
configurationFetcher *frostfsFetcherMock
|
|
credentialSource *credentialSourceMock
|
|
treeFetcher *treeFetcherMock
|
|
}
|
|
|
|
func initMocks(users, containers int) (*fetchersMock, error) {
|
|
usersMap, err := generateUsersMap(users)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
cnrsMap := make(map[util.Uint160][]cid.ID)
|
|
treeMap := make(map[cid.ID]oid.ID)
|
|
configMap := make(map[oid.Address]*data.LifecycleConfiguration)
|
|
for hash := range usersMap {
|
|
for i := 0; i < containers; i++ {
|
|
addr := oidtest.Address()
|
|
cnrsMap[hash] = append(cnrsMap[hash], addr.Container())
|
|
treeMap[addr.Container()] = addr.Object()
|
|
configMap[addr] = &data.LifecycleConfiguration{Rules: []data.LifecycleRule{{ID: addr.EncodeToString()}}}
|
|
}
|
|
}
|
|
|
|
return &fetchersMock{
|
|
userFetcher: newUserFetcherMock(usersMap),
|
|
containerFetcher: newContainerFetcherMock(cnrsMap),
|
|
configurationFetcher: newFrostFSFetcherMock(configMap),
|
|
credentialSource: newCredentialSourceMock(usersMap),
|
|
treeFetcher: newTreeFetcherMock(treeMap),
|
|
}, nil
|
|
}
|
|
|
|
func generateKeys(n int) ([]*keys.PrivateKey, error) {
|
|
var err error
|
|
res := make([]*keys.PrivateKey, n)
|
|
|
|
for i := 0; i < n; i++ {
|
|
if res[i], err = keys.NewPrivateKey(); err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
return res, nil
|
|
}
|
|
|
|
func generateUsersMap(n int) (map[util.Uint160]*keys.PrivateKey, error) {
|
|
res := make(map[util.Uint160]*keys.PrivateKey, n)
|
|
|
|
userKeys, err := generateKeys(n)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
for _, key := range userKeys {
|
|
res[key.GetScriptHash()] = key
|
|
}
|
|
|
|
return res, nil
|
|
}
|