mirror of
https://github.com/vmware-tanzu/velero.git
synced 2026-01-06 21:36:30 +00:00
repo credentials (#5167)
Signed-off-by: Lyndon-Li <lyonghui@vmware.com>
This commit is contained in:
1
changelogs/unreleased/5167-lyndon
Normal file
1
changelogs/unreleased/5167-lyndon
Normal file
@@ -0,0 +1 @@
|
||||
Add changes for Kopia Integration: Unified Repository Provider - Repo Password
|
||||
24
internal/credentials/getter.go
Normal file
24
internal/credentials/getter.go
Normal 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
|
||||
}
|
||||
49
internal/credentials/mocks/FileStore.go
Normal file
49
internal/credentials/mocks/FileStore.go
Normal 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
|
||||
}
|
||||
49
internal/credentials/mocks/SecretStore.go
Normal file
49
internal/credentials/mocks/SecretStore.go
Normal 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
|
||||
}
|
||||
56
internal/credentials/secret_store.go
Normal file
56
internal/credentials/secret_store.go
Normal 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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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")
|
||||
}
|
||||
|
||||
@@ -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")
|
||||
}
|
||||
|
||||
75
pkg/repository/keys/keys.go
Normal file
75
pkg/repository/keys/keys.go
Normal 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()
|
||||
}
|
||||
30
pkg/repository/keys/keys_test.go
Normal file
30
pkg/repository/keys/keys_test.go
Normal 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)
|
||||
}
|
||||
@@ -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")
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user