mirror of
https://github.com/vmware-tanzu/velero.git
synced 2026-08-15 11:46:06 +00:00
Merge pull request #9141 from kaovilai/9097
Run the E2E test on kind / get-go-version (push) Failing after 1m8s
Run the E2E test on kind / build (push) Has been skipped
Run the E2E test on kind / setup-test-matrix (push) Successful in 3s
Run the E2E test on kind / run-e2e-test (push) Has been skipped
Main CI / get-go-version (push) Successful in 16s
Main CI / Build (push) Failing after 45s
Run the E2E test on kind / get-go-version (push) Failing after 1m8s
Run the E2E test on kind / build (push) Has been skipped
Run the E2E test on kind / setup-test-matrix (push) Successful in 3s
Run the E2E test on kind / run-e2e-test (push) Has been skipped
Main CI / get-go-version (push) Successful in 16s
Main CI / Build (push) Failing after 45s
feat: Enhance BackupStorageLocation with Secret-based CA certificate support
This commit is contained in:
@@ -558,7 +558,7 @@ func (s *server) runControllers(defaultVolumeSnapshotLocations map[string]string
|
||||
return clientmgmt.NewManager(logger, s.logLevel, s.pluginRegistry)
|
||||
}
|
||||
|
||||
backupStoreGetter := persistence.NewObjectBackupStoreGetter(s.credentialFileStore)
|
||||
backupStoreGetter := persistence.NewObjectBackupStoreGetterWithSecretStore(s.credentialFileStore, s.credentialSecretStore)
|
||||
|
||||
backupTracker := controller.NewBackupTracker()
|
||||
|
||||
|
||||
@@ -20,7 +20,9 @@ import (
|
||||
"context"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
corev1api "k8s.io/api/core/v1"
|
||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
kbclient "sigs.k8s.io/controller-runtime/pkg/client"
|
||||
|
||||
velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1"
|
||||
@@ -52,6 +54,7 @@ func GetCACertFromRestore(ctx context.Context, client kbclient.Client, namespace
|
||||
}
|
||||
|
||||
// GetCACertFromBSL fetches a BackupStorageLocation directly and returns its cacert
|
||||
// Priority order: caCertRef (from Secret) > caCert (inline, deprecated)
|
||||
func GetCACertFromBSL(ctx context.Context, client kbclient.Client, namespace, bslName string) (string, error) {
|
||||
if bslName == "" {
|
||||
return "", nil
|
||||
@@ -71,7 +74,44 @@ func GetCACertFromBSL(ctx context.Context, client kbclient.Client, namespace, bs
|
||||
return "", errors.Wrapf(err, "error getting backup storage location %s", bslName)
|
||||
}
|
||||
|
||||
if bsl.Spec.ObjectStorage != nil && len(bsl.Spec.ObjectStorage.CACert) > 0 {
|
||||
if bsl.Spec.ObjectStorage == nil {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
// Prefer caCertRef over inline caCert
|
||||
if bsl.Spec.ObjectStorage.CACertRef != nil {
|
||||
// Fetch certificate from Secret
|
||||
secret := &corev1api.Secret{}
|
||||
secretKey := types.NamespacedName{
|
||||
Name: bsl.Spec.ObjectStorage.CACertRef.Name,
|
||||
Namespace: namespace,
|
||||
}
|
||||
|
||||
if err := client.Get(ctx, secretKey, secret); err != nil {
|
||||
if apierrors.IsNotFound(err) {
|
||||
return "", errors.Errorf("certificate secret %s not found in namespace %s",
|
||||
bsl.Spec.ObjectStorage.CACertRef.Name, namespace)
|
||||
}
|
||||
return "", errors.Wrapf(err, "error getting certificate secret %s",
|
||||
bsl.Spec.ObjectStorage.CACertRef.Name)
|
||||
}
|
||||
|
||||
keyName := bsl.Spec.ObjectStorage.CACertRef.Key
|
||||
if keyName == "" {
|
||||
return "", errors.New("caCertRef key is empty")
|
||||
}
|
||||
|
||||
certData, ok := secret.Data[keyName]
|
||||
if !ok {
|
||||
return "", errors.Errorf("key %s not found in secret %s",
|
||||
keyName, bsl.Spec.ObjectStorage.CACertRef.Name)
|
||||
}
|
||||
|
||||
return string(certData), nil
|
||||
}
|
||||
|
||||
// Fall back to inline caCert (deprecated)
|
||||
if len(bsl.Spec.ObjectStorage.CACert) > 0 {
|
||||
return string(bsl.Spec.ObjectStorage.CACert), nil
|
||||
}
|
||||
|
||||
|
||||
@@ -21,6 +21,7 @@ import (
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
corev1api "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client/fake"
|
||||
@@ -294,6 +295,271 @@ func TestGetCACertFromBSL(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// TestGetCACertFromBSL_WithCACertRef tests the new caCertRef functionality
|
||||
func TestGetCACertFromBSL_WithCACertRef(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
bslName string
|
||||
bsl *velerov1api.BackupStorageLocation
|
||||
secret *corev1api.Secret
|
||||
expectedCACert string
|
||||
expectedError bool
|
||||
errorContains string
|
||||
}{
|
||||
{
|
||||
name: "BSL with caCertRef pointing to valid secret",
|
||||
bslName: "test-bsl",
|
||||
bsl: &velerov1api.BackupStorageLocation{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Namespace: "test-ns",
|
||||
Name: "test-bsl",
|
||||
},
|
||||
Spec: velerov1api.BackupStorageLocationSpec{
|
||||
Provider: "aws",
|
||||
StorageType: velerov1api.StorageType{
|
||||
ObjectStorage: &velerov1api.ObjectStorageLocation{
|
||||
Bucket: "test-bucket",
|
||||
CACertRef: &corev1api.SecretKeySelector{
|
||||
LocalObjectReference: corev1api.LocalObjectReference{
|
||||
Name: "test-secret",
|
||||
},
|
||||
Key: "ca-bundle.crt",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
secret: &corev1api.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Namespace: "test-ns",
|
||||
Name: "test-secret",
|
||||
},
|
||||
Data: map[string][]byte{
|
||||
"ca-bundle.crt": []byte("test-cacert-from-secret"),
|
||||
},
|
||||
},
|
||||
expectedCACert: "test-cacert-from-secret",
|
||||
expectedError: false,
|
||||
},
|
||||
{
|
||||
name: "BSL with both caCertRef and caCert - caCertRef takes precedence",
|
||||
bslName: "test-bsl",
|
||||
bsl: &velerov1api.BackupStorageLocation{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Namespace: "test-ns",
|
||||
Name: "test-bsl",
|
||||
},
|
||||
Spec: velerov1api.BackupStorageLocationSpec{
|
||||
Provider: "aws",
|
||||
StorageType: velerov1api.StorageType{
|
||||
ObjectStorage: &velerov1api.ObjectStorageLocation{
|
||||
Bucket: "test-bucket",
|
||||
CACert: []byte("inline-cacert-deprecated"),
|
||||
CACertRef: &corev1api.SecretKeySelector{
|
||||
LocalObjectReference: corev1api.LocalObjectReference{
|
||||
Name: "test-secret",
|
||||
},
|
||||
Key: "ca-bundle.crt",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
secret: &corev1api.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Namespace: "test-ns",
|
||||
Name: "test-secret",
|
||||
},
|
||||
Data: map[string][]byte{
|
||||
"ca-bundle.crt": []byte("cacert-from-secret-takes-precedence"),
|
||||
},
|
||||
},
|
||||
expectedCACert: "cacert-from-secret-takes-precedence",
|
||||
expectedError: false,
|
||||
},
|
||||
{
|
||||
name: "BSL with caCertRef but secret not found",
|
||||
bslName: "test-bsl",
|
||||
bsl: &velerov1api.BackupStorageLocation{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Namespace: "test-ns",
|
||||
Name: "test-bsl",
|
||||
},
|
||||
Spec: velerov1api.BackupStorageLocationSpec{
|
||||
Provider: "aws",
|
||||
StorageType: velerov1api.StorageType{
|
||||
ObjectStorage: &velerov1api.ObjectStorageLocation{
|
||||
Bucket: "test-bucket",
|
||||
CACertRef: &corev1api.SecretKeySelector{
|
||||
LocalObjectReference: corev1api.LocalObjectReference{
|
||||
Name: "missing-secret",
|
||||
},
|
||||
Key: "ca-bundle.crt",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
secret: nil,
|
||||
expectedCACert: "",
|
||||
expectedError: true,
|
||||
errorContains: "certificate secret missing-secret not found",
|
||||
},
|
||||
{
|
||||
name: "BSL with caCertRef but key not found in secret",
|
||||
bslName: "test-bsl",
|
||||
bsl: &velerov1api.BackupStorageLocation{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Namespace: "test-ns",
|
||||
Name: "test-bsl",
|
||||
},
|
||||
Spec: velerov1api.BackupStorageLocationSpec{
|
||||
Provider: "aws",
|
||||
StorageType: velerov1api.StorageType{
|
||||
ObjectStorage: &velerov1api.ObjectStorageLocation{
|
||||
Bucket: "test-bucket",
|
||||
CACertRef: &corev1api.SecretKeySelector{
|
||||
LocalObjectReference: corev1api.LocalObjectReference{
|
||||
Name: "test-secret",
|
||||
},
|
||||
Key: "missing-key",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
secret: &corev1api.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Namespace: "test-ns",
|
||||
Name: "test-secret",
|
||||
},
|
||||
Data: map[string][]byte{
|
||||
"ca-bundle.crt": []byte("test-cacert"),
|
||||
},
|
||||
},
|
||||
expectedCACert: "",
|
||||
expectedError: true,
|
||||
errorContains: "key missing-key not found in secret test-secret",
|
||||
},
|
||||
{
|
||||
name: "BSL with caCertRef but empty key",
|
||||
bslName: "test-bsl",
|
||||
bsl: &velerov1api.BackupStorageLocation{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Namespace: "test-ns",
|
||||
Name: "test-bsl",
|
||||
},
|
||||
Spec: velerov1api.BackupStorageLocationSpec{
|
||||
Provider: "aws",
|
||||
StorageType: velerov1api.StorageType{
|
||||
ObjectStorage: &velerov1api.ObjectStorageLocation{
|
||||
Bucket: "test-bucket",
|
||||
CACertRef: &corev1api.SecretKeySelector{
|
||||
LocalObjectReference: corev1api.LocalObjectReference{
|
||||
Name: "test-secret",
|
||||
},
|
||||
Key: "",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
secret: &corev1api.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Namespace: "test-ns",
|
||||
Name: "test-secret",
|
||||
},
|
||||
Data: map[string][]byte{
|
||||
"ca-bundle.crt": []byte("test-cacert"),
|
||||
},
|
||||
},
|
||||
expectedCACert: "",
|
||||
expectedError: true,
|
||||
errorContains: "caCertRef key is empty",
|
||||
},
|
||||
{
|
||||
name: "BSL with caCertRef containing multi-line PEM certificate",
|
||||
bslName: "test-bsl",
|
||||
bsl: &velerov1api.BackupStorageLocation{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Namespace: "test-ns",
|
||||
Name: "test-bsl",
|
||||
},
|
||||
Spec: velerov1api.BackupStorageLocationSpec{
|
||||
Provider: "aws",
|
||||
StorageType: velerov1api.StorageType{
|
||||
ObjectStorage: &velerov1api.ObjectStorageLocation{
|
||||
Bucket: "test-bucket",
|
||||
CACertRef: &corev1api.SecretKeySelector{
|
||||
LocalObjectReference: corev1api.LocalObjectReference{
|
||||
Name: "test-secret",
|
||||
},
|
||||
Key: "ca.pem",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
secret: &corev1api.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Namespace: "test-ns",
|
||||
Name: "test-secret",
|
||||
},
|
||||
Data: map[string][]byte{
|
||||
"ca.pem": []byte("-----BEGIN CERTIFICATE-----\nMIIDETC...\n-----END CERTIFICATE-----\n"),
|
||||
},
|
||||
},
|
||||
expectedCACert: "-----BEGIN CERTIFICATE-----\nMIIDETC...\n-----END CERTIFICATE-----\n",
|
||||
expectedError: false,
|
||||
},
|
||||
{
|
||||
name: "BSL falls back to inline caCert when caCertRef is nil",
|
||||
bslName: "test-bsl",
|
||||
bsl: builder.ForBackupStorageLocation("test-ns", "test-bsl").
|
||||
Provider("aws").
|
||||
Bucket("test-bucket").
|
||||
CACert([]byte("fallback-inline-cacert")).
|
||||
Result(),
|
||||
secret: nil,
|
||||
expectedCACert: "fallback-inline-cacert",
|
||||
expectedError: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
var objs []runtime.Object
|
||||
if tc.bsl != nil {
|
||||
objs = append(objs, tc.bsl)
|
||||
}
|
||||
if tc.secret != nil {
|
||||
objs = append(objs, tc.secret)
|
||||
}
|
||||
|
||||
scheme := runtime.NewScheme()
|
||||
_ = velerov1api.AddToScheme(scheme)
|
||||
_ = corev1api.AddToScheme(scheme)
|
||||
|
||||
fakeClient := fake.NewClientBuilder().
|
||||
WithScheme(scheme).
|
||||
WithRuntimeObjects(objs...).
|
||||
Build()
|
||||
|
||||
cacert, err := GetCACertFromBSL(t.Context(), fakeClient, "test-ns", tc.bslName)
|
||||
|
||||
if tc.expectedError {
|
||||
require.Error(t, err)
|
||||
if tc.errorContains != "" {
|
||||
assert.Contains(t, err.Error(), tc.errorContains)
|
||||
}
|
||||
} else {
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, tc.expectedCACert, cacert)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// TestGetCACertFromBackup_ClientError tests error scenarios where client.Get returns non-NotFound errors
|
||||
func TestGetCACertFromBackup_ClientError(t *testing.T) {
|
||||
testCases := []struct {
|
||||
|
||||
Reference in New Issue
Block a user