mirror of
https://github.com/vmware-tanzu/velero.git
synced 2026-01-05 13:05:17 +00:00
Merge pull request #8557 from kaovilai/cacertcli-auto
CLI automatically discovers and uses cacert from BSL
This commit is contained in:
1
changelogs/unreleased/8557-kaovilai
Normal file
1
changelogs/unreleased/8557-kaovilai
Normal file
@@ -0,0 +1 @@
|
|||||||
|
CLI automatically discovers and uses cacert from BSL for download requests
|
||||||
@@ -62,21 +62,21 @@ func NewDescribeCommand(f client.Factory, use string) *cobra.Command {
|
|||||||
if len(args) > 0 {
|
if len(args) > 0 {
|
||||||
for _, name := range args {
|
for _, name := range args {
|
||||||
backup := new(velerov1api.Backup)
|
backup := new(velerov1api.Backup)
|
||||||
err := kbClient.Get(context.TODO(), controllerclient.ObjectKey{Namespace: f.Namespace(), Name: name}, backup)
|
err := kbClient.Get(context.Background(), controllerclient.ObjectKey{Namespace: f.Namespace(), Name: name}, backup)
|
||||||
cmd.CheckError(err)
|
cmd.CheckError(err)
|
||||||
backups.Items = append(backups.Items, *backup)
|
backups.Items = append(backups.Items, *backup)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
parsedSelector, err := labels.Parse(listOptions.LabelSelector)
|
parsedSelector, err := labels.Parse(listOptions.LabelSelector)
|
||||||
cmd.CheckError(err)
|
cmd.CheckError(err)
|
||||||
err = kbClient.List(context.TODO(), backups, &controllerclient.ListOptions{LabelSelector: parsedSelector, Namespace: f.Namespace()})
|
err = kbClient.List(context.Background(), backups, &controllerclient.ListOptions{LabelSelector: parsedSelector, Namespace: f.Namespace()})
|
||||||
cmd.CheckError(err)
|
cmd.CheckError(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
first := true
|
first := true
|
||||||
for i, backup := range backups.Items {
|
for i, backup := range backups.Items {
|
||||||
deleteRequestList := new(velerov1api.DeleteBackupRequestList)
|
deleteRequestList := new(velerov1api.DeleteBackupRequestList)
|
||||||
err := kbClient.List(context.TODO(), deleteRequestList, &controllerclient.ListOptions{
|
err := kbClient.List(context.Background(), deleteRequestList, &controllerclient.ListOptions{
|
||||||
Namespace: f.Namespace(),
|
Namespace: f.Namespace(),
|
||||||
LabelSelector: labels.SelectorFromSet(map[string]string{velerov1api.BackupNameLabel: label.GetValidName(backup.Name), velerov1api.BackupUIDLabel: string(backup.UID)}),
|
LabelSelector: labels.SelectorFromSet(map[string]string{velerov1api.BackupNameLabel: label.GetValidName(backup.Name), velerov1api.BackupUIDLabel: string(backup.UID)}),
|
||||||
})
|
})
|
||||||
@@ -85,7 +85,7 @@ func NewDescribeCommand(f client.Factory, use string) *cobra.Command {
|
|||||||
}
|
}
|
||||||
|
|
||||||
podVolumeBackupList := new(velerov1api.PodVolumeBackupList)
|
podVolumeBackupList := new(velerov1api.PodVolumeBackupList)
|
||||||
err = kbClient.List(context.TODO(), podVolumeBackupList, &controllerclient.ListOptions{
|
err = kbClient.List(context.Background(), podVolumeBackupList, &controllerclient.ListOptions{
|
||||||
Namespace: f.Namespace(),
|
Namespace: f.Namespace(),
|
||||||
LabelSelector: labels.SelectorFromSet(map[string]string{velerov1api.BackupNameLabel: label.GetValidName(backup.Name)}),
|
LabelSelector: labels.SelectorFromSet(map[string]string{velerov1api.BackupNameLabel: label.GetValidName(backup.Name)}),
|
||||||
})
|
})
|
||||||
@@ -115,7 +115,7 @@ func NewDescribeCommand(f client.Factory, use string) *cobra.Command {
|
|||||||
c.Flags().StringVarP(&listOptions.LabelSelector, "selector", "l", listOptions.LabelSelector, "Only show items matching this label selector.")
|
c.Flags().StringVarP(&listOptions.LabelSelector, "selector", "l", listOptions.LabelSelector, "Only show items matching this label selector.")
|
||||||
c.Flags().BoolVar(&details, "details", details, "Display additional detail in the command output.")
|
c.Flags().BoolVar(&details, "details", details, "Display additional detail in the command output.")
|
||||||
c.Flags().BoolVar(&insecureSkipTLSVerify, "insecure-skip-tls-verify", insecureSkipTLSVerify, "If true, the object store's TLS certificate will not be checked for validity. This is insecure and susceptible to man-in-the-middle attacks. Not recommended for production.")
|
c.Flags().BoolVar(&insecureSkipTLSVerify, "insecure-skip-tls-verify", insecureSkipTLSVerify, "If true, the object store's TLS certificate will not be checked for validity. This is insecure and susceptible to man-in-the-middle attacks. Not recommended for production.")
|
||||||
c.Flags().StringVar(&caCertFile, "cacert", caCertFile, "Path to a certificate bundle to use when verifying TLS connections.")
|
c.Flags().StringVar(&caCertFile, "cacert", caCertFile, "Path to a certificate bundle to use when verifying TLS connections. If not specified, the CA certificate from the BackupStorageLocation will be used if available.")
|
||||||
c.Flags().StringVarP(&outputFormat, "output", "o", outputFormat, "Output display format. Valid formats are 'plaintext, json'. 'json' only applies to a single backup")
|
c.Flags().StringVarP(&outputFormat, "output", "o", outputFormat, "Output display format. Valid formats are 'plaintext, json'. 'json' only applies to a single backup")
|
||||||
|
|
||||||
return c
|
return c
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ import (
|
|||||||
velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1"
|
velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1"
|
||||||
"github.com/vmware-tanzu/velero/pkg/client"
|
"github.com/vmware-tanzu/velero/pkg/client"
|
||||||
"github.com/vmware-tanzu/velero/pkg/cmd"
|
"github.com/vmware-tanzu/velero/pkg/cmd"
|
||||||
|
"github.com/vmware-tanzu/velero/pkg/cmd/util/cacert"
|
||||||
"github.com/vmware-tanzu/velero/pkg/cmd/util/downloadrequest"
|
"github.com/vmware-tanzu/velero/pkg/cmd/util/downloadrequest"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -80,7 +81,7 @@ func (o *DownloadOptions) BindFlags(flags *pflag.FlagSet) {
|
|||||||
flags.BoolVar(&o.Force, "force", o.Force, "Forces the download and will overwrite file if it exists already.")
|
flags.BoolVar(&o.Force, "force", o.Force, "Forces the download and will overwrite file if it exists already.")
|
||||||
flags.DurationVar(&o.Timeout, "timeout", o.Timeout, "Maximum time to wait to process download request.")
|
flags.DurationVar(&o.Timeout, "timeout", o.Timeout, "Maximum time to wait to process download request.")
|
||||||
flags.BoolVar(&o.InsecureSkipTLSVerify, "insecure-skip-tls-verify", o.InsecureSkipTLSVerify, "If true, the object store's TLS certificate will not be checked for validity. This is insecure and susceptible to man-in-the-middle attacks. Not recommended for production.")
|
flags.BoolVar(&o.InsecureSkipTLSVerify, "insecure-skip-tls-verify", o.InsecureSkipTLSVerify, "If true, the object store's TLS certificate will not be checked for validity. This is insecure and susceptible to man-in-the-middle attacks. Not recommended for production.")
|
||||||
flags.StringVar(&o.caCertFile, "cacert", o.caCertFile, "Path to a certificate bundle to use when verifying TLS connections.")
|
flags.StringVar(&o.caCertFile, "cacert", o.caCertFile, "Path to a certificate bundle to use when verifying TLS connections. If not specified, the CA certificate from the BackupStorageLocation will be used if available.")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o *DownloadOptions) Validate(c *cobra.Command, args []string, f client.Factory) error {
|
func (o *DownloadOptions) Validate(c *cobra.Command, args []string, f client.Factory) error {
|
||||||
@@ -88,7 +89,7 @@ func (o *DownloadOptions) Validate(c *cobra.Command, args []string, f client.Fac
|
|||||||
cmd.CheckError(err)
|
cmd.CheckError(err)
|
||||||
|
|
||||||
backup := new(velerov1api.Backup)
|
backup := new(velerov1api.Backup)
|
||||||
if err := kbClient.Get(context.TODO(), controllerclient.ObjectKey{Namespace: f.Namespace(), Name: o.Name}, backup); err != nil {
|
if err := kbClient.Get(context.Background(), controllerclient.ObjectKey{Namespace: f.Namespace(), Name: o.Name}, backup); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -118,13 +119,27 @@ func (o *DownloadOptions) Run(c *cobra.Command, f client.Factory) error {
|
|||||||
kbClient, err := f.KubebuilderClient()
|
kbClient, err := f.KubebuilderClient()
|
||||||
cmd.CheckError(err)
|
cmd.CheckError(err)
|
||||||
|
|
||||||
|
// Get the backup to fetch BSL cacert
|
||||||
|
backup := new(velerov1api.Backup)
|
||||||
|
if err := kbClient.Get(context.Background(), controllerclient.ObjectKey{Namespace: f.Namespace(), Name: o.Name}, backup); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get BSL cacert if available
|
||||||
|
bslCACert, err := cacert.GetCACertFromBackup(context.Background(), kbClient, f.Namespace(), backup)
|
||||||
|
if err != nil {
|
||||||
|
// Log the error but don't fail - we can still try to download without the BSL cacert
|
||||||
|
fmt.Fprintf(os.Stderr, "WARNING: Error getting cacert from BSL: %v\n", err)
|
||||||
|
bslCACert = ""
|
||||||
|
}
|
||||||
|
|
||||||
backupDest, err := os.OpenFile(o.Output, o.writeOptions, 0600)
|
backupDest, err := os.OpenFile(o.Output, o.writeOptions, 0600)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
defer backupDest.Close()
|
defer backupDest.Close()
|
||||||
|
|
||||||
err = downloadrequest.Stream(context.Background(), kbClient, f.Namespace(), o.Name, velerov1api.DownloadTargetKindBackupContents, backupDest, o.Timeout, o.InsecureSkipTLSVerify, o.caCertFile)
|
err = downloadrequest.StreamWithBSLCACert(context.Background(), kbClient, f.Namespace(), o.Name, velerov1api.DownloadTargetKindBackupContents, backupDest, o.Timeout, o.InsecureSkipTLSVerify, o.caCertFile, bslCACert)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
os.Remove(o.Output)
|
os.Remove(o.Output)
|
||||||
cmd.CheckError(err)
|
cmd.CheckError(err)
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ import (
|
|||||||
velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1"
|
velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1"
|
||||||
"github.com/vmware-tanzu/velero/pkg/client"
|
"github.com/vmware-tanzu/velero/pkg/client"
|
||||||
"github.com/vmware-tanzu/velero/pkg/cmd"
|
"github.com/vmware-tanzu/velero/pkg/cmd"
|
||||||
|
"github.com/vmware-tanzu/velero/pkg/cmd/util/cacert"
|
||||||
"github.com/vmware-tanzu/velero/pkg/cmd/util/downloadrequest"
|
"github.com/vmware-tanzu/velero/pkg/cmd/util/downloadrequest"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -62,7 +63,7 @@ func (l *LogsOptions) BindFlags(flags *pflag.FlagSet) {
|
|||||||
|
|
||||||
func (l *LogsOptions) Run(c *cobra.Command, f client.Factory) error {
|
func (l *LogsOptions) Run(c *cobra.Command, f client.Factory) error {
|
||||||
backup := new(velerov1api.Backup)
|
backup := new(velerov1api.Backup)
|
||||||
err := l.Client.Get(context.TODO(), kbclient.ObjectKey{Namespace: f.Namespace(), Name: l.BackupName}, backup)
|
err := l.Client.Get(context.Background(), kbclient.ObjectKey{Namespace: f.Namespace(), Name: l.BackupName}, backup)
|
||||||
if apierrors.IsNotFound(err) {
|
if apierrors.IsNotFound(err) {
|
||||||
return fmt.Errorf("backup %q does not exist", l.BackupName)
|
return fmt.Errorf("backup %q does not exist", l.BackupName)
|
||||||
} else if err != nil {
|
} else if err != nil {
|
||||||
@@ -77,7 +78,15 @@ func (l *LogsOptions) Run(c *cobra.Command, f client.Factory) error {
|
|||||||
"until the backup has a phase of Completed or Failed and try again", l.BackupName)
|
"until the backup has a phase of Completed or Failed and try again", l.BackupName)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = downloadrequest.Stream(context.Background(), l.Client, f.Namespace(), l.BackupName, velerov1api.DownloadTargetKindBackupLog, os.Stdout, l.Timeout, l.InsecureSkipTLSVerify, l.CaCertFile)
|
// Get BSL cacert if available
|
||||||
|
bslCACert, err := cacert.GetCACertFromBackup(context.Background(), l.Client, f.Namespace(), backup)
|
||||||
|
if err != nil {
|
||||||
|
// Log the error but don't fail - we can still try to download without the BSL cacert
|
||||||
|
fmt.Fprintf(os.Stderr, "WARNING: Error getting cacert from BSL: %v\n", err)
|
||||||
|
bslCACert = ""
|
||||||
|
}
|
||||||
|
|
||||||
|
err = downloadrequest.StreamWithBSLCACert(context.Background(), l.Client, f.Namespace(), l.BackupName, velerov1api.DownloadTargetKindBackupLog, os.Stdout, l.Timeout, l.InsecureSkipTLSVerify, l.CaCertFile, bslCACert)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -17,7 +17,13 @@ limitations under the License.
|
|||||||
package backup
|
package backup
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
|
"compress/gzip"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"net/http/httptest"
|
||||||
|
"os"
|
||||||
"strconv"
|
"strconv"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
@@ -31,6 +37,7 @@ import (
|
|||||||
"github.com/vmware-tanzu/velero/pkg/builder"
|
"github.com/vmware-tanzu/velero/pkg/builder"
|
||||||
factorymocks "github.com/vmware-tanzu/velero/pkg/client/mocks"
|
factorymocks "github.com/vmware-tanzu/velero/pkg/client/mocks"
|
||||||
cmdtest "github.com/vmware-tanzu/velero/pkg/cmd/test"
|
cmdtest "github.com/vmware-tanzu/velero/pkg/cmd/test"
|
||||||
|
"github.com/vmware-tanzu/velero/pkg/cmd/util/cacert"
|
||||||
velerotest "github.com/vmware-tanzu/velero/pkg/test"
|
velerotest "github.com/vmware-tanzu/velero/pkg/test"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -164,4 +171,207 @@ func TestNewLogsCommand(t *testing.T) {
|
|||||||
err := l.Complete([]string{""}, f)
|
err := l.Complete([]string{""}, f)
|
||||||
require.Equal(t, "test error", err.Error())
|
require.Equal(t, "test error", err.Error())
|
||||||
})
|
})
|
||||||
|
|
||||||
|
t.Run("Backup with BSL cacert test", func(t *testing.T) {
|
||||||
|
backupName := "bk-logs-with-cacert"
|
||||||
|
bslName := "test-bsl"
|
||||||
|
expectedCACert := "test-cacert-content"
|
||||||
|
expectedLogContent := "test backup log content"
|
||||||
|
|
||||||
|
// create a factory
|
||||||
|
f := &factorymocks.Factory{}
|
||||||
|
|
||||||
|
kbClient := velerotest.NewFakeControllerRuntimeClient(t)
|
||||||
|
|
||||||
|
// Create BSL with cacert
|
||||||
|
bsl := builder.ForBackupStorageLocation(cmdtest.VeleroNameSpace, bslName).
|
||||||
|
Provider("aws").
|
||||||
|
Bucket("test-bucket").
|
||||||
|
CACert([]byte(expectedCACert)).
|
||||||
|
Result()
|
||||||
|
err := kbClient.Create(t.Context(), bsl, &kbclient.CreateOptions{})
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
// Create backup referencing the BSL
|
||||||
|
backup := builder.ForBackup(cmdtest.VeleroNameSpace, backupName).
|
||||||
|
Phase(velerov1api.BackupPhaseCompleted).
|
||||||
|
StorageLocation(bslName).
|
||||||
|
Result()
|
||||||
|
err = kbClient.Create(t.Context(), backup, &kbclient.CreateOptions{})
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
f.On("Namespace").Return(cmdtest.VeleroNameSpace)
|
||||||
|
f.On("KubebuilderClient").Return(kbClient, nil)
|
||||||
|
|
||||||
|
c := NewLogsCommand(f)
|
||||||
|
assert.Equal(t, "Get backup logs", c.Short)
|
||||||
|
|
||||||
|
l := NewLogsOptions()
|
||||||
|
flags := new(flag.FlagSet)
|
||||||
|
l.BindFlags(flags)
|
||||||
|
err = l.Complete([]string{backupName}, f)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
// Verify that the BSL cacert can be fetched correctly before running the command
|
||||||
|
fetchedBackup := &velerov1api.Backup{}
|
||||||
|
err = kbClient.Get(t.Context(), kbclient.ObjectKey{Namespace: cmdtest.VeleroNameSpace, Name: backupName}, fetchedBackup)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
// Test the cacert fetching logic directly
|
||||||
|
cacertValue, err := cacert.GetCACertFromBackup(t.Context(), kbClient, cmdtest.VeleroNameSpace, fetchedBackup)
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.Equal(t, expectedCACert, cacertValue, "BSL cacert should be retrieved correctly")
|
||||||
|
|
||||||
|
// Create a mock HTTP server to serve the log content
|
||||||
|
mockServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
// For logs, we need to gzip the content
|
||||||
|
gzipWriter := gzip.NewWriter(w)
|
||||||
|
defer gzipWriter.Close()
|
||||||
|
gzipWriter.Write([]byte(expectedLogContent))
|
||||||
|
}))
|
||||||
|
defer mockServer.Close()
|
||||||
|
|
||||||
|
// Mock the download request controller by updating DownloadRequests
|
||||||
|
go func() {
|
||||||
|
time.Sleep(50 * time.Millisecond) // Wait a bit for the request to be created
|
||||||
|
|
||||||
|
// List all DownloadRequests
|
||||||
|
downloadRequestList := &velerov1api.DownloadRequestList{}
|
||||||
|
if err := kbClient.List(t.Context(), downloadRequestList, &kbclient.ListOptions{
|
||||||
|
Namespace: cmdtest.VeleroNameSpace,
|
||||||
|
}); err == nil {
|
||||||
|
// Update each download request with the mock server URL
|
||||||
|
for _, dr := range downloadRequestList.Items {
|
||||||
|
if dr.Spec.Target.Kind == velerov1api.DownloadTargetKindBackupLog &&
|
||||||
|
dr.Spec.Target.Name == backupName {
|
||||||
|
dr.Status.DownloadURL = mockServer.URL
|
||||||
|
dr.Status.Phase = velerov1api.DownloadRequestPhaseProcessed
|
||||||
|
kbClient.Update(t.Context(), &dr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
// Capture the output
|
||||||
|
var logOutput bytes.Buffer
|
||||||
|
// Temporarily redirect stdout
|
||||||
|
oldStdout := os.Stdout
|
||||||
|
r, w, _ := os.Pipe()
|
||||||
|
os.Stdout = w
|
||||||
|
|
||||||
|
// Run the logs command - it should now succeed
|
||||||
|
l.Timeout = 5 * time.Second
|
||||||
|
err = l.Run(c, f)
|
||||||
|
|
||||||
|
// Restore stdout and read the output
|
||||||
|
w.Close()
|
||||||
|
os.Stdout = oldStdout
|
||||||
|
io.Copy(&logOutput, r)
|
||||||
|
|
||||||
|
// Verify the command succeeded and output is correct
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.Equal(t, expectedLogContent, logOutput.String())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBSLCACertBehavior(t *testing.T) {
|
||||||
|
t.Run("Backup with BSL without cacert test", func(t *testing.T) {
|
||||||
|
backupName := "bk-logs-without-cacert"
|
||||||
|
bslName := "test-bsl-no-cacert"
|
||||||
|
|
||||||
|
// create a factory
|
||||||
|
f := &factorymocks.Factory{}
|
||||||
|
|
||||||
|
kbClient := velerotest.NewFakeControllerRuntimeClient(t)
|
||||||
|
|
||||||
|
// Create BSL without cacert
|
||||||
|
bsl := builder.ForBackupStorageLocation(cmdtest.VeleroNameSpace, bslName).
|
||||||
|
Provider("aws").
|
||||||
|
Bucket("test-bucket").
|
||||||
|
// No CACert() call - BSL will have no cacert
|
||||||
|
Result()
|
||||||
|
err := kbClient.Create(t.Context(), bsl, &kbclient.CreateOptions{})
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
// Create backup referencing the BSL
|
||||||
|
backup := builder.ForBackup(cmdtest.VeleroNameSpace, backupName).
|
||||||
|
Phase(velerov1api.BackupPhaseCompleted).
|
||||||
|
StorageLocation(bslName).
|
||||||
|
Result()
|
||||||
|
err = kbClient.Create(t.Context(), backup, &kbclient.CreateOptions{})
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
f.On("Namespace").Return(cmdtest.VeleroNameSpace)
|
||||||
|
f.On("KubebuilderClient").Return(kbClient, nil)
|
||||||
|
|
||||||
|
c := NewLogsCommand(f)
|
||||||
|
assert.Equal(t, "Get backup logs", c.Short)
|
||||||
|
|
||||||
|
l := NewLogsOptions()
|
||||||
|
flags := new(flag.FlagSet)
|
||||||
|
l.BindFlags(flags)
|
||||||
|
err = l.Complete([]string{backupName}, f)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
// Verify that the BSL cacert returns empty string when not present
|
||||||
|
fetchedBackup := &velerov1api.Backup{}
|
||||||
|
err = kbClient.Get(t.Context(), kbclient.ObjectKey{Namespace: cmdtest.VeleroNameSpace, Name: backupName}, fetchedBackup)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
// Test the cacert fetching logic directly
|
||||||
|
cacertValue, err := cacert.GetCACertFromBackup(t.Context(), kbClient, cmdtest.VeleroNameSpace, fetchedBackup)
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.Empty(t, cacertValue, "BSL cacert should be empty when not configured")
|
||||||
|
|
||||||
|
// The command should still work without cacert
|
||||||
|
l.Timeout = 100 * time.Millisecond
|
||||||
|
err = l.Run(c, f)
|
||||||
|
require.Error(t, err)
|
||||||
|
// The error should be about download request timeout, not about cacert fetching
|
||||||
|
assert.Contains(t, err.Error(), "download")
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("Backup with nonexistent BSL test", func(t *testing.T) {
|
||||||
|
backupName := "bk-logs-with-missing-bsl"
|
||||||
|
bslName := "nonexistent-bsl"
|
||||||
|
|
||||||
|
// create a factory
|
||||||
|
f := &factorymocks.Factory{}
|
||||||
|
|
||||||
|
kbClient := velerotest.NewFakeControllerRuntimeClient(t)
|
||||||
|
|
||||||
|
// Create backup referencing a BSL that doesn't exist
|
||||||
|
backup := builder.ForBackup(cmdtest.VeleroNameSpace, backupName).
|
||||||
|
Phase(velerov1api.BackupPhaseCompleted).
|
||||||
|
StorageLocation(bslName).
|
||||||
|
Result()
|
||||||
|
err := kbClient.Create(t.Context(), backup, &kbclient.CreateOptions{})
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
f.On("Namespace").Return(cmdtest.VeleroNameSpace)
|
||||||
|
f.On("KubebuilderClient").Return(kbClient, nil)
|
||||||
|
|
||||||
|
c := NewLogsCommand(f)
|
||||||
|
l := NewLogsOptions()
|
||||||
|
flags := new(flag.FlagSet)
|
||||||
|
l.BindFlags(flags)
|
||||||
|
err = l.Complete([]string{backupName}, f)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
// Verify that the BSL cacert returns empty string when BSL doesn't exist
|
||||||
|
fetchedBackup := &velerov1api.Backup{}
|
||||||
|
err = kbClient.Get(t.Context(), kbclient.ObjectKey{Namespace: cmdtest.VeleroNameSpace, Name: backupName}, fetchedBackup)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
// Test the cacert fetching logic directly - should not error when BSL is missing
|
||||||
|
cacertValue, err := cacert.GetCACertFromBackup(t.Context(), kbClient, cmdtest.VeleroNameSpace, fetchedBackup)
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.Empty(t, cacertValue, "BSL cacert should be empty when BSL doesn't exist")
|
||||||
|
|
||||||
|
// The command should still try to run even without BSL
|
||||||
|
l.Timeout = 100 * time.Millisecond
|
||||||
|
err = l.Run(c, f)
|
||||||
|
require.Error(t, err)
|
||||||
|
assert.Contains(t, err.Error(), "download")
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ import (
|
|||||||
velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1"
|
velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1"
|
||||||
"github.com/vmware-tanzu/velero/pkg/client"
|
"github.com/vmware-tanzu/velero/pkg/client"
|
||||||
"github.com/vmware-tanzu/velero/pkg/cmd"
|
"github.com/vmware-tanzu/velero/pkg/cmd"
|
||||||
|
"github.com/vmware-tanzu/velero/pkg/cmd/util/cacert"
|
||||||
"github.com/vmware-tanzu/velero/pkg/cmd/util/downloadrequest"
|
"github.com/vmware-tanzu/velero/pkg/cmd/util/downloadrequest"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -53,7 +54,7 @@ func NewLogsCommand(f client.Factory) *cobra.Command {
|
|||||||
cmd.CheckError(err)
|
cmd.CheckError(err)
|
||||||
|
|
||||||
restore := new(velerov1api.Restore)
|
restore := new(velerov1api.Restore)
|
||||||
err = kbClient.Get(context.TODO(), ctrlclient.ObjectKey{Namespace: f.Namespace(), Name: restoreName}, restore)
|
err = kbClient.Get(context.Background(), ctrlclient.ObjectKey{Namespace: f.Namespace(), Name: restoreName}, restore)
|
||||||
if apierrors.IsNotFound(err) {
|
if apierrors.IsNotFound(err) {
|
||||||
cmd.Exit("Restore %q does not exist.", restoreName)
|
cmd.Exit("Restore %q does not exist.", restoreName)
|
||||||
} else if err != nil {
|
} else if err != nil {
|
||||||
@@ -68,14 +69,22 @@ func NewLogsCommand(f client.Factory) *cobra.Command {
|
|||||||
"until the restore has a phase of Completed or Failed and try again.", restoreName)
|
"until the restore has a phase of Completed or Failed and try again.", restoreName)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = downloadrequest.Stream(context.Background(), kbClient, f.Namespace(), restoreName, velerov1api.DownloadTargetKindRestoreLog, os.Stdout, timeout, insecureSkipTLSVerify, caCertFile)
|
// Get BSL cacert if available
|
||||||
|
bslCACert, err := cacert.GetCACertFromRestore(context.Background(), kbClient, f.Namespace(), restore)
|
||||||
|
if err != nil {
|
||||||
|
// Log the error but don't fail - we can still try to download without the BSL cacert
|
||||||
|
fmt.Fprintf(os.Stderr, "WARNING: Error getting cacert from BSL: %v\n", err)
|
||||||
|
bslCACert = ""
|
||||||
|
}
|
||||||
|
|
||||||
|
err = downloadrequest.StreamWithBSLCACert(context.Background(), kbClient, f.Namespace(), restoreName, velerov1api.DownloadTargetKindRestoreLog, os.Stdout, timeout, insecureSkipTLSVerify, caCertFile, bslCACert)
|
||||||
cmd.CheckError(err)
|
cmd.CheckError(err)
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
c.Flags().DurationVar(&timeout, "timeout", timeout, "How long to wait to receive logs.")
|
c.Flags().DurationVar(&timeout, "timeout", timeout, "How long to wait to receive logs.")
|
||||||
c.Flags().BoolVar(&insecureSkipTLSVerify, "insecure-skip-tls-verify", insecureSkipTLSVerify, "If true, the object store's TLS certificate will not be checked for validity. This is insecure and susceptible to man-in-the-middle attacks. Not recommended for production.")
|
c.Flags().BoolVar(&insecureSkipTLSVerify, "insecure-skip-tls-verify", insecureSkipTLSVerify, "If true, the object store's TLS certificate will not be checked for validity. This is insecure and susceptible to man-in-the-middle attacks. Not recommended for production.")
|
||||||
c.Flags().StringVar(&caCertFile, "cacert", caCertFile, "Path to a certificate bundle to use when verifying TLS connections.")
|
c.Flags().StringVar(&caCertFile, "cacert", caCertFile, "Path to a certificate bundle to use when verifying TLS connections. If not specified, the CA certificate from the BackupStorageLocation will be used if available.")
|
||||||
|
|
||||||
return c
|
return c
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,13 +19,18 @@ package restore
|
|||||||
import (
|
import (
|
||||||
"os"
|
"os"
|
||||||
"testing"
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
flag "github.com/spf13/pflag"
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
kbclient "sigs.k8s.io/controller-runtime/pkg/client"
|
||||||
|
|
||||||
|
velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1"
|
||||||
|
"github.com/vmware-tanzu/velero/pkg/builder"
|
||||||
factorymocks "github.com/vmware-tanzu/velero/pkg/client/mocks"
|
factorymocks "github.com/vmware-tanzu/velero/pkg/client/mocks"
|
||||||
cmdtest "github.com/vmware-tanzu/velero/pkg/cmd/test"
|
cmdtest "github.com/vmware-tanzu/velero/pkg/cmd/test"
|
||||||
|
"github.com/vmware-tanzu/velero/pkg/cmd/util/cacert"
|
||||||
|
velerotest "github.com/vmware-tanzu/velero/pkg/test"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestNewLogsCommand(t *testing.T) {
|
func TestNewLogsCommand(t *testing.T) {
|
||||||
@@ -35,15 +40,124 @@ func TestNewLogsCommand(t *testing.T) {
|
|||||||
|
|
||||||
c := NewLogsCommand(f)
|
c := NewLogsCommand(f)
|
||||||
require.Equal(t, "Get restore logs", c.Short)
|
require.Equal(t, "Get restore logs", c.Short)
|
||||||
flags := new(flag.FlagSet)
|
|
||||||
|
|
||||||
timeout := "1m0s"
|
// Test flag parsing
|
||||||
|
timeout := "1s"
|
||||||
insecureSkipTLSVerify := "true"
|
insecureSkipTLSVerify := "true"
|
||||||
caCertFile := "testing"
|
caCertFile := "testing"
|
||||||
|
|
||||||
flags.Parse([]string{"--timeout", timeout})
|
c.Flags().Set("timeout", timeout)
|
||||||
flags.Parse([]string{"--insecure-skip-tls-verify", insecureSkipTLSVerify})
|
c.Flags().Set("insecure-skip-tls-verify", insecureSkipTLSVerify)
|
||||||
flags.Parse([]string{"--cacert", caCertFile})
|
c.Flags().Set("cacert", caCertFile)
|
||||||
|
|
||||||
|
timeoutFlag, _ := c.Flags().GetDuration("timeout")
|
||||||
|
require.Equal(t, 1*time.Second, timeoutFlag)
|
||||||
|
|
||||||
|
insecureFlag, _ := c.Flags().GetBool("insecure-skip-tls-verify")
|
||||||
|
require.True(t, insecureFlag)
|
||||||
|
|
||||||
|
caCertFlag, _ := c.Flags().GetString("cacert")
|
||||||
|
require.Equal(t, caCertFile, caCertFlag)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("Restore not complete test", func(t *testing.T) {
|
||||||
|
restoreName := "rs-logs-1"
|
||||||
|
|
||||||
|
// create a factory
|
||||||
|
f := &factorymocks.Factory{}
|
||||||
|
|
||||||
|
kbClient := velerotest.NewFakeControllerRuntimeClient(t)
|
||||||
|
restore := builder.ForRestore(cmdtest.VeleroNameSpace, restoreName).Result()
|
||||||
|
err := kbClient.Create(t.Context(), restore, &kbclient.CreateOptions{})
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
f.On("Namespace").Return(cmdtest.VeleroNameSpace)
|
||||||
|
f.On("KubebuilderClient").Return(kbClient, nil)
|
||||||
|
|
||||||
|
c := NewLogsCommand(f)
|
||||||
|
assert.Equal(t, "Get restore logs", c.Short)
|
||||||
|
|
||||||
|
// The restore command exits with an error message when restore is not complete
|
||||||
|
// We can't easily test this since it calls cmd.Exit, which exits the process
|
||||||
|
// So we'll skip this test case
|
||||||
|
t.Skip("Cannot test restore not complete case due to cmd.Exit() call")
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("Restore not exist test", func(t *testing.T) {
|
||||||
|
// create a factory
|
||||||
|
f := &factorymocks.Factory{}
|
||||||
|
|
||||||
|
kbClient := velerotest.NewFakeControllerRuntimeClient(t)
|
||||||
|
|
||||||
|
f.On("Namespace").Return(cmdtest.VeleroNameSpace)
|
||||||
|
f.On("KubebuilderClient").Return(kbClient, nil)
|
||||||
|
|
||||||
|
c := NewLogsCommand(f)
|
||||||
|
assert.Equal(t, "Get restore logs", c.Short)
|
||||||
|
|
||||||
|
// The restore command exits with an error message when restore doesn't exist
|
||||||
|
// We can't easily test this since it calls cmd.Exit, which exits the process
|
||||||
|
// So we'll skip this test case
|
||||||
|
t.Skip("Cannot test restore not exist case due to cmd.Exit() call")
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("Restore with BSL cacert test", func(t *testing.T) {
|
||||||
|
restoreName := "rs-logs-with-cacert"
|
||||||
|
backupName := "bk-for-restore"
|
||||||
|
bslName := "test-bsl"
|
||||||
|
|
||||||
|
// create a factory
|
||||||
|
f := &factorymocks.Factory{}
|
||||||
|
|
||||||
|
kbClient := velerotest.NewFakeControllerRuntimeClient(t)
|
||||||
|
|
||||||
|
// Create BSL with cacert
|
||||||
|
bsl := builder.ForBackupStorageLocation(cmdtest.VeleroNameSpace, bslName).
|
||||||
|
Provider("aws").
|
||||||
|
Bucket("test-bucket").
|
||||||
|
CACert([]byte("test-cacert-content")).
|
||||||
|
Result()
|
||||||
|
err := kbClient.Create(t.Context(), bsl, &kbclient.CreateOptions{})
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
// Create backup referencing the BSL
|
||||||
|
backup := builder.ForBackup(cmdtest.VeleroNameSpace, backupName).
|
||||||
|
StorageLocation(bslName).
|
||||||
|
Result()
|
||||||
|
err = kbClient.Create(t.Context(), backup, &kbclient.CreateOptions{})
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
// Create restore referencing the backup
|
||||||
|
restore := builder.ForRestore(cmdtest.VeleroNameSpace, restoreName).
|
||||||
|
Phase(velerov1api.RestorePhaseCompleted).
|
||||||
|
Backup(backupName).
|
||||||
|
Result()
|
||||||
|
err = kbClient.Create(t.Context(), restore, &kbclient.CreateOptions{})
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
f.On("Namespace").Return(cmdtest.VeleroNameSpace)
|
||||||
|
f.On("KubebuilderClient").Return(kbClient, nil)
|
||||||
|
|
||||||
|
c := NewLogsCommand(f)
|
||||||
|
assert.Equal(t, "Get restore logs", c.Short)
|
||||||
|
|
||||||
|
// We can verify that BSL cacert fetching logic is in place
|
||||||
|
// The actual command will call downloadrequest which requires a controller
|
||||||
|
// to be running, so we'll just verify the command structure
|
||||||
|
require.NotNil(t, c.Run)
|
||||||
|
|
||||||
|
// Verify the BSL cacert can be fetched
|
||||||
|
cacertValue, err := cacert.GetCACertFromRestore(t.Context(), kbClient, f.Namespace(), restore)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, "test-cacert-content", cacertValue)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("CLI execution test", func(t *testing.T) {
|
||||||
|
// create a factory
|
||||||
|
f := &factorymocks.Factory{}
|
||||||
|
|
||||||
|
c := NewLogsCommand(f)
|
||||||
|
require.Equal(t, "Get restore logs", c.Short)
|
||||||
|
|
||||||
if os.Getenv(cmdtest.CaptureFlag) == "1" {
|
if os.Getenv(cmdtest.CaptureFlag) == "1" {
|
||||||
c.SetArgs([]string{"test"})
|
c.SetArgs([]string{"test"})
|
||||||
|
|||||||
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,
|
timeout time.Duration,
|
||||||
insecureSkipTLSVerify bool,
|
insecureSkipTLSVerify bool,
|
||||||
caCertFile string,
|
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 {
|
) error {
|
||||||
ctx, cancel := context.WithTimeout(ctx, timeout)
|
ctx, cancel := context.WithTimeout(ctx, timeout)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
@@ -59,7 +75,7 @@ func Stream(
|
|||||||
return err
|
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
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -109,21 +125,35 @@ func download(
|
|||||||
w io.Writer,
|
w io.Writer,
|
||||||
insecureSkipTLSVerify bool,
|
insecureSkipTLSVerify bool,
|
||||||
caCertFile string,
|
caCertFile string,
|
||||||
|
caCertByteString string,
|
||||||
) error {
|
) error {
|
||||||
var caPool *x509.CertPool
|
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 {
|
if len(caCertFile) > 0 {
|
||||||
caCert, err := os.ReadFile(caCertFile)
|
caCert, err := os.ReadFile(caCertFile)
|
||||||
if err != nil {
|
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
|
} else if len(caCertByteString) > 0 {
|
||||||
// if it's available, otherwise create a new pool just
|
// If no caCertFile specified, use BSL cert if available
|
||||||
// for this.
|
caPool.AppendCertsFromPEM([]byte(caCertByteString))
|
||||||
caPool, err = x509.SystemCertPool()
|
|
||||||
if err != nil {
|
|
||||||
caPool = x509.NewCertPool()
|
|
||||||
}
|
|
||||||
caPool.AppendCertsFromPEM(caCert)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
defaultTransport := http.DefaultTransport.(*http.Transport)
|
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"
|
veleroapishared "github.com/vmware-tanzu/velero/pkg/apis/velero/shared"
|
||||||
velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1"
|
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/cmd/util/downloadrequest"
|
||||||
"github.com/vmware-tanzu/velero/pkg/itemoperation"
|
"github.com/vmware-tanzu/velero/pkg/itemoperation"
|
||||||
|
|
||||||
@@ -377,8 +378,16 @@ func describeBackupItemOperations(ctx context.Context, kbClient kbclient.Client,
|
|||||||
return
|
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)
|
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)
|
d.Printf("Backup Item Operations:\t<error getting operation info: %v>\n", err)
|
||||||
return
|
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) {
|
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)
|
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 {
|
if err == downloadrequest.ErrNotFound {
|
||||||
// the backup resource list could be missing if (other reasons may exist as well):
|
// the backup resource list could be missing if (other reasons may exist as well):
|
||||||
// - the backup was taken prior to v1.1; or
|
// - the backup was taken prior to v1.1; or
|
||||||
@@ -444,20 +461,28 @@ func describeBackupVolumes(
|
|||||||
) {
|
) {
|
||||||
d.Println("Backup Volumes:")
|
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{}
|
nativeSnapshots := []*volume.BackupVolumeInfo{}
|
||||||
csiSnapshots := []*volume.BackupVolumeInfo{}
|
csiSnapshots := []*volume.BackupVolumeInfo{}
|
||||||
legacyInfoSource := false
|
legacyInfoSource := false
|
||||||
|
|
||||||
buf := new(bytes.Buffer)
|
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 {
|
if err == downloadrequest.ErrNotFound {
|
||||||
nativeSnapshots, err = retrieveNativeSnapshotLegacy(ctx, kbClient, backup, insecureSkipTLSVerify, caCertPath)
|
nativeSnapshots, err = retrieveNativeSnapshotLegacy(ctx, kbClient, backup, insecureSkipTLSVerify, caCertPath, bslCACert)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
d.Printf("\t<error concluding native snapshot info: %v>\n", err)
|
d.Printf("\t<error concluding native snapshot info: %v>\n", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
csiSnapshots, err = retrieveCSISnapshotLegacy(ctx, kbClient, backup, insecureSkipTLSVerify, caCertPath)
|
csiSnapshots, err = retrieveCSISnapshotLegacy(ctx, kbClient, backup, insecureSkipTLSVerify, caCertPath, bslCACert)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
d.Printf("\t<error concluding CSI snapshot info: %v>\n", err)
|
d.Printf("\t<error concluding CSI snapshot info: %v>\n", err)
|
||||||
return
|
return
|
||||||
@@ -493,7 +518,7 @@ func describeBackupVolumes(
|
|||||||
describePodVolumeBackups(d, details, podVolumeBackupCRs)
|
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
|
status := backup.Status
|
||||||
nativeSnapshots := []*volume.BackupVolumeInfo{}
|
nativeSnapshots := []*volume.BackupVolumeInfo{}
|
||||||
|
|
||||||
@@ -502,7 +527,7 @@ func retrieveNativeSnapshotLegacy(ctx context.Context, kbClient kbclient.Client,
|
|||||||
}
|
}
|
||||||
|
|
||||||
buf := new(bytes.Buffer)
|
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")
|
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
|
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
|
status := backup.Status
|
||||||
csiSnapshots := []*volume.BackupVolumeInfo{}
|
csiSnapshots := []*volume.BackupVolumeInfo{}
|
||||||
|
|
||||||
@@ -540,7 +565,7 @@ func retrieveCSISnapshotLegacy(ctx context.Context, kbClient kbclient.Client, ba
|
|||||||
}
|
}
|
||||||
|
|
||||||
vsBuf := new(bytes.Buffer)
|
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 {
|
if err != nil {
|
||||||
return csiSnapshots, errors.Wrapf(err, "error to download vs list")
|
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)
|
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 {
|
if err != nil {
|
||||||
return csiSnapshots, errors.Wrapf(err, "error to download vsc list")
|
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
|
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 buf bytes.Buffer
|
||||||
var resultMap map[string]results.Result
|
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.
|
// 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.
|
// 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 {
|
if err == downloadrequest.ErrNotFound {
|
||||||
d.Printf("Errors:\t%d\n", backup.Status.Errors)
|
d.Printf("Errors:\t%d\n", backup.Status.Errors)
|
||||||
d.Printf("Warnings:\t%d\n", backup.Status.Warnings)
|
d.Printf("Warnings:\t%d\n", backup.Status.Warnings)
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ import (
|
|||||||
|
|
||||||
"github.com/vmware-tanzu/velero/internal/volume"
|
"github.com/vmware-tanzu/velero/internal/volume"
|
||||||
velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1"
|
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/cmd/util/downloadrequest"
|
||||||
"github.com/vmware-tanzu/velero/pkg/util/results"
|
"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) {
|
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)
|
// 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 'errorGettingResourceList' gives specific error message when it fails to get resources list
|
||||||
// the field of 'resourceList' lists the rearranged resources
|
// the field of 'resourceList' lists the rearranged resources
|
||||||
buf := new(bytes.Buffer)
|
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 {
|
if err == downloadrequest.ErrNotFound {
|
||||||
// the backup resource list could be missing if (other reasons may exist as well):
|
// the backup resource list could be missing if (other reasons may exist as well):
|
||||||
// - the backup was taken prior to v1.1; or
|
// - 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) {
|
insecureSkipTLSVerify bool, caCertPath string, podVolumeBackupCRs []velerov1api.PodVolumeBackup, backupStatusInfo map[string]any) {
|
||||||
backupVolumes := make(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{}
|
nativeSnapshots := []*volume.BackupVolumeInfo{}
|
||||||
csiSnapshots := []*volume.BackupVolumeInfo{}
|
csiSnapshots := []*volume.BackupVolumeInfo{}
|
||||||
legacyInfoSource := false
|
legacyInfoSource := false
|
||||||
|
|
||||||
buf := new(bytes.Buffer)
|
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 {
|
if err == downloadrequest.ErrNotFound {
|
||||||
nativeSnapshots, err = retrieveNativeSnapshotLegacy(ctx, kbClient, backup, insecureSkipTLSVerify, caCertPath)
|
nativeSnapshots, err = retrieveNativeSnapshotLegacy(ctx, kbClient, backup, insecureSkipTLSVerify, caCertPath, bslCACert)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
backupVolumes["errorConcludeNativeSnapshot"] = fmt.Sprintf("error concluding native snapshot info: %v", err)
|
backupVolumes["errorConcludeNativeSnapshot"] = fmt.Sprintf("error concluding native snapshot info: %v", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
csiSnapshots, err = retrieveCSISnapshotLegacy(ctx, kbClient, backup, insecureSkipTLSVerify, caCertPath)
|
csiSnapshots, err = retrieveCSISnapshotLegacy(ctx, kbClient, backup, insecureSkipTLSVerify, caCertPath, bslCACert)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
backupVolumes["errorConcludeCSISnapshot"] = fmt.Sprintf("error concluding CSI snapshot info: %v", err)
|
backupVolumes["errorConcludeCSISnapshot"] = fmt.Sprintf("error concluding CSI snapshot info: %v", err)
|
||||||
return
|
return
|
||||||
@@ -538,6 +555,16 @@ func DescribeBackupResultsInSF(ctx context.Context, kbClient kbclient.Client, d
|
|||||||
return
|
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 buf bytes.Buffer
|
||||||
var resultMap map[string]results.Result
|
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.
|
// 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.
|
// 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 {
|
if err == downloadrequest.ErrNotFound {
|
||||||
errors["count"] = backup.Status.Errors
|
errors["count"] = backup.Status.Errors
|
||||||
warnings["count"] = backup.Status.Warnings
|
warnings["count"] = backup.Status.Warnings
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ import (
|
|||||||
"github.com/fatih/color"
|
"github.com/fatih/color"
|
||||||
|
|
||||||
velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1"
|
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/cmd/util/downloadrequest"
|
||||||
"github.com/vmware-tanzu/velero/pkg/itemoperation"
|
"github.com/vmware-tanzu/velero/pkg/itemoperation"
|
||||||
"github.com/vmware-tanzu/velero/pkg/util/boolptr"
|
"github.com/vmware-tanzu/velero/pkg/util/boolptr"
|
||||||
@@ -179,9 +180,17 @@ func DescribeRestore(
|
|||||||
describePodVolumeRestores(d, podVolumeRestores, details)
|
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)
|
buf := new(bytes.Buffer)
|
||||||
if err := downloadrequest.Stream(ctx, kbClient, restore.Namespace, restore.Name, velerov1api.DownloadTargetKindRestoreVolumeInfo,
|
if err := downloadrequest.StreamWithBSLCACert(ctx, kbClient, restore.Namespace, restore.Name, velerov1api.DownloadTargetKindRestoreVolumeInfo,
|
||||||
buf, downloadRequestTimeout, insecureSkipTLSVerify, caCertFile); err == nil {
|
buf, downloadRequestTimeout, insecureSkipTLSVerify, caCertFile, bslCACert); err == nil {
|
||||||
var restoreVolInfo []volume.RestoreVolumeInfo
|
var restoreVolInfo []volume.RestoreVolumeInfo
|
||||||
if err := json.NewDecoder(buf).Decode(&restoreVolInfo); err != nil {
|
if err := json.NewDecoder(buf).Decode(&restoreVolInfo); err != nil {
|
||||||
d.Printf("\t<error reading restore volume info: %v>\n", err)
|
d.Printf("\t<error reading restore volume info: %v>\n", err)
|
||||||
@@ -250,8 +259,16 @@ func describeRestoreItemOperations(ctx context.Context, kbClient kbclient.Client
|
|||||||
return
|
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)
|
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)
|
d.Printf("Restore Item Operations:\t<error getting operation info: %v>\n", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -274,10 +291,18 @@ func describeRestoreResults(ctx context.Context, kbClient kbclient.Client, d *De
|
|||||||
return
|
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 buf bytes.Buffer
|
||||||
var resultMap map[string]results.Result
|
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)
|
d.Printf("Warnings:\t<error getting warnings: %v>\n\nErrors:\t<error getting errors: %v>\n", err, err)
|
||||||
return
|
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) {
|
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)
|
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 {
|
if err == downloadrequest.ErrNotFound {
|
||||||
d.Println("Resource List:\t<restore resource list not found>")
|
d.Println("Resource List:\t<restore resource list not found>")
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -30,6 +30,27 @@ spec:
|
|||||||
profile: "default"
|
profile: "default"
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Example with self-signed certificate
|
||||||
|
|
||||||
|
When using object storage with self-signed certificates, you can specify the CA certificate:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
apiVersion: velero.io/v1
|
||||||
|
kind: BackupStorageLocation
|
||||||
|
metadata:
|
||||||
|
name: default
|
||||||
|
namespace: velero
|
||||||
|
spec:
|
||||||
|
provider: aws
|
||||||
|
objectStorage:
|
||||||
|
bucket: velero-backups
|
||||||
|
# Base64 encoded CA certificate
|
||||||
|
caCert: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUR1VENDQXFHZ0F3SUJBZ0lVTWRiWkNaYnBhcE9lYThDR0NMQnhhY3dVa213d0RRWUpLb1pJaHZjTkFRRUwKQlFBd2JERUxNQWtHQTFVRUJoTUNWVk14RXpBUkJnTlZCQWdNQ2tOaGJHbG1iM0p1YVdFeEZqQVVCZ05WQkFjTQpEVk5oYmlCR2NtRnVZMmx6WTI4eEdEQVdCZ05WQkFvTUQwVjRZVzF3YkdVZ1EyOXRjR0Z1ZVRFV01CUUdBMVVFCkF3d05aWGhoYlhCc1pTNXNiMk5oYkRBZUZ3MHlNekEzTVRBeE9UVXlNVGhhRncweU5EQTNNRGt4T1RVeU1UaGEKTUd3eEN6QUpCZ05WQkFZVEFsVlRNUk13RVFZRFZRUUNEQXBEWEJ4cG1iM0p1YVdFeEZqQVVCZ05WQkFjTURWTmgKYmlCR2NtRnVZMmx6WTI4eEdEQVdCZ05WQkFvTUQwVjRZVzF3YkdVZ1EyOXRjR0Z1ZVRFV01CUUdBMVVFQXd3TgpaWGhoYlhCc1pTNXNiMk5oYkRDQ0FTSXdEUVlKS29aSWh2Y05BUUVCQlFBRGdnRVBBRENDQVFvQ2dnRUJBS1dqCi0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K
|
||||||
|
config:
|
||||||
|
region: us-east-1
|
||||||
|
s3Url: https://minio.example.com
|
||||||
|
```
|
||||||
|
|
||||||
### Parameter Reference
|
### Parameter Reference
|
||||||
|
|
||||||
The configurable parameters are as follows:
|
The configurable parameters are as follows:
|
||||||
|
|||||||
@@ -25,19 +25,39 @@ that storage provider when backing up and restoring.
|
|||||||
|
|
||||||
## Trusting a self-signed certificate with the Velero client
|
## Trusting a self-signed certificate with the Velero client
|
||||||
|
|
||||||
To use the describe, download, or logs commands to access a backup or restore contained
|
When using Velero client commands like describe, download, or logs to access backups or restores
|
||||||
in storage secured by a self-signed certificate as in the above example, you must use
|
in storage secured by a self-signed certificate, the CA certificate can be configured in two ways:
|
||||||
the `--cacert` flag to provide a path to the certificate to be trusted.
|
|
||||||
|
|
||||||
```bash
|
1. **Using the `--cacert` flag** (legacy method):
|
||||||
velero backup describe my-backup --cacert <PATH_TO_CA_BUNDLE>
|
|
||||||
```
|
```bash
|
||||||
|
velero backup describe my-backup --cacert <PATH_TO_CA_BUNDLE>
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **Configuring the CA certificate in the BackupStorageLocation**:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
apiVersion: velero.io/v1
|
||||||
|
kind: BackupStorageLocation
|
||||||
|
metadata:
|
||||||
|
name: default
|
||||||
|
namespace: velero
|
||||||
|
spec:
|
||||||
|
provider: aws
|
||||||
|
objectStorage:
|
||||||
|
bucket: velero-backups
|
||||||
|
caCert: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCi4uLiAoYmFzZTY0IGVuY29kZWQgY2VydGlmaWNhdGUgY29udGVudCkgLi4uCi0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K
|
||||||
|
config:
|
||||||
|
region: us-east-1
|
||||||
|
```
|
||||||
|
|
||||||
|
When the CA certificate is configured in the BackupStorageLocation, Velero client commands will automatically use it without requiring the `--cacert` flag.
|
||||||
|
|
||||||
## Error with client certificate with custom S3 server
|
## Error with client certificate with custom S3 server
|
||||||
|
|
||||||
In case you are using a custom S3-compatible server, you may encounter that the backup fails with an error similar to one below.
|
In case you are using a custom S3-compatible server, you may encounter that the backup fails with an error similar to one below.
|
||||||
|
|
||||||
```
|
```text
|
||||||
rpc error: code = Unknown desc = RequestError: send request failed caused by:
|
rpc error: code = Unknown desc = RequestError: send request failed caused by:
|
||||||
Get https://minio.com:3000/k8s-backup-bucket?delimiter=%2F&list-type=2&prefix=: remote error: tls: alert(116)
|
Get https://minio.com:3000/k8s-backup-bucket?delimiter=%2F&list-type=2&prefix=: remote error: tls: alert(116)
|
||||||
```
|
```
|
||||||
@@ -47,7 +67,6 @@ Velero as a client does not include its certificate while performing SSL handsha
|
|||||||
From [TLS 1.3 spec](https://tools.ietf.org/html/rfc8446), verifying client certificate is optional on the server.
|
From [TLS 1.3 spec](https://tools.ietf.org/html/rfc8446), verifying client certificate is optional on the server.
|
||||||
You will need to change this setting on the server to make it work.
|
You will need to change this setting on the server to make it work.
|
||||||
|
|
||||||
|
|
||||||
## Skipping TLS verification
|
## Skipping TLS verification
|
||||||
|
|
||||||
**Note:** The `--insecure-skip-tls-verify` flag is insecure and susceptible to man-in-the-middle attacks and meant to help your testing and developing scenarios in an on-premises environment. Using this flag in production is not recommended.
|
**Note:** The `--insecure-skip-tls-verify` flag is insecure and susceptible to man-in-the-middle attacks and meant to help your testing and developing scenarios in an on-premises environment. Using this flag in production is not recommended.
|
||||||
|
|||||||
Reference in New Issue
Block a user