mirror of
https://github.com/vmware-tanzu/velero.git
synced 2026-01-07 13:55:20 +00:00
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:
79
pkg/cmd/util/cacert/bsl_cacert.go
Normal file
79
pkg/cmd/util/cacert/bsl_cacert.go
Normal 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
|
||||
}
|
||||
380
pkg/cmd/util/cacert/bsl_cacert_test.go
Normal file
380
pkg/cmd/util/cacert/bsl_cacert_test.go
Normal 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)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
|
||||
1217
pkg/cmd/util/downloadrequest/downloadrequest_test.go
Normal file
1217
pkg/cmd/util/downloadrequest/downloadrequest_test.go
Normal file
File diff suppressed because it is too large
Load Diff
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user