CLI automatically discovers and uses cacert from BSL for download requests

Signed-off-by: Tiger Kaovilai <tkaovila@redhat.com>

feat: Add CA cert fallback when caCertFile fails in download requests

- Fallback to BSL cert when caCertFile cannot be opened
- Combine certificate handling blocks to reuse CA pool initialization
- Add comprehensive unit tests for fallback behavior

This improves robustness by allowing downloads to proceed with BSL CA cert
when the provided CA cert file is unavailable or unreadable.

🤖 Generated with [Claude Code](https://claude.ai/code)

Signed-off-by: Tiger Kaovilai <tkaovila@redhat.com>
Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Tiger Kaovilai
2024-12-25 12:00:44 +07:00
parent bd3aa00b29
commit f4233c0f9f
16 changed files with 2255 additions and 58 deletions

View File

@@ -0,0 +1,79 @@
/*
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 cacert
import (
"context"
"github.com/pkg/errors"
apierrors "k8s.io/apimachinery/pkg/api/errors"
kbclient "sigs.k8s.io/controller-runtime/pkg/client"
velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1"
)
// GetCACertFromBackup fetches the BackupStorageLocation for a backup and returns its cacert
func GetCACertFromBackup(ctx context.Context, client kbclient.Client, namespace string, backup *velerov1api.Backup) (string, error) {
return GetCACertFromBSL(ctx, client, namespace, backup.Spec.StorageLocation)
}
// GetCACertFromRestore fetches the BackupStorageLocation for a restore's backup and returns its cacert
func GetCACertFromRestore(ctx context.Context, client kbclient.Client, namespace string, restore *velerov1api.Restore) (string, error) {
// First get the backup that this restore references
backup := &velerov1api.Backup{}
key := kbclient.ObjectKey{
Namespace: namespace,
Name: restore.Spec.BackupName,
}
if err := client.Get(ctx, key, backup); err != nil {
if apierrors.IsNotFound(err) {
// Backup not found is not a fatal error for cacert retrieval
return "", nil
}
return "", errors.Wrapf(err, "error getting backup %s", restore.Spec.BackupName)
}
return GetCACertFromBackup(ctx, client, namespace, backup)
}
// GetCACertFromBSL fetches a BackupStorageLocation directly and returns its cacert
func GetCACertFromBSL(ctx context.Context, client kbclient.Client, namespace, bslName string) (string, error) {
if bslName == "" {
return "", nil
}
bsl := &velerov1api.BackupStorageLocation{}
key := kbclient.ObjectKey{
Namespace: namespace,
Name: bslName,
}
if err := client.Get(ctx, key, bsl); err != nil {
if apierrors.IsNotFound(err) {
// BSL not found is not a fatal error, just means no cacert
return "", nil
}
return "", errors.Wrapf(err, "error getting backup storage location %s", bslName)
}
if bsl.Spec.ObjectStorage != nil && len(bsl.Spec.ObjectStorage.CACert) > 0 {
return string(bsl.Spec.ObjectStorage.CACert), nil
}
return "", nil
}

View File

@@ -0,0 +1,380 @@
/*
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 cacert
import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"sigs.k8s.io/controller-runtime/pkg/client/fake"
velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1"
"github.com/vmware-tanzu/velero/pkg/builder"
"github.com/vmware-tanzu/velero/pkg/util"
)
func TestGetCACertFromBackup(t *testing.T) {
testCases := []struct {
name string
backup *velerov1api.Backup
bsl *velerov1api.BackupStorageLocation
expectedCACert string
expectedError bool
}{
{
name: "backup with BSL containing cacert",
backup: builder.ForBackup("test-ns", "test-backup").
StorageLocation("test-bsl").
Result(),
bsl: builder.ForBackupStorageLocation("test-ns", "test-bsl").
Provider("aws").
Bucket("test-bucket").
CACert([]byte("test-cacert-content")).
Result(),
expectedCACert: "test-cacert-content",
expectedError: false,
},
{
name: "backup with BSL without cacert",
backup: builder.ForBackup("test-ns", "test-backup").
StorageLocation("test-bsl").
Result(),
bsl: builder.ForBackupStorageLocation("test-ns", "test-bsl").
Provider("aws").
Bucket("test-bucket").
Result(),
expectedCACert: "",
expectedError: false,
},
{
name: "backup without storage location",
backup: builder.ForBackup("test-ns", "test-backup").
Result(),
bsl: nil,
expectedCACert: "",
expectedError: false,
},
{
name: "BSL not found",
backup: builder.ForBackup("test-ns", "test-backup").
StorageLocation("missing-bsl").
Result(),
bsl: nil,
expectedCACert: "",
expectedError: false,
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
var objs []runtime.Object
objs = append(objs, tc.backup)
if tc.bsl != nil {
objs = append(objs, tc.bsl)
}
fakeClient := fake.NewClientBuilder().
WithScheme(util.VeleroScheme).
WithRuntimeObjects(objs...).
Build()
cacert, err := GetCACertFromBackup(t.Context(), fakeClient, "test-ns", tc.backup)
if tc.expectedError {
assert.Error(t, err)
} else {
require.NoError(t, err)
assert.Equal(t, tc.expectedCACert, cacert)
}
})
}
}
func TestGetCACertFromRestore(t *testing.T) {
testCases := []struct {
name string
restore *velerov1api.Restore
backup *velerov1api.Backup
bsl *velerov1api.BackupStorageLocation
expectedCACert string
expectedError bool
}{
{
name: "restore with backup having BSL containing cacert",
restore: builder.ForRestore("test-ns", "test-restore").
Backup("test-backup").
Result(),
backup: builder.ForBackup("test-ns", "test-backup").
StorageLocation("test-bsl").
Result(),
bsl: builder.ForBackupStorageLocation("test-ns", "test-bsl").
Provider("aws").
Bucket("test-bucket").
CACert([]byte("test-cacert-content")).
Result(),
expectedCACert: "test-cacert-content",
expectedError: false,
},
{
name: "restore with backup not found",
restore: builder.ForRestore("test-ns", "test-restore").
Backup("missing-backup").
Result(),
backup: nil,
bsl: nil,
expectedCACert: "",
expectedError: false,
},
{
name: "restore with backup having BSL without cacert",
restore: builder.ForRestore("test-ns", "test-restore").
Backup("test-backup").
Result(),
backup: builder.ForBackup("test-ns", "test-backup").
StorageLocation("test-bsl").
Result(),
bsl: builder.ForBackupStorageLocation("test-ns", "test-bsl").
Provider("aws").
Bucket("test-bucket").
Result(),
expectedCACert: "",
expectedError: false,
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
var objs []runtime.Object
objs = append(objs, tc.restore)
if tc.backup != nil {
objs = append(objs, tc.backup)
}
if tc.bsl != nil {
objs = append(objs, tc.bsl)
}
fakeClient := fake.NewClientBuilder().
WithScheme(util.VeleroScheme).
WithRuntimeObjects(objs...).
Build()
cacert, err := GetCACertFromRestore(t.Context(), fakeClient, "test-ns", tc.restore)
if tc.expectedError {
assert.Error(t, err)
} else {
require.NoError(t, err)
assert.Equal(t, tc.expectedCACert, cacert)
}
})
}
}
func TestGetCACertFromBSL(t *testing.T) {
testCases := []struct {
name string
bslName string
bsl *velerov1api.BackupStorageLocation
expectedCACert string
expectedError bool
}{
{
name: "BSL with cacert",
bslName: "test-bsl",
bsl: builder.ForBackupStorageLocation("test-ns", "test-bsl").
Provider("aws").
Bucket("test-bucket").
CACert([]byte("test-cacert-content")).
Result(),
expectedCACert: "test-cacert-content",
expectedError: false,
},
{
name: "BSL without cacert",
bslName: "test-bsl",
bsl: builder.ForBackupStorageLocation("test-ns", "test-bsl").
Provider("aws").
Bucket("test-bucket").
Result(),
expectedCACert: "",
expectedError: false,
},
{
name: "empty BSL name",
bslName: "",
bsl: nil,
expectedCACert: "",
expectedError: false,
},
{
name: "BSL not found",
bslName: "missing-bsl",
bsl: nil,
expectedCACert: "",
expectedError: false,
},
{
name: "BSL with invalid CA cert format",
bslName: "test-bsl",
bsl: builder.ForBackupStorageLocation("test-ns", "test-bsl").
Provider("aws").
Bucket("test-bucket").
CACert([]byte("INVALID CERT DATA WITHOUT PEM HEADERS")).
Result(),
expectedCACert: "INVALID CERT DATA WITHOUT PEM HEADERS", // We still return it, validation happens during TLS handshake
expectedError: false,
},
{
name: "BSL with malformed PEM certificate",
bslName: "test-bsl",
bsl: builder.ForBackupStorageLocation("test-ns", "test-bsl").
Provider("aws").
Bucket("test-bucket").
CACert([]byte("-----BEGIN CERTIFICATE-----\nINVALID BASE64 DATA!!!\n-----END CERTIFICATE-----\n")).
Result(),
expectedCACert: "-----BEGIN CERTIFICATE-----\nINVALID BASE64 DATA!!!\n-----END CERTIFICATE-----\n",
expectedError: false,
},
{
name: "BSL with nil config",
bslName: "test-bsl",
bsl: &velerov1api.BackupStorageLocation{
ObjectMeta: metav1.ObjectMeta{
Namespace: "test-ns",
Name: "test-bsl",
},
Spec: velerov1api.BackupStorageLocationSpec{
Provider: "aws",
Config: nil,
},
},
expectedCACert: "",
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)
}
fakeClient := fake.NewClientBuilder().
WithScheme(util.VeleroScheme).
WithRuntimeObjects(objs...).
Build()
cacert, err := GetCACertFromBSL(t.Context(), fakeClient, "test-ns", tc.bslName)
if tc.expectedError {
assert.Error(t, err)
} 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 {
name string
backup *velerov1api.Backup
bsl *velerov1api.BackupStorageLocation
expectedError string
}{
{
name: "client error getting BSL",
backup: builder.ForBackup("test-ns", "test-backup").
StorageLocation("test-bsl").
Result(),
bsl: builder.ForBackupStorageLocation("different-ns", "test-bsl"). // Different namespace to trigger error
Provider("aws").
Bucket("test-bucket").
CACert([]byte("test-cacert-content")).
Result(),
expectedError: "not found",
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
var objs []runtime.Object
objs = append(objs, tc.backup)
if tc.bsl != nil {
objs = append(objs, tc.bsl)
}
fakeClient := fake.NewClientBuilder().
WithScheme(util.VeleroScheme).
WithRuntimeObjects(objs...).
Build()
// Try to get BSL from wrong namespace to simulate error
_, err := GetCACertFromBSL(t.Context(), fakeClient, "wrong-ns", tc.backup.Spec.StorageLocation)
require.NoError(t, err) // Not found errors are handled gracefully
})
}
}
// TestGetCACertFromRestore_ClientError tests error scenarios for GetCACertFromRestore
func TestGetCACertFromRestore_ClientError(t *testing.T) {
testCases := []struct {
name string
restore *velerov1api.Restore
backup *velerov1api.Backup
expectedError string
}{
{
name: "backup in different namespace",
restore: builder.ForRestore("test-ns", "test-restore").
Backup("test-backup").
Result(),
backup: builder.ForBackup("different-ns", "test-backup"). // Different namespace
StorageLocation("test-bsl").
Result(),
expectedError: "not found",
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
var objs []runtime.Object
objs = append(objs, tc.restore)
if tc.backup != nil {
objs = append(objs, tc.backup)
}
fakeClient := fake.NewClientBuilder().
WithScheme(util.VeleroScheme).
WithRuntimeObjects(objs...).
Build()
// This should not find the backup in the wrong namespace
cacert, err := GetCACertFromRestore(t.Context(), fakeClient, "test-ns", tc.restore)
require.NoError(t, err) // Not found errors are handled gracefully, returning empty string
assert.Empty(t, cacert)
})
}
}

View File

@@ -50,6 +50,22 @@ func Stream(
timeout time.Duration,
insecureSkipTLSVerify bool,
caCertFile string,
) error {
return StreamWithBSLCACert(ctx, kbClient, namespace, name, kind, w, timeout, insecureSkipTLSVerify, caCertFile, "")
}
// StreamWithBSLCACert is like Stream but accepts an additional bslCACert parameter
// that contains the cacert from the BackupStorageLocation config
func StreamWithBSLCACert(
ctx context.Context,
kbClient kbclient.Client,
namespace, name string,
kind veleroV1api.DownloadTargetKind,
w io.Writer,
timeout time.Duration,
insecureSkipTLSVerify bool,
caCertFile string,
bslCACert string,
) error {
ctx, cancel := context.WithTimeout(ctx, timeout)
defer cancel()
@@ -59,7 +75,7 @@ func Stream(
return err
}
if err := download(ctx, downloadURL, kind, w, insecureSkipTLSVerify, caCertFile); err != nil {
if err := download(ctx, downloadURL, kind, w, insecureSkipTLSVerify, caCertFile, bslCACert); err != nil {
return err
}
@@ -109,21 +125,35 @@ func download(
w io.Writer,
insecureSkipTLSVerify bool,
caCertFile string,
caCertByteString string,
) error {
var caPool *x509.CertPool
var err error
// Initialize caPool once
caPool, err = x509.SystemCertPool()
if err != nil {
caPool = x509.NewCertPool()
}
// Try to load CA cert from file first
if len(caCertFile) > 0 {
caCert, err := os.ReadFile(caCertFile)
if err != nil {
return errors.Wrapf(err, "couldn't open cacert")
// If caCertFile fails and BSL cert is available, fall back to it
if len(caCertByteString) > 0 {
fmt.Fprintf(os.Stderr, "Warning: Failed to open CA certificate file %s: %v. Using CA certificate from backup storage location instead.\n", caCertFile, err)
caPool.AppendCertsFromPEM([]byte(caCertByteString))
} else {
// If no BSL cert available, return the original error
return errors.Wrapf(err, "couldn't open cacert")
}
} else {
caPool.AppendCertsFromPEM(caCert)
}
// bundle the passed in cert with the system cert pool
// if it's available, otherwise create a new pool just
// for this.
caPool, err = x509.SystemCertPool()
if err != nil {
caPool = x509.NewCertPool()
}
caPool.AppendCertsFromPEM(caCert)
} else if len(caCertByteString) > 0 {
// If no caCertFile specified, use BSL cert if available
caPool.AppendCertsFromPEM([]byte(caCertByteString))
}
defaultTransport := http.DefaultTransport.(*http.Transport)

File diff suppressed because it is too large Load Diff

View File

@@ -36,6 +36,7 @@ import (
veleroapishared "github.com/vmware-tanzu/velero/pkg/apis/velero/shared"
velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1"
"github.com/vmware-tanzu/velero/pkg/cmd/util/cacert"
"github.com/vmware-tanzu/velero/pkg/cmd/util/downloadrequest"
"github.com/vmware-tanzu/velero/pkg/itemoperation"
@@ -377,8 +378,16 @@ func describeBackupItemOperations(ctx context.Context, kbClient kbclient.Client,
return
}
// Get BSL cacert if available
bslCACert, err := cacert.GetCACertFromBackup(ctx, kbClient, backup.Namespace, backup)
if err != nil {
// Log the error but don't fail - we can still try to download without the BSL cacert
d.Printf("WARNING: Error getting cacert from BSL: %v\n", err)
bslCACert = ""
}
buf := new(bytes.Buffer)
if err := downloadrequest.Stream(ctx, kbClient, backup.Namespace, backup.Name, velerov1api.DownloadTargetKindBackupItemOperations, buf, downloadRequestTimeout, insecureSkipTLSVerify, caCertPath); err != nil {
if err := downloadrequest.StreamWithBSLCACert(ctx, kbClient, backup.Namespace, backup.Name, velerov1api.DownloadTargetKindBackupItemOperations, buf, downloadRequestTimeout, insecureSkipTLSVerify, caCertPath, bslCACert); err != nil {
d.Printf("Backup Item Operations:\t<error getting operation info: %v>\n", err)
return
}
@@ -397,8 +406,16 @@ func describeBackupItemOperations(ctx context.Context, kbClient kbclient.Client,
}
func describeBackupResourceList(ctx context.Context, kbClient kbclient.Client, d *Describer, backup *velerov1api.Backup, insecureSkipTLSVerify bool, caCertPath string) {
// Get BSL cacert if available
bslCACert, err := cacert.GetCACertFromBackup(ctx, kbClient, backup.Namespace, backup)
if err != nil {
// Log the error but don't fail - we can still try to download without the BSL cacert
d.Printf("WARNING: Error getting cacert from BSL: %v\n", err)
bslCACert = ""
}
buf := new(bytes.Buffer)
if err := downloadrequest.Stream(ctx, kbClient, backup.Namespace, backup.Name, velerov1api.DownloadTargetKindBackupResourceList, buf, downloadRequestTimeout, insecureSkipTLSVerify, caCertPath); err != nil {
if err := downloadrequest.StreamWithBSLCACert(ctx, kbClient, backup.Namespace, backup.Name, velerov1api.DownloadTargetKindBackupResourceList, buf, downloadRequestTimeout, insecureSkipTLSVerify, caCertPath, bslCACert); err != nil {
if err == downloadrequest.ErrNotFound {
// the backup resource list could be missing if (other reasons may exist as well):
// - the backup was taken prior to v1.1; or
@@ -444,20 +461,28 @@ func describeBackupVolumes(
) {
d.Println("Backup Volumes:")
// Get BSL cacert if available
bslCACert, err := cacert.GetCACertFromBackup(ctx, kbClient, backup.Namespace, backup)
if err != nil {
// Log the error but don't fail - we can still try to download without the BSL cacert
d.Printf("WARNING: Error getting cacert from BSL: %v\n", err)
bslCACert = ""
}
nativeSnapshots := []*volume.BackupVolumeInfo{}
csiSnapshots := []*volume.BackupVolumeInfo{}
legacyInfoSource := false
buf := new(bytes.Buffer)
err := downloadrequest.Stream(ctx, kbClient, backup.Namespace, backup.Name, velerov1api.DownloadTargetKindBackupVolumeInfos, buf, downloadRequestTimeout, insecureSkipTLSVerify, caCertPath)
err = downloadrequest.StreamWithBSLCACert(ctx, kbClient, backup.Namespace, backup.Name, velerov1api.DownloadTargetKindBackupVolumeInfos, buf, downloadRequestTimeout, insecureSkipTLSVerify, caCertPath, bslCACert)
if err == downloadrequest.ErrNotFound {
nativeSnapshots, err = retrieveNativeSnapshotLegacy(ctx, kbClient, backup, insecureSkipTLSVerify, caCertPath)
nativeSnapshots, err = retrieveNativeSnapshotLegacy(ctx, kbClient, backup, insecureSkipTLSVerify, caCertPath, bslCACert)
if err != nil {
d.Printf("\t<error concluding native snapshot info: %v>\n", err)
return
}
csiSnapshots, err = retrieveCSISnapshotLegacy(ctx, kbClient, backup, insecureSkipTLSVerify, caCertPath)
csiSnapshots, err = retrieveCSISnapshotLegacy(ctx, kbClient, backup, insecureSkipTLSVerify, caCertPath, bslCACert)
if err != nil {
d.Printf("\t<error concluding CSI snapshot info: %v>\n", err)
return
@@ -493,7 +518,7 @@ func describeBackupVolumes(
describePodVolumeBackups(d, details, podVolumeBackupCRs)
}
func retrieveNativeSnapshotLegacy(ctx context.Context, kbClient kbclient.Client, backup *velerov1api.Backup, insecureSkipTLSVerify bool, caCertPath string) ([]*volume.BackupVolumeInfo, error) {
func retrieveNativeSnapshotLegacy(ctx context.Context, kbClient kbclient.Client, backup *velerov1api.Backup, insecureSkipTLSVerify bool, caCertPath string, bslCACert string) ([]*volume.BackupVolumeInfo, error) {
status := backup.Status
nativeSnapshots := []*volume.BackupVolumeInfo{}
@@ -502,7 +527,7 @@ func retrieveNativeSnapshotLegacy(ctx context.Context, kbClient kbclient.Client,
}
buf := new(bytes.Buffer)
if err := downloadrequest.Stream(ctx, kbClient, backup.Namespace, backup.Name, velerov1api.DownloadTargetKindBackupVolumeSnapshots, buf, downloadRequestTimeout, insecureSkipTLSVerify, caCertPath); err != nil {
if err := downloadrequest.StreamWithBSLCACert(ctx, kbClient, backup.Namespace, backup.Name, velerov1api.DownloadTargetKindBackupVolumeSnapshots, buf, downloadRequestTimeout, insecureSkipTLSVerify, caCertPath, bslCACert); err != nil {
return nativeSnapshots, errors.Wrapf(err, "error to download native snapshot info")
}
@@ -531,7 +556,7 @@ func retrieveNativeSnapshotLegacy(ctx context.Context, kbClient kbclient.Client,
return nativeSnapshots, nil
}
func retrieveCSISnapshotLegacy(ctx context.Context, kbClient kbclient.Client, backup *velerov1api.Backup, insecureSkipTLSVerify bool, caCertPath string) ([]*volume.BackupVolumeInfo, error) {
func retrieveCSISnapshotLegacy(ctx context.Context, kbClient kbclient.Client, backup *velerov1api.Backup, insecureSkipTLSVerify bool, caCertPath string, bslCACert string) ([]*volume.BackupVolumeInfo, error) {
status := backup.Status
csiSnapshots := []*volume.BackupVolumeInfo{}
@@ -540,7 +565,7 @@ func retrieveCSISnapshotLegacy(ctx context.Context, kbClient kbclient.Client, ba
}
vsBuf := new(bytes.Buffer)
err := downloadrequest.Stream(ctx, kbClient, backup.Namespace, backup.Name, velerov1api.DownloadTargetKindCSIBackupVolumeSnapshots, vsBuf, downloadRequestTimeout, insecureSkipTLSVerify, caCertPath)
err := downloadrequest.StreamWithBSLCACert(ctx, kbClient, backup.Namespace, backup.Name, velerov1api.DownloadTargetKindCSIBackupVolumeSnapshots, vsBuf, downloadRequestTimeout, insecureSkipTLSVerify, caCertPath, bslCACert)
if err != nil {
return csiSnapshots, errors.Wrapf(err, "error to download vs list")
}
@@ -551,7 +576,7 @@ func retrieveCSISnapshotLegacy(ctx context.Context, kbClient kbclient.Client, ba
}
vscBuf := new(bytes.Buffer)
err = downloadrequest.Stream(ctx, kbClient, backup.Namespace, backup.Name, velerov1api.DownloadTargetKindCSIBackupVolumeSnapshotContents, vscBuf, downloadRequestTimeout, insecureSkipTLSVerify, caCertPath)
err = downloadrequest.StreamWithBSLCACert(ctx, kbClient, backup.Namespace, backup.Name, velerov1api.DownloadTargetKindCSIBackupVolumeSnapshotContents, vscBuf, downloadRequestTimeout, insecureSkipTLSVerify, caCertPath, bslCACert)
if err != nil {
return csiSnapshots, errors.Wrapf(err, "error to download vsc list")
}
@@ -901,12 +926,20 @@ func DescribeBackupResults(ctx context.Context, kbClient kbclient.Client, d *Des
return
}
// Get BSL cacert if available
bslCACert, err := cacert.GetCACertFromBackup(ctx, kbClient, backup.Namespace, backup)
if err != nil {
// Log the error but don't fail - we can still try to download without the BSL cacert
d.Printf("WARNING: Error getting cacert from BSL: %v\n", err)
bslCACert = ""
}
var buf bytes.Buffer
var resultMap map[string]results.Result
// If err 'ErrNotFound' occurs, it means the backup bundle in the bucket has already been there before the backup-result file is introduced.
// We only display the count of errors and warnings in this case.
err := downloadrequest.Stream(ctx, kbClient, backup.Namespace, backup.Name, velerov1api.DownloadTargetKindBackupResults, &buf, downloadRequestTimeout, insecureSkipTLSVerify, caCertPath)
err = downloadrequest.StreamWithBSLCACert(ctx, kbClient, backup.Namespace, backup.Name, velerov1api.DownloadTargetKindBackupResults, &buf, downloadRequestTimeout, insecureSkipTLSVerify, caCertPath, bslCACert)
if err == downloadrequest.ErrNotFound {
d.Printf("Errors:\t%d\n", backup.Status.Errors)
d.Printf("Warnings:\t%d\n", backup.Status.Warnings)

View File

@@ -30,6 +30,7 @@ import (
"github.com/vmware-tanzu/velero/internal/volume"
velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1"
"github.com/vmware-tanzu/velero/pkg/cmd/util/cacert"
"github.com/vmware-tanzu/velero/pkg/cmd/util/downloadrequest"
"github.com/vmware-tanzu/velero/pkg/util/results"
)
@@ -272,11 +273,19 @@ func DescribeBackupStatusInSF(ctx context.Context, kbClient kbclient.Client, d *
}
func describeBackupResourceListInSF(ctx context.Context, kbClient kbclient.Client, backupStatusInfo map[string]any, backup *velerov1api.Backup, insecureSkipTLSVerify bool, caCertPath string) {
// Get BSL cacert if available
bslCACert, err := cacert.GetCACertFromBackup(ctx, kbClient, backup.Namespace, backup)
if err != nil {
// Log the error but don't fail - we can still try to download without the BSL cacert
backupStatusInfo["warningGettingBSLCACert"] = fmt.Sprintf("Warning: Error getting cacert from BSL: %v", err)
bslCACert = ""
}
// In consideration of decoding structured output conveniently, the two separate fields were created here(in func describeBackupResourceList, there is only one field describing either error message or resource list)
// the field of 'errorGettingResourceList' gives specific error message when it fails to get resources list
// the field of 'resourceList' lists the rearranged resources
buf := new(bytes.Buffer)
if err := downloadrequest.Stream(ctx, kbClient, backup.Namespace, backup.Name, velerov1api.DownloadTargetKindBackupResourceList, buf, downloadRequestTimeout, insecureSkipTLSVerify, caCertPath); err != nil {
if err := downloadrequest.StreamWithBSLCACert(ctx, kbClient, backup.Namespace, backup.Name, velerov1api.DownloadTargetKindBackupResourceList, buf, downloadRequestTimeout, insecureSkipTLSVerify, caCertPath, bslCACert); err != nil {
if err == downloadrequest.ErrNotFound {
// the backup resource list could be missing if (other reasons may exist as well):
// - the backup was taken prior to v1.1; or
@@ -302,20 +311,28 @@ func describeBackupVolumesInSF(ctx context.Context, kbClient kbclient.Client, ba
insecureSkipTLSVerify bool, caCertPath string, podVolumeBackupCRs []velerov1api.PodVolumeBackup, backupStatusInfo map[string]any) {
backupVolumes := make(map[string]any)
// Get BSL cacert if available
bslCACert, err := cacert.GetCACertFromBackup(ctx, kbClient, backup.Namespace, backup)
if err != nil {
// Log the error but don't fail - we can still try to download without the BSL cacert
backupVolumes["warningGettingBSLCACert"] = fmt.Sprintf("Warning: Error getting cacert from BSL: %v", err)
bslCACert = ""
}
nativeSnapshots := []*volume.BackupVolumeInfo{}
csiSnapshots := []*volume.BackupVolumeInfo{}
legacyInfoSource := false
buf := new(bytes.Buffer)
err := downloadrequest.Stream(ctx, kbClient, backup.Namespace, backup.Name, velerov1api.DownloadTargetKindBackupVolumeInfos, buf, downloadRequestTimeout, insecureSkipTLSVerify, caCertPath)
err = downloadrequest.StreamWithBSLCACert(ctx, kbClient, backup.Namespace, backup.Name, velerov1api.DownloadTargetKindBackupVolumeInfos, buf, downloadRequestTimeout, insecureSkipTLSVerify, caCertPath, bslCACert)
if err == downloadrequest.ErrNotFound {
nativeSnapshots, err = retrieveNativeSnapshotLegacy(ctx, kbClient, backup, insecureSkipTLSVerify, caCertPath)
nativeSnapshots, err = retrieveNativeSnapshotLegacy(ctx, kbClient, backup, insecureSkipTLSVerify, caCertPath, bslCACert)
if err != nil {
backupVolumes["errorConcludeNativeSnapshot"] = fmt.Sprintf("error concluding native snapshot info: %v", err)
return
}
csiSnapshots, err = retrieveCSISnapshotLegacy(ctx, kbClient, backup, insecureSkipTLSVerify, caCertPath)
csiSnapshots, err = retrieveCSISnapshotLegacy(ctx, kbClient, backup, insecureSkipTLSVerify, caCertPath, bslCACert)
if err != nil {
backupVolumes["errorConcludeCSISnapshot"] = fmt.Sprintf("error concluding CSI snapshot info: %v", err)
return
@@ -538,6 +555,16 @@ func DescribeBackupResultsInSF(ctx context.Context, kbClient kbclient.Client, d
return
}
// Get BSL cacert if available
bslCACert, err := cacert.GetCACertFromBackup(ctx, kbClient, backup.Namespace, backup)
if err != nil {
// Log the error but don't fail - we can still try to download without the BSL cacert
warnings := make(map[string]any)
warnings["warningGettingBSLCACert"] = fmt.Sprintf("Warning: Error getting cacert from BSL: %v", err)
d.Describe("warningsGettingBSLCACert", warnings)
bslCACert = ""
}
var buf bytes.Buffer
var resultMap map[string]results.Result
@@ -549,7 +576,7 @@ func DescribeBackupResultsInSF(ctx context.Context, kbClient kbclient.Client, d
// If 'ErrNotFound' occurs, it means the backup bundle in the bucket has already been there before the backup-result file is introduced.
// We only display the count of errors and warnings in this case.
err := downloadrequest.Stream(ctx, kbClient, backup.Namespace, backup.Name, velerov1api.DownloadTargetKindBackupResults, &buf, downloadRequestTimeout, insecureSkipTLSVerify, caCertPath)
err = downloadrequest.StreamWithBSLCACert(ctx, kbClient, backup.Namespace, backup.Name, velerov1api.DownloadTargetKindBackupResults, &buf, downloadRequestTimeout, insecureSkipTLSVerify, caCertPath, bslCACert)
if err == downloadrequest.ErrNotFound {
errors["count"] = backup.Status.Errors
warnings["count"] = backup.Status.Warnings

View File

@@ -34,6 +34,7 @@ import (
"github.com/fatih/color"
velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1"
"github.com/vmware-tanzu/velero/pkg/cmd/util/cacert"
"github.com/vmware-tanzu/velero/pkg/cmd/util/downloadrequest"
"github.com/vmware-tanzu/velero/pkg/itemoperation"
"github.com/vmware-tanzu/velero/pkg/util/boolptr"
@@ -179,9 +180,17 @@ func DescribeRestore(
describePodVolumeRestores(d, podVolumeRestores, details)
}
// Get BSL cacert if available
bslCACert, err := cacert.GetCACertFromRestore(ctx, kbClient, restore.Namespace, restore)
if err != nil {
// Log the error but don't fail - we can still try to download without the BSL cacert
d.Printf("WARNING: Error getting cacert from BSL: %v\n", err)
bslCACert = ""
}
buf := new(bytes.Buffer)
if err := downloadrequest.Stream(ctx, kbClient, restore.Namespace, restore.Name, velerov1api.DownloadTargetKindRestoreVolumeInfo,
buf, downloadRequestTimeout, insecureSkipTLSVerify, caCertFile); err == nil {
if err := downloadrequest.StreamWithBSLCACert(ctx, kbClient, restore.Namespace, restore.Name, velerov1api.DownloadTargetKindRestoreVolumeInfo,
buf, downloadRequestTimeout, insecureSkipTLSVerify, caCertFile, bslCACert); err == nil {
var restoreVolInfo []volume.RestoreVolumeInfo
if err := json.NewDecoder(buf).Decode(&restoreVolInfo); err != nil {
d.Printf("\t<error reading restore volume info: %v>\n", err)
@@ -250,8 +259,16 @@ func describeRestoreItemOperations(ctx context.Context, kbClient kbclient.Client
return
}
// Get BSL cacert if available
bslCACert, err := cacert.GetCACertFromRestore(ctx, kbClient, restore.Namespace, restore)
if err != nil {
// Log the error but don't fail - we can still try to download without the BSL cacert
d.Printf("WARNING: Error getting cacert from BSL: %v\n", err)
bslCACert = ""
}
buf := new(bytes.Buffer)
if err := downloadrequest.Stream(ctx, kbClient, restore.Namespace, restore.Name, velerov1api.DownloadTargetKindRestoreItemOperations, buf, downloadRequestTimeout, insecureSkipTLSVerify, caCertPath); err != nil {
if err := downloadrequest.StreamWithBSLCACert(ctx, kbClient, restore.Namespace, restore.Name, velerov1api.DownloadTargetKindRestoreItemOperations, buf, downloadRequestTimeout, insecureSkipTLSVerify, caCertPath, bslCACert); err != nil {
d.Printf("Restore Item Operations:\t<error getting operation info: %v>\n", err)
return
}
@@ -274,10 +291,18 @@ func describeRestoreResults(ctx context.Context, kbClient kbclient.Client, d *De
return
}
// Get BSL cacert if available
bslCACert, err := cacert.GetCACertFromRestore(ctx, kbClient, restore.Namespace, restore)
if err != nil {
// Log the error but don't fail - we can still try to download without the BSL cacert
d.Printf("WARNING: Error getting cacert from BSL: %v\n", err)
bslCACert = ""
}
var buf bytes.Buffer
var resultMap map[string]results.Result
if err := downloadrequest.Stream(ctx, kbClient, restore.Namespace, restore.Name, velerov1api.DownloadTargetKindRestoreResults, &buf, downloadRequestTimeout, insecureSkipTLSVerify, caCertPath); err != nil {
if err := downloadrequest.StreamWithBSLCACert(ctx, kbClient, restore.Namespace, restore.Name, velerov1api.DownloadTargetKindRestoreResults, &buf, downloadRequestTimeout, insecureSkipTLSVerify, caCertPath, bslCACert); err != nil {
d.Printf("Warnings:\t<error getting warnings: %v>\n\nErrors:\t<error getting errors: %v>\n", err, err)
return
}
@@ -463,8 +488,16 @@ func groupRestoresByPhase(restores []velerov1api.PodVolumeRestore) map[string][]
}
func describeRestoreResourceList(ctx context.Context, kbClient kbclient.Client, d *Describer, restore *velerov1api.Restore, insecureSkipTLSVerify bool, caCertPath string) {
// Get BSL cacert if available
bslCACert, err := cacert.GetCACertFromRestore(ctx, kbClient, restore.Namespace, restore)
if err != nil {
// Log the error but don't fail - we can still try to download without the BSL cacert
d.Printf("WARNING: Error getting cacert from BSL: %v\n", err)
bslCACert = ""
}
buf := new(bytes.Buffer)
if err := downloadrequest.Stream(ctx, kbClient, restore.Namespace, restore.Name, velerov1api.DownloadTargetKindRestoreResourceList, buf, downloadRequestTimeout, insecureSkipTLSVerify, caCertPath); err != nil {
if err := downloadrequest.StreamWithBSLCACert(ctx, kbClient, restore.Namespace, restore.Name, velerov1api.DownloadTargetKindRestoreResourceList, buf, downloadRequestTimeout, insecureSkipTLSVerify, caCertPath, bslCACert); err != nil {
if err == downloadrequest.ErrNotFound {
d.Println("Resource List:\t<restore resource list not found>")
} else {