repo credentials (#5167)

Signed-off-by: Lyndon-Li <lyonghui@vmware.com>
This commit is contained in:
lyndon
2022-08-04 15:20:02 +08:00
committed by GitHub
parent 701256d296
commit 088eb9b83c
13 changed files with 503 additions and 51 deletions

View File

@@ -0,0 +1 @@
Add changes for Kopia Integration: Unified Repository Provider - Repo Password

View File

@@ -0,0 +1,24 @@
/*
Copyright the Velero contributors.
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 credentials
// CredentialGetter is a collection of interfaces for interacting with credentials
// that are stored in different targets
type CredentialGetter struct {
FromFile FileStore
FromSecret SecretStore
}

View File

@@ -0,0 +1,49 @@
// Code generated by mockery v2.14.0. DO NOT EDIT.
package mocks
import (
mock "github.com/stretchr/testify/mock"
v1 "k8s.io/api/core/v1"
)
// FileStore is an autogenerated mock type for the FileStore type
type FileStore struct {
mock.Mock
}
// Path provides a mock function with given fields: selector
func (_m *FileStore) Path(selector *v1.SecretKeySelector) (string, error) {
ret := _m.Called(selector)
var r0 string
if rf, ok := ret.Get(0).(func(*v1.SecretKeySelector) string); ok {
r0 = rf(selector)
} else {
r0 = ret.Get(0).(string)
}
var r1 error
if rf, ok := ret.Get(1).(func(*v1.SecretKeySelector) error); ok {
r1 = rf(selector)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
type mockConstructorTestingTNewFileStore interface {
mock.TestingT
Cleanup(func())
}
// NewFileStore creates a new instance of FileStore. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
func NewFileStore(t mockConstructorTestingTNewFileStore) *FileStore {
mock := &FileStore{}
mock.Mock.Test(t)
t.Cleanup(func() { mock.AssertExpectations(t) })
return mock
}

View File

@@ -0,0 +1,49 @@
// Code generated by mockery v2.14.0. DO NOT EDIT.
package mocks
import (
mock "github.com/stretchr/testify/mock"
v1 "k8s.io/api/core/v1"
)
// SecretStore is an autogenerated mock type for the SecretStore type
type SecretStore struct {
mock.Mock
}
// Get provides a mock function with given fields: selector
func (_m *SecretStore) Get(selector *v1.SecretKeySelector) (string, error) {
ret := _m.Called(selector)
var r0 string
if rf, ok := ret.Get(0).(func(*v1.SecretKeySelector) string); ok {
r0 = rf(selector)
} else {
r0 = ret.Get(0).(string)
}
var r1 error
if rf, ok := ret.Get(1).(func(*v1.SecretKeySelector) error); ok {
r1 = rf(selector)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
type mockConstructorTestingTNewSecretStore interface {
mock.TestingT
Cleanup(func())
}
// NewSecretStore creates a new instance of SecretStore. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
func NewSecretStore(t mockConstructorTestingTNewSecretStore) *SecretStore {
mock := &SecretStore{}
mock.Mock.Test(t)
t.Cleanup(func() { mock.AssertExpectations(t) })
return mock
}

View File

@@ -0,0 +1,56 @@
/*
Copyright the Velero contributors.
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 credentials
import (
"github.com/pkg/errors"
corev1api "k8s.io/api/core/v1"
kbclient "sigs.k8s.io/controller-runtime/pkg/client"
"github.com/vmware-tanzu/velero/pkg/util/kube"
)
// SecretStore defines operations for interacting with credentials
// that are stored in Secret.
type SecretStore interface {
// Get returns the secret key defined by the given selector
Get(selector *corev1api.SecretKeySelector) (string, error)
}
type namespacedSecretStore struct {
client kbclient.Client
namespace string
}
// NewNamespacedSecretStore returns a SecretStore which can interact with credentials
// for the given namespace.
func NewNamespacedSecretStore(client kbclient.Client, namespace string) (SecretStore, error) {
return &namespacedSecretStore{
client: client,
namespace: namespace,
}, nil
}
// Buffer returns the secret key defined by the given selector.
func (n *namespacedSecretStore) Get(selector *corev1api.SecretKeySelector) (string, error) {
creds, err := kube.GetSecretKey(n.client, n.namespace, selector)
if err != nil {
return "", errors.Wrap(err, "unable to get key for secret")
}
return string(creds), nil
}

View File

@@ -80,6 +80,7 @@ import (
"github.com/vmware-tanzu/velero/internal/storage"
"github.com/vmware-tanzu/velero/internal/util/managercontroller"
velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1"
repokey "github.com/vmware-tanzu/velero/pkg/repository/keys"
)
const (
@@ -519,7 +520,7 @@ func (s *server) initRestic() error {
}
// ensure the repo key secret is set up
if err := restic.EnsureCommonRepositoryKey(s.kubeClient.CoreV1(), s.namespace); err != nil {
if err := repokey.EnsureCommonRepositoryKey(s.kubeClient.CoreV1(), s.namespace); err != nil {
return err
}

View File

@@ -36,6 +36,7 @@ import (
"github.com/vmware-tanzu/velero/internal/credentials"
velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1"
"github.com/vmware-tanzu/velero/pkg/metrics"
repokey "github.com/vmware-tanzu/velero/pkg/repository/keys"
"github.com/vmware-tanzu/velero/pkg/restic"
"github.com/vmware-tanzu/velero/pkg/util/filesystem"
"github.com/vmware-tanzu/velero/pkg/util/kube"
@@ -324,7 +325,7 @@ func (r *PodVolumeBackupReconciler) buildResticCommand(ctx context.Context, log
log.WithField("path", path).Debugf("Found path matching glob")
// Temporary credentials.
details.credsFile, err = r.CredsFileStore.Path(restic.RepoKeySelector())
details.credsFile, err = r.CredsFileStore.Path(repokey.RepoKeySelector())
if err != nil {
return nil, errors.Wrap(err, "creating temporary Restic credentials file")
}

View File

@@ -39,6 +39,7 @@ import (
"github.com/vmware-tanzu/velero/internal/credentials"
velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1"
repokey "github.com/vmware-tanzu/velero/pkg/repository/keys"
"github.com/vmware-tanzu/velero/pkg/restic"
"github.com/vmware-tanzu/velero/pkg/util/boolptr"
"github.com/vmware-tanzu/velero/pkg/util/filesystem"
@@ -241,7 +242,7 @@ func (c *PodVolumeRestoreReconciler) processRestore(ctx context.Context, req *ve
return errors.Wrap(err, "error identifying path of volume")
}
credsFile, err := c.credentialsFileStore.Path(restic.RepoKeySelector())
credsFile, err := c.credentialsFileStore.Path(repokey.RepoKeySelector())
if err != nil {
return errors.Wrap(err, "error creating temp restic credentials file")
}

View File

@@ -0,0 +1,75 @@
/*
Copyright the Velero contributors.
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 keys
import (
"context"
"github.com/pkg/errors"
corev1api "k8s.io/api/core/v1"
apierrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
corev1client "k8s.io/client-go/kubernetes/typed/core/v1"
"github.com/vmware-tanzu/velero/pkg/builder"
)
const (
credentialsSecretName = "velero-restic-credentials"
credentialsKey = "repository-password"
encryptionKey = "static-passw0rd"
)
func EnsureCommonRepositoryKey(secretClient corev1client.SecretsGetter, namespace string) error {
_, err := secretClient.Secrets(namespace).Get(context.TODO(), credentialsSecretName, metav1.GetOptions{})
if err != nil && !apierrors.IsNotFound(err) {
return errors.WithStack(err)
}
if err == nil {
return nil
}
// if we got here, we got an IsNotFound error, so we need to create the key
secret := &corev1api.Secret{
ObjectMeta: metav1.ObjectMeta{
Namespace: namespace,
Name: credentialsSecretName,
},
Type: corev1api.SecretTypeOpaque,
Data: map[string][]byte{
credentialsKey: []byte(encryptionKey),
},
}
if _, err = secretClient.Secrets(namespace).Create(context.TODO(), secret, metav1.CreateOptions{}); err != nil {
return errors.Wrapf(err, "error creating %s secret", credentialsSecretName)
}
return nil
}
// RepoKeySelector returns the SecretKeySelector which can be used to fetch
// the restic repository key.
func RepoKeySelector() *corev1api.SecretKeySelector {
// For now, all restic repos share the same key so we don't need the repoName to fetch it.
// When we move to full-backup encryption, we'll likely have a separate key per restic repo
// (all within the Velero server's namespace) so RepoKeySelector will need to select the key
// for that repo.
return builder.ForSecretKeySelector(credentialsSecretName, credentialsKey).Result()
}

View File

@@ -0,0 +1,30 @@
/*
Copyright the Velero contributors.
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 keys
import (
"testing"
"github.com/stretchr/testify/require"
)
func TestRepoKeySelector(t *testing.T) {
selector := RepoKeySelector()
require.Equal(t, credentialsSecretName, selector.Name)
require.Equal(t, credentialsKey, selector.Key)
}

View File

@@ -28,15 +28,16 @@ import (
"github.com/vmware-tanzu/velero/internal/credentials"
velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1"
repoconfig "github.com/vmware-tanzu/velero/pkg/repository/config"
repokey "github.com/vmware-tanzu/velero/pkg/repository/keys"
"github.com/vmware-tanzu/velero/pkg/repository/udmrepo"
"github.com/vmware-tanzu/velero/pkg/util/ownership"
)
type unifiedRepoProvider struct {
credentialsFileStore credentials.FileStore
workPath string
repoService udmrepo.BackupRepoService
log logrus.FieldLogger
credentialGetter credentials.CredentialGetter
workPath string
repoService udmrepo.BackupRepoService
log logrus.FieldLogger
}
// this func is assigned to a package-level variable so it can be
@@ -47,18 +48,30 @@ var getGCPCredentials = repoconfig.GetGCPCredentials
var getS3BucketRegion = repoconfig.GetAWSBucketRegion
var getAzureStorageDomain = repoconfig.GetAzureStorageDomain
type localFuncTable struct {
getRepoPassword func(credentials.SecretStore, RepoParam) (string, error)
getStorageVariables func(*velerov1api.BackupStorageLocation, string) (map[string]string, error)
getStorageCredentials func(*velerov1api.BackupStorageLocation, credentials.FileStore) (map[string]string, error)
}
var funcTable = localFuncTable{
getRepoPassword: getRepoPassword,
getStorageVariables: getStorageVariables,
getStorageCredentials: getStorageCredentials,
}
// NewUnifiedRepoProvider creates the service provider for Unified Repo
// workPath is the path for Unified Repo to store some local information
// workPath could be empty, if so, the default path will be used
func NewUnifiedRepoProvider(
credentialFileStore credentials.FileStore,
credentialGetter credentials.CredentialGetter,
workPath string,
log logrus.FieldLogger,
) (Provider, error) {
repo := unifiedRepoProvider{
credentialsFileStore: credentialFileStore,
workPath: workPath,
log: log,
credentialGetter: credentialGetter,
workPath: workPath,
log: log,
}
repo.repoService = createRepoService(log)
@@ -120,15 +133,38 @@ func (urp *unifiedRepoProvider) Forget(ctx context.Context, snapshotID string, p
return nil
}
func (urp *unifiedRepoProvider) getRepoPassword(param RepoParam) (string, error) {
///TODO: get repo password
func getRepoPassword(secretStore credentials.SecretStore, param RepoParam) (string, error) {
if secretStore == nil {
return "", errors.New("invalid credentials interface")
}
return "", nil
buf, err := secretStore.Get(repokey.RepoKeySelector())
if err != nil {
return "", errors.Wrap(err, "error to get password buffer")
}
return strings.TrimSpace(string(buf)), nil
}
func (urp *unifiedRepoProvider) getRepoOption(param RepoParam) (udmrepo.RepoOptions, error) {
repoPassword, err := funcTable.getRepoPassword(urp.credentialGetter.FromSecret, param)
if err != nil {
return udmrepo.RepoOptions{}, errors.Wrap(err, "error to get repo password")
}
storeVar, err := funcTable.getStorageVariables(param.BackupLocation, param.SubDir)
if err != nil {
return udmrepo.RepoOptions{}, errors.Wrap(err, "error to get storage variables")
}
storeCred, err := funcTable.getStorageCredentials(param.BackupLocation, urp.credentialGetter.FromFile)
if err != nil {
return udmrepo.RepoOptions{}, errors.Wrap(err, "error to get repo credentials")
}
repoOption := udmrepo.RepoOptions{
StorageType: getStorageType(param.BackupLocation),
RepoPassword: repoPassword,
ConfigFilePath: getRepoConfigFile(urp.workPath, string(param.BackupLocation.UID)),
Ownership: udmrepo.OwnershipOptions{
Username: ownership.GetRepositoryOwner().Username,
@@ -138,27 +174,10 @@ func (urp *unifiedRepoProvider) getRepoOption(param RepoParam) (udmrepo.RepoOpti
GeneralOptions: make(map[string]string),
}
repoPassword, err := urp.getRepoPassword(param)
if err != nil {
return repoOption, errors.Wrap(err, "error to get repo password")
}
repoOption.RepoPassword = repoPassword
storeVar, err := getStorageVariables(param.BackupLocation, param.SubDir)
if err != nil {
return repoOption, errors.Wrap(err, "error to get storage variables")
}
for k, v := range storeVar {
repoOption.StorageOptions[k] = v
}
storeCred, err := getStorageCredentials(param.BackupLocation, urp.credentialsFileStore)
if err != nil {
return repoOption, errors.Wrap(err, "error to get repo credential env")
}
for k, v := range storeCred {
repoOption.StorageOptions[k] = v
}
@@ -187,6 +206,10 @@ func getStorageCredentials(backupLocation *velerov1api.BackupStorageLocation, cr
result := make(map[string]string)
var err error
if credentialsFileStore == nil {
return map[string]string{}, errors.New("invalid credentials interface")
}
backendType := repoconfig.GetBackendType(backupLocation.Spec.Provider)
if !repoconfig.IsBackendTypeValid(backendType) {
return map[string]string{}, errors.New("invalid storage provider")

View File

@@ -22,25 +22,35 @@ import (
awscredentials "github.com/aws/aws-sdk-go/aws/credentials"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
"github.com/stretchr/testify/require"
corev1api "k8s.io/api/core/v1"
filecredentials "github.com/vmware-tanzu/velero/internal/credentials"
velerocredentials "github.com/vmware-tanzu/velero/internal/credentials"
credmock "github.com/vmware-tanzu/velero/internal/credentials/mocks"
velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1"
velerotest "github.com/vmware-tanzu/velero/pkg/test"
"github.com/vmware-tanzu/velero/pkg/repository/udmrepo"
)
func TestGetStorageCredentials(t *testing.T) {
testCases := []struct {
name string
backupLocation velerov1api.BackupStorageLocation
credFileStore filecredentials.FileStore
credFileStore *credmock.FileStore
credStoreError error
credStorePath string
getAzureCredentials func(map[string]string) (string, string, error)
getS3Credentials func(map[string]string) (awscredentials.Value, error)
getGCPCredentials func(map[string]string) string
expected map[string]string
expectedErr string
}{
{
name: "invalid credentials file store interface",
expected: map[string]string{},
expectedErr: "invalid credentials interface",
},
{
name: "invalid provider",
backupLocation: velerov1api.BackupStorageLocation{
@@ -48,8 +58,9 @@ func TestGetStorageCredentials(t *testing.T) {
Provider: "invalid-provider",
},
},
expected: map[string]string{},
expectedErr: "invalid storage provider",
credFileStore: new(credmock.FileStore),
expected: map[string]string{},
expectedErr: "invalid storage provider",
},
{
name: "credential section exists in BSL, file store fail",
@@ -59,9 +70,10 @@ func TestGetStorageCredentials(t *testing.T) {
Credential: &corev1api.SecretKeySelector{},
},
},
credFileStore: velerotest.NewFakeCredentialsFileStore("", errors.New("fake error")),
expected: map[string]string{},
expectedErr: "error get credential file in bsl: fake error",
credFileStore: new(credmock.FileStore),
credStoreError: errors.New("fake error"),
expected: map[string]string{},
expectedErr: "error get credential file in bsl: fake error",
},
{
name: "aws, Credential section not exists in BSL",
@@ -78,7 +90,7 @@ func TestGetStorageCredentials(t *testing.T) {
AccessKeyID: "from: " + config["credentialsFile"],
}, nil
},
credFileStore: new(credmock.FileStore),
expected: map[string]string{
"accessKeyID": "from: credentials-from-config-map",
"providerName": "",
@@ -97,7 +109,8 @@ func TestGetStorageCredentials(t *testing.T) {
Credential: &corev1api.SecretKeySelector{},
},
},
credFileStore: velerotest.NewFakeCredentialsFileStore("credentials-from-credential-key", nil),
credFileStore: new(credmock.FileStore),
credStorePath: "credentials-from-credential-key",
getS3Credentials: func(config map[string]string) (awscredentials.Value, error) {
return awscredentials.Value{
AccessKeyID: "from: " + config["credentialsFile"],
@@ -121,12 +134,12 @@ func TestGetStorageCredentials(t *testing.T) {
},
},
},
credFileStore: velerotest.NewFakeCredentialsFileStore("", nil),
getS3Credentials: func(config map[string]string) (awscredentials.Value, error) {
return awscredentials.Value{}, errors.New("fake error")
},
expected: map[string]string{},
expectedErr: "error get s3 credentials: fake error",
credFileStore: new(credmock.FileStore),
expected: map[string]string{},
expectedErr: "error get s3 credentials: fake error",
},
{
name: "azure, Credential section exists in BSL",
@@ -139,7 +152,8 @@ func TestGetStorageCredentials(t *testing.T) {
Credential: &corev1api.SecretKeySelector{},
},
},
credFileStore: velerotest.NewFakeCredentialsFileStore("credentials-from-credential-key", nil),
credFileStore: new(credmock.FileStore),
credStorePath: "credentials-from-credential-key",
getAzureCredentials: func(config map[string]string) (string, string, error) {
return "storage account from: " + config["credentialsFile"], "", nil
},
@@ -162,9 +176,9 @@ func TestGetStorageCredentials(t *testing.T) {
getAzureCredentials: func(config map[string]string) (string, string, error) {
return "", "", errors.New("fake error")
},
expected: map[string]string{},
expectedErr: "error get azure credentials: fake error",
credFileStore: new(credmock.FileStore),
expected: map[string]string{},
expectedErr: "error get azure credentials: fake error",
},
{
name: "gcp, Credential section not exists in BSL",
@@ -179,7 +193,7 @@ func TestGetStorageCredentials(t *testing.T) {
getGCPCredentials: func(config map[string]string) string {
return "credentials-from-config-map"
},
credFileStore: new(credmock.FileStore),
expected: map[string]string{
"credFile": "credentials-from-config-map",
},
@@ -192,7 +206,13 @@ func TestGetStorageCredentials(t *testing.T) {
getS3Credentials = tc.getS3Credentials
getGCPCredentials = tc.getGCPCredentials
actual, err := getStorageCredentials(&tc.backupLocation, tc.credFileStore)
var fileStore velerocredentials.FileStore
if tc.credFileStore != nil {
tc.credFileStore.On("Path", mock.Anything, mock.Anything).Return(tc.credStorePath, tc.credStoreError)
fileStore = tc.credFileStore
}
actual, err := getStorageCredentials(&tc.backupLocation, fileStore)
require.Equal(t, tc.expected, actual)
@@ -412,3 +432,124 @@ func TestGetStorageVariables(t *testing.T) {
})
}
}
func TestGetRepoPassword(t *testing.T) {
testCases := []struct {
name string
getter *credmock.SecretStore
credStoreReturn string
credStoreError error
cached string
expected string
expectedErr string
}{
{
name: "invalid secret interface",
expectedErr: "invalid credentials interface",
},
{
name: "error from secret interface",
getter: new(credmock.SecretStore),
credStoreError: errors.New("fake error"),
expectedErr: "error to get password buffer: fake error",
},
{
name: "secret with whitespace",
getter: new(credmock.SecretStore),
credStoreReturn: " fake-passwor d ",
expected: "fake-passwor d",
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
var secretStore velerocredentials.SecretStore
if tc.getter != nil {
tc.getter.On("Get", mock.Anything, mock.Anything).Return(tc.credStoreReturn, tc.credStoreError)
secretStore = tc.getter
}
urp := unifiedRepoProvider{
credentialGetter: velerocredentials.CredentialGetter{
FromSecret: secretStore,
},
}
password, err := getRepoPassword(urp.credentialGetter.FromSecret, RepoParam{})
require.Equal(t, tc.expected, password)
if tc.expectedErr == "" {
assert.NoError(t, err)
} else {
assert.EqualError(t, err, tc.expectedErr)
}
})
}
}
func TestGetRepoOption(t *testing.T) {
testCases := []struct {
name string
funcTable localFuncTable
getRepoPassword func(velerocredentials.SecretStore, RepoParam) (string, error)
getStorageCredentials func(*velerov1api.BackupStorageLocation, velerocredentials.FileStore) (map[string]string, error)
getStorageVariables func(*velerov1api.BackupStorageLocation, string) (map[string]string, error)
expected udmrepo.RepoOptions
expectedErr string
}{
{
name: "get repo password fail",
funcTable: localFuncTable{
getRepoPassword: func(velerocredentials.SecretStore, RepoParam) (string, error) {
return "", errors.New("fake-error-1")
},
},
expectedErr: "error to get repo password: fake-error-1",
},
{
name: "get storage variable fail",
funcTable: localFuncTable{
getRepoPassword: func(velerocredentials.SecretStore, RepoParam) (string, error) {
return "fake-password", nil
},
getStorageVariables: func(*velerov1api.BackupStorageLocation, string) (map[string]string, error) {
return map[string]string{}, errors.New("fake-error-2")
},
},
expectedErr: "error to get storage variables: fake-error-2",
},
{
name: "get storage credentials fail",
funcTable: localFuncTable{
getRepoPassword: func(velerocredentials.SecretStore, RepoParam) (string, error) {
return "fake-password", nil
},
getStorageVariables: func(*velerov1api.BackupStorageLocation, string) (map[string]string, error) {
return map[string]string{}, nil
},
getStorageCredentials: func(*velerov1api.BackupStorageLocation, velerocredentials.FileStore) (map[string]string, error) {
return map[string]string{}, errors.New("fake-error-3")
},
},
expectedErr: "error to get repo credentials: fake-error-3",
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
funcTable = tc.funcTable
urp := unifiedRepoProvider{}
password, err := urp.getRepoOption(RepoParam{})
require.Equal(t, tc.expected, password)
if tc.expectedErr == "" {
assert.NoError(t, err)
} else {
assert.EqualError(t, err, tc.expectedErr)
}
})
}
}

View File

@@ -36,6 +36,7 @@ import (
velerov1client "github.com/vmware-tanzu/velero/pkg/generated/clientset/versioned/typed/velero/v1"
velerov1informers "github.com/vmware-tanzu/velero/pkg/generated/informers/externalversions/velero/v1"
velerov1listers "github.com/vmware-tanzu/velero/pkg/generated/listers/velero/v1"
repokey "github.com/vmware-tanzu/velero/pkg/repository/keys"
veleroexec "github.com/vmware-tanzu/velero/pkg/util/exec"
"github.com/vmware-tanzu/velero/pkg/util/filesystem"
)
@@ -242,7 +243,7 @@ func (rm *repositoryManager) Forget(ctx context.Context, snapshot SnapshotIdenti
}
func (rm *repositoryManager) exec(cmd *Command, backupLocation string) error {
file, err := rm.credentialsFileStore.Path(RepoKeySelector())
file, err := rm.credentialsFileStore.Path(repokey.RepoKeySelector())
if err != nil {
return err
}