2018-08-20 23:29:54 +00:00
|
|
|
/*
|
2021-03-04 21:43:15 +00:00
|
|
|
Copyright the Velero contributors.
|
2018-08-20 23:29:54 +00:00
|
|
|
|
|
|
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
you may not use this file except in compliance with the License.
|
|
|
|
You may obtain a copy of the License at
|
|
|
|
|
|
|
|
http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
|
|
|
|
Unless required by applicable law or agreed to in writing, software
|
|
|
|
distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
See the License for the specific language governing permissions and
|
|
|
|
limitations under the License.
|
|
|
|
*/
|
|
|
|
|
|
|
|
package persistence
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
2018-10-17 21:10:42 +00:00
|
|
|
"compress/gzip"
|
|
|
|
"encoding/json"
|
2018-08-20 23:29:54 +00:00
|
|
|
"errors"
|
2021-03-04 21:43:15 +00:00
|
|
|
"fmt"
|
2018-08-20 23:29:54 +00:00
|
|
|
"io"
|
|
|
|
"io/ioutil"
|
|
|
|
"sort"
|
|
|
|
"strings"
|
|
|
|
"testing"
|
|
|
|
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
|
|
"github.com/stretchr/testify/require"
|
|
|
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
|
|
"k8s.io/apimachinery/pkg/runtime"
|
|
|
|
|
2021-03-04 21:43:15 +00:00
|
|
|
"github.com/vmware-tanzu/velero/internal/credentials"
|
2019-09-30 21:26:56 +00:00
|
|
|
velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1"
|
|
|
|
"github.com/vmware-tanzu/velero/pkg/builder"
|
|
|
|
"github.com/vmware-tanzu/velero/pkg/plugin/velero"
|
2019-10-22 22:31:27 +00:00
|
|
|
providermocks "github.com/vmware-tanzu/velero/pkg/plugin/velero/mocks"
|
2019-09-30 21:26:56 +00:00
|
|
|
velerotest "github.com/vmware-tanzu/velero/pkg/test"
|
|
|
|
"github.com/vmware-tanzu/velero/pkg/util/encode"
|
|
|
|
"github.com/vmware-tanzu/velero/pkg/volume"
|
2018-08-20 23:29:54 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
type objectBackupStoreTestHarness struct {
|
|
|
|
// embedded to reduce verbosity when calling methods
|
|
|
|
*objectBackupStore
|
|
|
|
|
2019-10-22 22:31:27 +00:00
|
|
|
objectStore *inMemoryObjectStore
|
2018-08-20 23:29:54 +00:00
|
|
|
bucket, prefix string
|
|
|
|
}
|
|
|
|
|
|
|
|
func newObjectBackupStoreTestHarness(bucket, prefix string) *objectBackupStoreTestHarness {
|
2019-10-22 22:31:27 +00:00
|
|
|
objectStore := newInMemoryObjectStore(bucket)
|
2018-08-20 23:29:54 +00:00
|
|
|
|
|
|
|
return &objectBackupStoreTestHarness{
|
|
|
|
objectBackupStore: &objectBackupStore{
|
|
|
|
objectStore: objectStore,
|
|
|
|
bucket: bucket,
|
2018-09-25 19:10:03 +00:00
|
|
|
layout: NewObjectStoreLayout(prefix),
|
2019-01-25 03:33:07 +00:00
|
|
|
logger: velerotest.NewLogger(),
|
2018-08-20 23:29:54 +00:00
|
|
|
},
|
|
|
|
objectStore: objectStore,
|
|
|
|
bucket: bucket,
|
|
|
|
prefix: prefix,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-09-18 15:56:45 +00:00
|
|
|
func TestIsValid(t *testing.T) {
|
|
|
|
tests := []struct {
|
|
|
|
name string
|
|
|
|
prefix string
|
2019-10-22 22:31:27 +00:00
|
|
|
storageData BucketData
|
2018-09-18 15:56:45 +00:00
|
|
|
expectErr bool
|
|
|
|
}{
|
|
|
|
{
|
|
|
|
name: "empty backup store with no prefix is valid",
|
|
|
|
expectErr: false,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "empty backup store with a prefix is valid",
|
|
|
|
prefix: "bar",
|
|
|
|
expectErr: false,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "backup store with no prefix and only unsupported directories is invalid",
|
|
|
|
storageData: map[string][]byte{
|
2019-01-25 03:33:07 +00:00
|
|
|
"backup-1/velero-backup.json": {},
|
|
|
|
"backup-2/velero-backup.json": {},
|
2018-09-18 15:56:45 +00:00
|
|
|
},
|
|
|
|
expectErr: true,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "backup store with a prefix and only unsupported directories is invalid",
|
|
|
|
prefix: "backups",
|
|
|
|
storageData: map[string][]byte{
|
2019-01-25 03:33:07 +00:00
|
|
|
"backups/backup-1/velero-backup.json": {},
|
|
|
|
"backups/backup-2/velero-backup.json": {},
|
2018-09-18 15:56:45 +00:00
|
|
|
},
|
|
|
|
expectErr: true,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "backup store with no prefix and both supported and unsupported directories is invalid",
|
|
|
|
storageData: map[string][]byte{
|
2019-01-25 03:33:07 +00:00
|
|
|
"backups/backup-1/velero-backup.json": {},
|
|
|
|
"backups/backup-2/velero-backup.json": {},
|
|
|
|
"restores/restore-1/foo": {},
|
|
|
|
"unsupported-dir/foo": {},
|
2018-09-18 15:56:45 +00:00
|
|
|
},
|
|
|
|
expectErr: true,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "backup store with a prefix and both supported and unsupported directories is invalid",
|
|
|
|
prefix: "cluster-1",
|
|
|
|
storageData: map[string][]byte{
|
2019-01-25 03:33:07 +00:00
|
|
|
"cluster-1/backups/backup-1/velero-backup.json": {},
|
|
|
|
"cluster-1/backups/backup-2/velero-backup.json": {},
|
|
|
|
"cluster-1/restores/restore-1/foo": {},
|
|
|
|
"cluster-1/unsupported-dir/foo": {},
|
2018-09-18 15:56:45 +00:00
|
|
|
},
|
|
|
|
expectErr: true,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "backup store with no prefix and only supported directories is valid",
|
|
|
|
storageData: map[string][]byte{
|
2019-01-25 03:33:07 +00:00
|
|
|
"backups/backup-1/velero-backup.json": {},
|
|
|
|
"backups/backup-2/velero-backup.json": {},
|
|
|
|
"restores/restore-1/foo": {},
|
2018-09-18 15:56:45 +00:00
|
|
|
},
|
|
|
|
expectErr: false,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "backup store with a prefix and only supported directories is valid",
|
|
|
|
prefix: "cluster-1",
|
|
|
|
storageData: map[string][]byte{
|
2019-01-25 03:33:07 +00:00
|
|
|
"cluster-1/backups/backup-1/velero-backup.json": {},
|
|
|
|
"cluster-1/backups/backup-2/velero-backup.json": {},
|
|
|
|
"cluster-1/restores/restore-1/foo": {},
|
2018-09-18 15:56:45 +00:00
|
|
|
},
|
|
|
|
expectErr: false,
|
|
|
|
},
|
2020-03-17 17:14:41 +00:00
|
|
|
{
|
|
|
|
name: "backup store with plugins directory is valid",
|
|
|
|
storageData: map[string][]byte{
|
|
|
|
"plugins/vsphere/foo": {},
|
|
|
|
},
|
|
|
|
expectErr: false,
|
|
|
|
},
|
2018-09-18 15:56:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
for _, tc := range tests {
|
|
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
|
|
harness := newObjectBackupStoreTestHarness("foo", tc.prefix)
|
|
|
|
|
|
|
|
for key, obj := range tc.storageData {
|
|
|
|
require.NoError(t, harness.objectStore.PutObject(harness.bucket, key, bytes.NewReader(obj)))
|
|
|
|
}
|
|
|
|
|
|
|
|
err := harness.IsValid()
|
|
|
|
if tc.expectErr {
|
|
|
|
assert.Error(t, err)
|
|
|
|
} else {
|
|
|
|
assert.NoError(t, err)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-08-20 23:29:54 +00:00
|
|
|
func TestListBackups(t *testing.T) {
|
|
|
|
tests := []struct {
|
|
|
|
name string
|
|
|
|
prefix string
|
2019-10-22 22:31:27 +00:00
|
|
|
storageData BucketData
|
2018-08-27 15:44:48 +00:00
|
|
|
expectedRes []string
|
2018-08-20 23:29:54 +00:00
|
|
|
expectedErr string
|
|
|
|
}{
|
|
|
|
{
|
|
|
|
name: "normal case",
|
|
|
|
storageData: map[string][]byte{
|
2019-07-31 15:27:12 +00:00
|
|
|
"backups/backup-1/velero-backup.json": encodeToBytes(builder.ForBackup("", "backup-1").Result()),
|
|
|
|
"backups/backup-2/velero-backup.json": encodeToBytes(builder.ForBackup("", "backup-2").Result()),
|
2018-08-20 23:29:54 +00:00
|
|
|
},
|
2018-08-27 15:44:48 +00:00
|
|
|
expectedRes: []string{"backup-1", "backup-2"},
|
2018-08-20 23:29:54 +00:00
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "normal case with backup store prefix",
|
2019-01-25 03:33:07 +00:00
|
|
|
prefix: "velero-backups/",
|
2018-08-20 23:29:54 +00:00
|
|
|
storageData: map[string][]byte{
|
2019-07-31 15:27:12 +00:00
|
|
|
"velero-backups/backups/backup-1/velero-backup.json": encodeToBytes(builder.ForBackup("", "backup-1").Result()),
|
|
|
|
"velero-backups/backups/backup-2/velero-backup.json": encodeToBytes(builder.ForBackup("", "backup-2").Result()),
|
2018-08-20 23:29:54 +00:00
|
|
|
},
|
2018-08-27 15:44:48 +00:00
|
|
|
expectedRes: []string{"backup-1", "backup-2"},
|
2018-08-20 23:29:54 +00:00
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, tc := range tests {
|
|
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
|
|
harness := newObjectBackupStoreTestHarness("foo", tc.prefix)
|
|
|
|
|
|
|
|
for key, obj := range tc.storageData {
|
|
|
|
require.NoError(t, harness.objectStore.PutObject(harness.bucket, key, bytes.NewReader(obj)))
|
|
|
|
}
|
|
|
|
|
|
|
|
res, err := harness.ListBackups()
|
|
|
|
|
2019-01-25 03:33:07 +00:00
|
|
|
velerotest.AssertErrorMatches(t, tc.expectedErr, err)
|
2018-08-20 23:29:54 +00:00
|
|
|
|
2018-08-27 15:44:48 +00:00
|
|
|
sort.Strings(tc.expectedRes)
|
|
|
|
sort.Strings(res)
|
2018-08-20 23:29:54 +00:00
|
|
|
|
|
|
|
assert.Equal(t, tc.expectedRes, res)
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestPutBackup(t *testing.T) {
|
|
|
|
tests := []struct {
|
2019-07-24 19:51:20 +00:00
|
|
|
name string
|
|
|
|
prefix string
|
|
|
|
metadata io.Reader
|
|
|
|
contents io.Reader
|
|
|
|
log io.Reader
|
|
|
|
podVolumeBackup io.Reader
|
|
|
|
snapshots io.Reader
|
2019-08-05 17:15:55 +00:00
|
|
|
resourceList io.Reader
|
2019-07-24 19:51:20 +00:00
|
|
|
expectedErr string
|
|
|
|
expectedKeys []string
|
2018-08-20 23:29:54 +00:00
|
|
|
}{
|
|
|
|
{
|
2019-07-24 19:51:20 +00:00
|
|
|
name: "normal case",
|
|
|
|
metadata: newStringReadSeeker("metadata"),
|
|
|
|
contents: newStringReadSeeker("contents"),
|
|
|
|
log: newStringReadSeeker("log"),
|
|
|
|
podVolumeBackup: newStringReadSeeker("podVolumeBackup"),
|
|
|
|
snapshots: newStringReadSeeker("snapshots"),
|
2019-08-05 17:15:55 +00:00
|
|
|
resourceList: newStringReadSeeker("resourceList"),
|
2019-07-24 19:51:20 +00:00
|
|
|
expectedErr: "",
|
2018-10-12 17:55:02 +00:00
|
|
|
expectedKeys: []string{
|
2019-01-25 03:33:07 +00:00
|
|
|
"backups/backup-1/velero-backup.json",
|
2018-10-12 17:55:02 +00:00
|
|
|
"backups/backup-1/backup-1.tar.gz",
|
|
|
|
"backups/backup-1/backup-1-logs.gz",
|
2019-07-24 19:51:20 +00:00
|
|
|
"backups/backup-1/backup-1-podvolumebackups.json.gz",
|
2018-10-12 17:55:02 +00:00
|
|
|
"backups/backup-1/backup-1-volumesnapshots.json.gz",
|
2019-08-05 17:15:55 +00:00
|
|
|
"backups/backup-1/backup-1-resource-list.json.gz",
|
2018-10-12 17:55:02 +00:00
|
|
|
},
|
2018-08-20 23:29:54 +00:00
|
|
|
},
|
|
|
|
{
|
2019-07-24 19:51:20 +00:00
|
|
|
name: "normal case with backup store prefix",
|
|
|
|
prefix: "prefix-1/",
|
|
|
|
metadata: newStringReadSeeker("metadata"),
|
|
|
|
contents: newStringReadSeeker("contents"),
|
|
|
|
log: newStringReadSeeker("log"),
|
|
|
|
podVolumeBackup: newStringReadSeeker("podVolumeBackup"),
|
|
|
|
snapshots: newStringReadSeeker("snapshots"),
|
2019-08-05 17:15:55 +00:00
|
|
|
resourceList: newStringReadSeeker("resourceList"),
|
2019-07-24 19:51:20 +00:00
|
|
|
expectedErr: "",
|
2018-10-12 17:55:02 +00:00
|
|
|
expectedKeys: []string{
|
2019-01-25 03:33:07 +00:00
|
|
|
"prefix-1/backups/backup-1/velero-backup.json",
|
2018-10-12 17:55:02 +00:00
|
|
|
"prefix-1/backups/backup-1/backup-1.tar.gz",
|
|
|
|
"prefix-1/backups/backup-1/backup-1-logs.gz",
|
2019-07-24 19:51:20 +00:00
|
|
|
"prefix-1/backups/backup-1/backup-1-podvolumebackups.json.gz",
|
2018-10-12 17:55:02 +00:00
|
|
|
"prefix-1/backups/backup-1/backup-1-volumesnapshots.json.gz",
|
2019-08-05 17:15:55 +00:00
|
|
|
"prefix-1/backups/backup-1/backup-1-resource-list.json.gz",
|
2018-10-12 17:55:02 +00:00
|
|
|
},
|
2018-08-20 23:29:54 +00:00
|
|
|
},
|
|
|
|
{
|
2019-07-24 19:51:20 +00:00
|
|
|
name: "error on metadata upload does not upload data",
|
|
|
|
metadata: new(errorReader),
|
|
|
|
contents: newStringReadSeeker("contents"),
|
|
|
|
log: newStringReadSeeker("log"),
|
|
|
|
podVolumeBackup: newStringReadSeeker("podVolumeBackup"),
|
|
|
|
snapshots: newStringReadSeeker("snapshots"),
|
2019-08-05 17:15:55 +00:00
|
|
|
resourceList: newStringReadSeeker("resourceList"),
|
2019-07-24 19:51:20 +00:00
|
|
|
expectedErr: "error readers return errors",
|
|
|
|
expectedKeys: []string{"backups/backup-1/backup-1-logs.gz"},
|
2018-08-20 23:29:54 +00:00
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "error on data upload deletes metadata",
|
|
|
|
metadata: newStringReadSeeker("metadata"),
|
|
|
|
contents: new(errorReader),
|
|
|
|
log: newStringReadSeeker("log"),
|
2018-10-12 17:55:02 +00:00
|
|
|
snapshots: newStringReadSeeker("snapshots"),
|
2019-08-05 17:15:55 +00:00
|
|
|
resourceList: newStringReadSeeker("resourceList"),
|
2018-08-20 23:29:54 +00:00
|
|
|
expectedErr: "error readers return errors",
|
2018-09-18 15:56:45 +00:00
|
|
|
expectedKeys: []string{"backups/backup-1/backup-1-logs.gz"},
|
2018-08-20 23:29:54 +00:00
|
|
|
},
|
|
|
|
{
|
2019-07-24 19:51:20 +00:00
|
|
|
name: "error on log upload is ok",
|
|
|
|
metadata: newStringReadSeeker("foo"),
|
|
|
|
contents: newStringReadSeeker("bar"),
|
|
|
|
log: new(errorReader),
|
|
|
|
podVolumeBackup: newStringReadSeeker("podVolumeBackup"),
|
|
|
|
snapshots: newStringReadSeeker("snapshots"),
|
2019-08-05 17:15:55 +00:00
|
|
|
resourceList: newStringReadSeeker("resourceList"),
|
2019-07-24 19:51:20 +00:00
|
|
|
expectedErr: "",
|
2018-10-12 17:55:02 +00:00
|
|
|
expectedKeys: []string{
|
2019-01-25 03:33:07 +00:00
|
|
|
"backups/backup-1/velero-backup.json",
|
2018-10-12 17:55:02 +00:00
|
|
|
"backups/backup-1/backup-1.tar.gz",
|
2019-07-24 19:51:20 +00:00
|
|
|
"backups/backup-1/backup-1-podvolumebackups.json.gz",
|
2018-10-12 17:55:02 +00:00
|
|
|
"backups/backup-1/backup-1-volumesnapshots.json.gz",
|
2019-08-05 17:15:55 +00:00
|
|
|
"backups/backup-1/backup-1-resource-list.json.gz",
|
2018-10-12 17:55:02 +00:00
|
|
|
},
|
2018-08-20 23:29:54 +00:00
|
|
|
},
|
|
|
|
{
|
2019-07-24 19:51:20 +00:00
|
|
|
name: "don't upload data when metadata is nil",
|
|
|
|
metadata: nil,
|
|
|
|
contents: newStringReadSeeker("contents"),
|
|
|
|
log: newStringReadSeeker("log"),
|
|
|
|
podVolumeBackup: newStringReadSeeker("podVolumeBackup"),
|
|
|
|
snapshots: newStringReadSeeker("snapshots"),
|
2019-08-05 17:15:55 +00:00
|
|
|
resourceList: newStringReadSeeker("resourceList"),
|
2019-07-24 19:51:20 +00:00
|
|
|
expectedErr: "",
|
|
|
|
expectedKeys: []string{"backups/backup-1/backup-1-logs.gz"},
|
2018-08-20 23:29:54 +00:00
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, tc := range tests {
|
2018-08-27 15:44:48 +00:00
|
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
|
|
harness := newObjectBackupStoreTestHarness("foo", tc.prefix)
|
2018-08-20 23:29:54 +00:00
|
|
|
|
2019-07-24 19:51:20 +00:00
|
|
|
backupInfo := BackupInfo{
|
2019-08-05 17:15:55 +00:00
|
|
|
Name: "backup-1",
|
|
|
|
Metadata: tc.metadata,
|
|
|
|
Contents: tc.contents,
|
|
|
|
Log: tc.log,
|
|
|
|
PodVolumeBackups: tc.podVolumeBackup,
|
|
|
|
VolumeSnapshots: tc.snapshots,
|
|
|
|
BackupResourceList: tc.resourceList,
|
2019-07-24 19:51:20 +00:00
|
|
|
}
|
|
|
|
err := harness.PutBackup(backupInfo)
|
2018-08-20 23:29:54 +00:00
|
|
|
|
2019-01-25 03:33:07 +00:00
|
|
|
velerotest.AssertErrorMatches(t, tc.expectedErr, err)
|
2018-08-27 15:44:48 +00:00
|
|
|
assert.Len(t, harness.objectStore.Data[harness.bucket], len(tc.expectedKeys))
|
|
|
|
for _, key := range tc.expectedKeys {
|
|
|
|
assert.Contains(t, harness.objectStore.Data[harness.bucket], key)
|
|
|
|
}
|
|
|
|
})
|
2018-08-20 23:29:54 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-01-25 03:33:07 +00:00
|
|
|
func TestGetBackupMetadata(t *testing.T) {
|
|
|
|
tests := []struct {
|
|
|
|
name string
|
|
|
|
backupName string
|
|
|
|
key string
|
|
|
|
obj metav1.Object
|
|
|
|
wantErr error
|
|
|
|
}{
|
|
|
|
{
|
2019-03-27 22:49:42 +00:00
|
|
|
name: "metadata file returns correctly",
|
2019-01-25 03:33:07 +00:00
|
|
|
backupName: "foo",
|
|
|
|
key: "backups/foo/velero-backup.json",
|
2019-07-31 15:27:12 +00:00
|
|
|
obj: builder.ForBackup(velerov1api.DefaultNamespace, "foo").Result(),
|
2019-01-25 03:33:07 +00:00
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "no metadata file returns an error",
|
|
|
|
backupName: "foo",
|
|
|
|
wantErr: errors.New("key not found"),
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, tc := range tests {
|
|
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
|
|
harness := newObjectBackupStoreTestHarness("test-bucket", "")
|
|
|
|
|
|
|
|
if tc.obj != nil {
|
|
|
|
jsonBytes, err := json.Marshal(tc.obj)
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
require.NoError(t, harness.objectStore.PutObject(harness.bucket, tc.key, bytes.NewReader(jsonBytes)))
|
|
|
|
}
|
|
|
|
|
|
|
|
res, err := harness.GetBackupMetadata(tc.backupName)
|
|
|
|
if tc.wantErr != nil {
|
|
|
|
assert.Equal(t, tc.wantErr, err)
|
|
|
|
} else {
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
assert.Equal(t, tc.obj.GetNamespace(), res.Namespace)
|
|
|
|
assert.Equal(t, tc.obj.GetName(), res.Name)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-10-17 21:10:42 +00:00
|
|
|
func TestGetBackupVolumeSnapshots(t *testing.T) {
|
|
|
|
harness := newObjectBackupStoreTestHarness("test-bucket", "")
|
|
|
|
|
|
|
|
// volumesnapshots file not found should not error
|
2019-01-25 03:33:07 +00:00
|
|
|
harness.objectStore.PutObject(harness.bucket, "backups/test-backup/velero-backup.json", newStringReadSeeker("foo"))
|
2018-10-17 21:10:42 +00:00
|
|
|
res, err := harness.GetBackupVolumeSnapshots("test-backup")
|
|
|
|
assert.NoError(t, err)
|
|
|
|
assert.Nil(t, res)
|
|
|
|
|
|
|
|
// volumesnapshots file containing invalid data should error
|
|
|
|
harness.objectStore.PutObject(harness.bucket, "backups/test-backup/test-backup-volumesnapshots.json.gz", newStringReadSeeker("foo"))
|
|
|
|
res, err = harness.GetBackupVolumeSnapshots("test-backup")
|
|
|
|
assert.NotNil(t, err)
|
|
|
|
|
|
|
|
// volumesnapshots file containing gzipped json data should return correctly
|
|
|
|
snapshots := []*volume.Snapshot{
|
|
|
|
{
|
|
|
|
Spec: volume.SnapshotSpec{
|
|
|
|
BackupName: "test-backup",
|
|
|
|
PersistentVolumeName: "pv-1",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
Spec: volume.SnapshotSpec{
|
|
|
|
BackupName: "test-backup",
|
|
|
|
PersistentVolumeName: "pv-2",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
obj := new(bytes.Buffer)
|
|
|
|
gzw := gzip.NewWriter(obj)
|
|
|
|
|
|
|
|
require.NoError(t, json.NewEncoder(gzw).Encode(snapshots))
|
|
|
|
require.NoError(t, gzw.Close())
|
|
|
|
require.NoError(t, harness.objectStore.PutObject(harness.bucket, "backups/test-backup/test-backup-volumesnapshots.json.gz", obj))
|
|
|
|
|
|
|
|
res, err = harness.GetBackupVolumeSnapshots("test-backup")
|
|
|
|
assert.NoError(t, err)
|
|
|
|
assert.EqualValues(t, snapshots, res)
|
|
|
|
}
|
|
|
|
|
2018-08-20 23:29:54 +00:00
|
|
|
func TestGetBackupContents(t *testing.T) {
|
|
|
|
harness := newObjectBackupStoreTestHarness("test-bucket", "")
|
|
|
|
|
2018-09-18 15:56:45 +00:00
|
|
|
harness.objectStore.PutObject(harness.bucket, "backups/test-backup/test-backup.tar.gz", newStringReadSeeker("foo"))
|
2018-08-20 23:29:54 +00:00
|
|
|
|
|
|
|
rc, err := harness.GetBackupContents("test-backup")
|
|
|
|
require.NoError(t, err)
|
|
|
|
require.NotNil(t, rc)
|
|
|
|
|
|
|
|
data, err := ioutil.ReadAll(rc)
|
|
|
|
require.NoError(t, err)
|
|
|
|
assert.Equal(t, "foo", string(data))
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestDeleteBackup(t *testing.T) {
|
|
|
|
tests := []struct {
|
|
|
|
name string
|
|
|
|
prefix string
|
|
|
|
listObjectsError error
|
|
|
|
deleteErrors []error
|
|
|
|
expectedErr string
|
|
|
|
}{
|
|
|
|
{
|
|
|
|
name: "normal case",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "normal case with backup store prefix",
|
2019-01-25 03:33:07 +00:00
|
|
|
prefix: "velero-backups/",
|
2018-08-20 23:29:54 +00:00
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "some delete errors, do as much as we can",
|
|
|
|
deleteErrors: []error{errors.New("a"), nil, errors.New("c")},
|
|
|
|
expectedErr: "[a, c]",
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, test := range tests {
|
|
|
|
t.Run(test.name, func(t *testing.T) {
|
2019-10-22 22:31:27 +00:00
|
|
|
objectStore := new(providermocks.ObjectStore)
|
2018-08-20 23:29:54 +00:00
|
|
|
backupStore := &objectBackupStore{
|
|
|
|
objectStore: objectStore,
|
|
|
|
bucket: "test-bucket",
|
2018-09-25 19:10:03 +00:00
|
|
|
layout: NewObjectStoreLayout(test.prefix),
|
2019-01-25 03:33:07 +00:00
|
|
|
logger: velerotest.NewLogger(),
|
2018-08-20 23:29:54 +00:00
|
|
|
}
|
|
|
|
defer objectStore.AssertExpectations(t)
|
|
|
|
|
2019-01-25 03:33:07 +00:00
|
|
|
objects := []string{test.prefix + "backups/bak/velero-backup.json", test.prefix + "backups/bak/bak.tar.gz", test.prefix + "backups/bak/bak.log.gz"}
|
2018-08-20 23:29:54 +00:00
|
|
|
|
2018-09-18 15:56:45 +00:00
|
|
|
objectStore.On("ListObjects", backupStore.bucket, test.prefix+"backups/bak/").Return(objects, test.listObjectsError)
|
2018-08-20 23:29:54 +00:00
|
|
|
for i, obj := range objects {
|
|
|
|
var err error
|
|
|
|
if i < len(test.deleteErrors) {
|
|
|
|
err = test.deleteErrors[i]
|
|
|
|
}
|
|
|
|
|
|
|
|
objectStore.On("DeleteObject", backupStore.bucket, obj).Return(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
err := backupStore.DeleteBackup("bak")
|
|
|
|
|
2019-01-25 03:33:07 +00:00
|
|
|
velerotest.AssertErrorMatches(t, test.expectedErr, err)
|
2018-08-20 23:29:54 +00:00
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestGetDownloadURL(t *testing.T) {
|
|
|
|
tests := []struct {
|
2019-08-05 18:23:20 +00:00
|
|
|
name string
|
|
|
|
targetName string
|
|
|
|
expectedKeyByKind map[velerov1api.DownloadTargetKind]string
|
|
|
|
prefix string
|
2018-08-20 23:29:54 +00:00
|
|
|
}{
|
|
|
|
{
|
2019-08-05 18:23:20 +00:00
|
|
|
name: "backup",
|
|
|
|
targetName: "my-backup",
|
|
|
|
expectedKeyByKind: map[velerov1api.DownloadTargetKind]string{
|
|
|
|
velerov1api.DownloadTargetKindBackupContents: "backups/my-backup/my-backup.tar.gz",
|
|
|
|
velerov1api.DownloadTargetKindBackupLog: "backups/my-backup/my-backup-logs.gz",
|
|
|
|
velerov1api.DownloadTargetKindBackupVolumeSnapshots: "backups/my-backup/my-backup-volumesnapshots.json.gz",
|
|
|
|
velerov1api.DownloadTargetKindBackupResourceList: "backups/my-backup/my-backup-resource-list.json.gz",
|
|
|
|
},
|
2018-08-20 23:29:54 +00:00
|
|
|
},
|
|
|
|
{
|
2019-08-05 18:23:20 +00:00
|
|
|
name: "backup with prefix",
|
|
|
|
targetName: "my-backup",
|
|
|
|
prefix: "velero-backups/",
|
|
|
|
expectedKeyByKind: map[velerov1api.DownloadTargetKind]string{
|
|
|
|
velerov1api.DownloadTargetKindBackupContents: "velero-backups/backups/my-backup/my-backup.tar.gz",
|
|
|
|
velerov1api.DownloadTargetKindBackupLog: "velero-backups/backups/my-backup/my-backup-logs.gz",
|
|
|
|
velerov1api.DownloadTargetKindBackupVolumeSnapshots: "velero-backups/backups/my-backup/my-backup-volumesnapshots.json.gz",
|
|
|
|
velerov1api.DownloadTargetKindBackupResourceList: "velero-backups/backups/my-backup/my-backup-resource-list.json.gz",
|
|
|
|
},
|
2018-08-20 23:29:54 +00:00
|
|
|
},
|
|
|
|
{
|
2019-08-05 18:23:20 +00:00
|
|
|
name: "backup with multiple dashes",
|
|
|
|
targetName: "b-cool-20170913154901-20170913154902",
|
|
|
|
expectedKeyByKind: map[velerov1api.DownloadTargetKind]string{
|
|
|
|
velerov1api.DownloadTargetKindBackupContents: "backups/b-cool-20170913154901-20170913154902/b-cool-20170913154901-20170913154902.tar.gz",
|
|
|
|
velerov1api.DownloadTargetKindBackupLog: "backups/b-cool-20170913154901-20170913154902/b-cool-20170913154901-20170913154902-logs.gz",
|
|
|
|
velerov1api.DownloadTargetKindBackupVolumeSnapshots: "backups/b-cool-20170913154901-20170913154902/b-cool-20170913154901-20170913154902-volumesnapshots.json.gz",
|
|
|
|
velerov1api.DownloadTargetKindBackupResourceList: "backups/b-cool-20170913154901-20170913154902/b-cool-20170913154901-20170913154902-resource-list.json.gz",
|
|
|
|
},
|
2018-08-20 23:29:54 +00:00
|
|
|
},
|
|
|
|
{
|
2019-08-05 18:23:20 +00:00
|
|
|
name: "scheduled backup",
|
|
|
|
targetName: "my-backup-20170913154901",
|
|
|
|
expectedKeyByKind: map[velerov1api.DownloadTargetKind]string{
|
|
|
|
velerov1api.DownloadTargetKindBackupContents: "backups/my-backup-20170913154901/my-backup-20170913154901.tar.gz",
|
|
|
|
velerov1api.DownloadTargetKindBackupLog: "backups/my-backup-20170913154901/my-backup-20170913154901-logs.gz",
|
|
|
|
velerov1api.DownloadTargetKindBackupVolumeSnapshots: "backups/my-backup-20170913154901/my-backup-20170913154901-volumesnapshots.json.gz",
|
|
|
|
velerov1api.DownloadTargetKindBackupResourceList: "backups/my-backup-20170913154901/my-backup-20170913154901-resource-list.json.gz",
|
|
|
|
},
|
2018-08-20 23:29:54 +00:00
|
|
|
},
|
|
|
|
{
|
2019-08-05 18:23:20 +00:00
|
|
|
name: "scheduled backup with prefix",
|
|
|
|
targetName: "my-backup-20170913154901",
|
|
|
|
prefix: "velero-backups/",
|
|
|
|
expectedKeyByKind: map[velerov1api.DownloadTargetKind]string{
|
|
|
|
velerov1api.DownloadTargetKindBackupContents: "velero-backups/backups/my-backup-20170913154901/my-backup-20170913154901.tar.gz",
|
|
|
|
velerov1api.DownloadTargetKindBackupLog: "velero-backups/backups/my-backup-20170913154901/my-backup-20170913154901-logs.gz",
|
|
|
|
velerov1api.DownloadTargetKindBackupVolumeSnapshots: "velero-backups/backups/my-backup-20170913154901/my-backup-20170913154901-volumesnapshots.json.gz",
|
|
|
|
velerov1api.DownloadTargetKindBackupResourceList: "velero-backups/backups/my-backup-20170913154901/my-backup-20170913154901-resource-list.json.gz",
|
|
|
|
},
|
2018-08-20 23:29:54 +00:00
|
|
|
},
|
|
|
|
{
|
2019-08-05 18:23:20 +00:00
|
|
|
name: "restore",
|
|
|
|
targetName: "my-backup",
|
|
|
|
expectedKeyByKind: map[velerov1api.DownloadTargetKind]string{
|
|
|
|
velerov1api.DownloadTargetKindRestoreLog: "restores/my-backup/restore-my-backup-logs.gz",
|
|
|
|
velerov1api.DownloadTargetKindRestoreResults: "restores/my-backup/restore-my-backup-results.gz",
|
|
|
|
},
|
2018-08-20 23:29:54 +00:00
|
|
|
},
|
|
|
|
{
|
2019-08-05 18:23:20 +00:00
|
|
|
name: "restore with prefix",
|
|
|
|
targetName: "my-backup",
|
|
|
|
prefix: "velero-backups/",
|
|
|
|
expectedKeyByKind: map[velerov1api.DownloadTargetKind]string{
|
|
|
|
velerov1api.DownloadTargetKindRestoreLog: "velero-backups/restores/my-backup/restore-my-backup-logs.gz",
|
|
|
|
velerov1api.DownloadTargetKindRestoreResults: "velero-backups/restores/my-backup/restore-my-backup-results.gz",
|
|
|
|
},
|
2018-08-20 23:29:54 +00:00
|
|
|
},
|
|
|
|
{
|
2019-08-05 18:23:20 +00:00
|
|
|
name: "restore with multiple dashes",
|
|
|
|
targetName: "b-cool-20170913154901-20170913154902",
|
|
|
|
expectedKeyByKind: map[velerov1api.DownloadTargetKind]string{
|
|
|
|
velerov1api.DownloadTargetKindRestoreLog: "restores/b-cool-20170913154901-20170913154902/restore-b-cool-20170913154901-20170913154902-logs.gz",
|
|
|
|
velerov1api.DownloadTargetKindRestoreResults: "restores/b-cool-20170913154901-20170913154902/restore-b-cool-20170913154901-20170913154902-results.gz",
|
|
|
|
},
|
2018-08-20 23:29:54 +00:00
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, test := range tests {
|
|
|
|
t.Run(test.name, func(t *testing.T) {
|
|
|
|
harness := newObjectBackupStoreTestHarness("test-bucket", test.prefix)
|
|
|
|
|
2019-08-05 18:23:20 +00:00
|
|
|
for kind, expectedKey := range test.expectedKeyByKind {
|
|
|
|
t.Run(string(kind), func(t *testing.T) {
|
|
|
|
require.NoError(t, harness.objectStore.PutObject("test-bucket", expectedKey, newStringReadSeeker("foo")))
|
2018-08-20 23:29:54 +00:00
|
|
|
|
2019-08-05 18:23:20 +00:00
|
|
|
url, err := harness.GetDownloadURL(velerov1api.DownloadTarget{Kind: kind, Name: test.targetName})
|
|
|
|
require.NoError(t, err)
|
|
|
|
assert.Equal(t, "a-url", url)
|
|
|
|
})
|
|
|
|
}
|
2018-08-20 23:29:54 +00:00
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-07-31 15:27:12 +00:00
|
|
|
type objectStoreGetter map[string]velero.ObjectStore
|
|
|
|
|
|
|
|
func (osg objectStoreGetter) GetObjectStore(provider string) (velero.ObjectStore, error) {
|
|
|
|
res, ok := osg[provider]
|
|
|
|
if !ok {
|
|
|
|
return nil, errors.New("object store not found")
|
|
|
|
}
|
|
|
|
|
|
|
|
return res, nil
|
|
|
|
}
|
|
|
|
|
2021-02-08 18:04:08 +00:00
|
|
|
// TestNewObjectBackupStore runs the NewObjectBackupStoreGetter constructor and ensures
|
|
|
|
// that it provides a BackupStore with a correctly constructed ObjectBackupStore or
|
|
|
|
// that an appropriate error is returned.
|
|
|
|
func TestNewObjectBackupStoreGetter(t *testing.T) {
|
2019-07-31 15:27:12 +00:00
|
|
|
tests := []struct {
|
|
|
|
name string
|
|
|
|
location *velerov1api.BackupStorageLocation
|
|
|
|
objectStoreGetter objectStoreGetter
|
2021-03-04 21:43:15 +00:00
|
|
|
credFileStore credentials.FileStore
|
|
|
|
fileStoreErr error
|
2019-07-31 15:27:12 +00:00
|
|
|
wantBucket string
|
|
|
|
wantPrefix string
|
|
|
|
wantErr string
|
|
|
|
}{
|
|
|
|
{
|
2021-03-04 21:43:15 +00:00
|
|
|
name: "when location does not use object storage, a backup store can't be retrieved",
|
|
|
|
location: new(velerov1api.BackupStorageLocation),
|
|
|
|
credFileStore: velerotest.NewFakeCredentialsFileStore("", nil),
|
|
|
|
wantErr: "backup storage location does not use object storage",
|
2019-07-31 15:27:12 +00:00
|
|
|
},
|
|
|
|
{
|
2021-03-04 21:43:15 +00:00
|
|
|
name: "when object storage does not specify a provider, a backup store can't be retrieved",
|
|
|
|
location: builder.ForBackupStorageLocation("", "").Bucket("").Result(),
|
|
|
|
credFileStore: velerotest.NewFakeCredentialsFileStore("", nil),
|
|
|
|
wantErr: "object storage provider name must not be empty",
|
2019-07-31 15:27:12 +00:00
|
|
|
},
|
|
|
|
{
|
2021-03-04 21:43:15 +00:00
|
|
|
name: "when the Bucket field has a '/' in the middle, a backup store can't be retrieved",
|
|
|
|
location: builder.ForBackupStorageLocation("", "").Provider("provider-1").Bucket("invalid/bucket").Result(),
|
|
|
|
credFileStore: velerotest.NewFakeCredentialsFileStore("", nil),
|
|
|
|
wantErr: "backup storage location's bucket name \"invalid/bucket\" must not contain a '/' (if using a prefix, put it in the 'Prefix' field instead)",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "when the credential selector is invalid, a backup store can't be retrieved",
|
|
|
|
location: builder.ForBackupStorageLocation("", "").Provider("provider-1").Bucket("bucket").Credential(
|
|
|
|
builder.ForSecretKeySelector("does-not-exist", "does-not-exist").Result(),
|
|
|
|
).Result(),
|
|
|
|
credFileStore: velerotest.NewFakeCredentialsFileStore("", fmt.Errorf("secret does not exist")),
|
|
|
|
wantErr: "unable to get credentials: secret does not exist",
|
2019-07-31 15:27:12 +00:00
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "when Bucket has a leading and trailing slash, they are both stripped",
|
|
|
|
location: builder.ForBackupStorageLocation("", "").Provider("provider-1").Bucket("/bucket/").Result(),
|
|
|
|
objectStoreGetter: objectStoreGetter{
|
2019-10-22 22:31:27 +00:00
|
|
|
"provider-1": newInMemoryObjectStore("bucket"),
|
2019-07-31 15:27:12 +00:00
|
|
|
},
|
2021-03-04 21:43:15 +00:00
|
|
|
credFileStore: velerotest.NewFakeCredentialsFileStore("", nil),
|
|
|
|
wantBucket: "bucket",
|
2019-07-31 15:27:12 +00:00
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "when Prefix has a leading and trailing slash, the leading slash is stripped and the trailing slash is left",
|
|
|
|
location: builder.ForBackupStorageLocation("", "").Provider("provider-1").Bucket("bucket").Prefix("/prefix/").Result(),
|
|
|
|
objectStoreGetter: objectStoreGetter{
|
2019-10-22 22:31:27 +00:00
|
|
|
"provider-1": newInMemoryObjectStore("bucket"),
|
2019-07-31 15:27:12 +00:00
|
|
|
},
|
2021-03-04 21:43:15 +00:00
|
|
|
credFileStore: velerotest.NewFakeCredentialsFileStore("", nil),
|
|
|
|
wantBucket: "bucket",
|
|
|
|
wantPrefix: "prefix/",
|
2019-07-31 15:27:12 +00:00
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "when Prefix has no leading or trailing slash, a trailing slash is added",
|
|
|
|
location: builder.ForBackupStorageLocation("", "").Provider("provider-1").Bucket("bucket").Prefix("prefix").Result(),
|
|
|
|
objectStoreGetter: objectStoreGetter{
|
2019-10-22 22:31:27 +00:00
|
|
|
"provider-1": newInMemoryObjectStore("bucket"),
|
2019-07-31 15:27:12 +00:00
|
|
|
},
|
2021-03-04 21:43:15 +00:00
|
|
|
credFileStore: velerotest.NewFakeCredentialsFileStore("", nil),
|
|
|
|
wantBucket: "bucket",
|
|
|
|
wantPrefix: "prefix/",
|
2019-07-31 15:27:12 +00:00
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, tc := range tests {
|
|
|
|
t.Run(tc.name, func(t *testing.T) {
|
2021-03-04 21:43:15 +00:00
|
|
|
getter := NewObjectBackupStoreGetter(tc.credFileStore)
|
2021-02-08 18:04:08 +00:00
|
|
|
res, err := getter.Get(tc.location, tc.objectStoreGetter, velerotest.NewLogger())
|
2019-07-31 15:27:12 +00:00
|
|
|
if tc.wantErr != "" {
|
2021-03-04 21:43:15 +00:00
|
|
|
require.EqualError(t, err, tc.wantErr)
|
2019-07-31 15:27:12 +00:00
|
|
|
} else {
|
|
|
|
require.Nil(t, err)
|
|
|
|
|
|
|
|
store, ok := res.(*objectBackupStore)
|
|
|
|
require.True(t, ok)
|
|
|
|
|
|
|
|
assert.Equal(t, tc.wantBucket, store.bucket)
|
|
|
|
assert.Equal(t, tc.wantPrefix, store.layout.rootPrefix)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-03-04 21:43:15 +00:00
|
|
|
// TestNewObjectBackupStoreGetterConfig runs the NewObjectBackupStoreGetter constructor and ensures
|
|
|
|
// that it initializes the ObjectBackupStore with the correct config.
|
|
|
|
func TestNewObjectBackupStoreGetterConfig(t *testing.T) {
|
|
|
|
provider := "provider"
|
|
|
|
bucket := "bucket"
|
|
|
|
|
|
|
|
tests := []struct {
|
|
|
|
name string
|
|
|
|
location *velerov1api.BackupStorageLocation
|
|
|
|
getter ObjectBackupStoreGetter
|
|
|
|
credentialPath string
|
|
|
|
wantConfig map[string]string
|
|
|
|
}{
|
|
|
|
{
|
|
|
|
name: "location with bucket but no prefix has config initialized with bucket and empty prefix",
|
|
|
|
location: builder.ForBackupStorageLocation("", "").Provider(provider).Bucket(bucket).Result(),
|
|
|
|
getter: NewObjectBackupStoreGetter(velerotest.NewFakeCredentialsFileStore("", nil)),
|
|
|
|
wantConfig: map[string]string{
|
|
|
|
"bucket": "bucket",
|
|
|
|
"prefix": "",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "location with bucket and prefix has config initialized with bucket and prefix",
|
|
|
|
location: builder.ForBackupStorageLocation("", "").Provider(provider).Bucket(bucket).Prefix("prefix").Result(),
|
|
|
|
getter: NewObjectBackupStoreGetter(velerotest.NewFakeCredentialsFileStore("", nil)),
|
|
|
|
wantConfig: map[string]string{
|
|
|
|
"bucket": "bucket",
|
|
|
|
"prefix": "prefix",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "location with CACert is initialized with caCert",
|
|
|
|
location: builder.ForBackupStorageLocation("", "").Provider(provider).Bucket(bucket).CACert([]byte("cacert-data")).Result(),
|
|
|
|
getter: NewObjectBackupStoreGetter(velerotest.NewFakeCredentialsFileStore("", nil)),
|
|
|
|
wantConfig: map[string]string{
|
|
|
|
"bucket": "bucket",
|
|
|
|
"prefix": "",
|
|
|
|
"caCert": "cacert-data",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "location with Credential is initialized with path of serialized secret",
|
|
|
|
location: builder.ForBackupStorageLocation("", "").Provider(provider).Bucket(bucket).Credential(
|
|
|
|
builder.ForSecretKeySelector("does-not-exist", "does-not-exist").Result(),
|
|
|
|
).Result(),
|
|
|
|
getter: NewObjectBackupStoreGetter(velerotest.NewFakeCredentialsFileStore("/tmp/credentials/secret-file", nil)),
|
|
|
|
wantConfig: map[string]string{
|
|
|
|
"bucket": "bucket",
|
|
|
|
"prefix": "",
|
|
|
|
"credentialsFile": "/tmp/credentials/secret-file",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, tc := range tests {
|
|
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
|
|
objStore := newInMemoryObjectStore(bucket)
|
|
|
|
objStoreGetter := &objectStoreGetter{provider: objStore}
|
|
|
|
|
|
|
|
_, err := tc.getter.Get(tc.location, objStoreGetter, velerotest.NewLogger())
|
|
|
|
require.NoError(t, err)
|
|
|
|
require.Equal(t, tc.wantConfig, objStore.Config)
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-08-20 23:29:54 +00:00
|
|
|
func encodeToBytes(obj runtime.Object) []byte {
|
|
|
|
res, err := encode.Encode(obj, "json")
|
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
return res
|
|
|
|
}
|
|
|
|
|
|
|
|
type stringReadSeeker struct {
|
|
|
|
*strings.Reader
|
|
|
|
}
|
|
|
|
|
|
|
|
func newStringReadSeeker(s string) *stringReadSeeker {
|
|
|
|
return &stringReadSeeker{
|
|
|
|
Reader: strings.NewReader(s),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (srs *stringReadSeeker) Seek(offset int64, whence int) (int64, error) {
|
|
|
|
return 0, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
type errorReader struct{}
|
|
|
|
|
|
|
|
func (r *errorReader) Read([]byte) (int, error) {
|
|
|
|
return 0, errors.New("error readers return errors")
|
|
|
|
}
|