mirror of
https://github.com/vmware-tanzu/velero.git
synced 2026-01-09 14:43:24 +00:00
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>
1218 lines
38 KiB
Go
1218 lines
38 KiB
Go
/*
|
|
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 downloadrequest
|
|
|
|
import (
|
|
"bytes"
|
|
"compress/gzip"
|
|
"crypto/rand"
|
|
"crypto/rsa"
|
|
"crypto/tls"
|
|
"crypto/x509"
|
|
"crypto/x509/pkix"
|
|
"encoding/pem"
|
|
"math/big"
|
|
"net"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"os"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
kbclient "sigs.k8s.io/controller-runtime/pkg/client"
|
|
"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/cmd/util/cacert"
|
|
"github.com/vmware-tanzu/velero/pkg/util"
|
|
)
|
|
|
|
// createSelfSignedCertificate creates a self-signed certificate for testing.
|
|
// This allows us to test the BSL CA certificate functionality by ensuring
|
|
// that the client properly validates server certificates against the CA cert
|
|
// provided in the BackupStorageLocation configuration.
|
|
func createSelfSignedCertificate(t *testing.T) (tls.Certificate, []byte) {
|
|
t.Helper()
|
|
|
|
// Generate a private key
|
|
priv, err := rsa.GenerateKey(rand.Reader, 2048)
|
|
require.NoError(t, err)
|
|
|
|
// Create certificate template for a self-signed certificate
|
|
template := x509.Certificate{
|
|
SerialNumber: big.NewInt(1),
|
|
Subject: pkix.Name{
|
|
Organization: []string{"Test Org"},
|
|
Country: []string{"US"},
|
|
Province: []string{""},
|
|
Locality: []string{"Test City"},
|
|
StreetAddress: []string{""},
|
|
PostalCode: []string{""},
|
|
},
|
|
NotBefore: time.Now(),
|
|
NotAfter: time.Now().Add(365 * 24 * time.Hour),
|
|
KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
|
|
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
|
|
BasicConstraintsValid: true,
|
|
IPAddresses: []net.IP{net.IPv4(127, 0, 0, 1)},
|
|
DNSNames: []string{"localhost"},
|
|
}
|
|
|
|
// Create the certificate
|
|
certDER, err := x509.CreateCertificate(rand.Reader, &template, &template, &priv.PublicKey, priv)
|
|
require.NoError(t, err)
|
|
|
|
// Encode certificate and key to PEM
|
|
certPEM := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: certDER})
|
|
keyPEM := pem.EncodeToMemory(&pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(priv)})
|
|
|
|
// Create tls.Certificate
|
|
tlsCert, err := tls.X509KeyPair(certPEM, keyPEM)
|
|
require.NoError(t, err)
|
|
|
|
return tlsCert, certPEM
|
|
}
|
|
|
|
func TestStream(t *testing.T) {
|
|
// Create a test server that returns download content
|
|
testContent := "test backup content"
|
|
downloadServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
w.WriteHeader(http.StatusOK)
|
|
if strings.Contains(r.URL.Path, "log") {
|
|
// For logs, return gzipped content
|
|
gzipWriter := gzip.NewWriter(w)
|
|
gzipWriter.Write([]byte(testContent))
|
|
gzipWriter.Close()
|
|
} else {
|
|
w.Write([]byte(testContent))
|
|
}
|
|
}))
|
|
defer downloadServer.Close()
|
|
|
|
testCases := []struct {
|
|
name string
|
|
target velerov1api.DownloadTargetKind
|
|
timeout time.Duration
|
|
setupClient func(*testing.T, kbclient.WithWatch)
|
|
expectedError bool
|
|
expectedErrMessage string
|
|
validateContent func(*testing.T, *bytes.Buffer)
|
|
}{
|
|
{
|
|
name: "successful backup log download",
|
|
target: velerov1api.DownloadTargetKindBackupLog,
|
|
timeout: 5 * time.Second,
|
|
setupClient: func(t *testing.T, client kbclient.WithWatch) {
|
|
t.Helper()
|
|
// Simulate controller updating the DownloadRequest with URL
|
|
go func() {
|
|
time.Sleep(10 * time.Millisecond)
|
|
list := &velerov1api.DownloadRequestList{}
|
|
err := client.List(t.Context(), list)
|
|
assert.NoError(t, err)
|
|
|
|
for _, dr := range list.Items {
|
|
dr.Status.DownloadURL = downloadServer.URL + "/log"
|
|
dr.Status.Phase = velerov1api.DownloadRequestPhaseProcessed
|
|
err := client.Update(t.Context(), &dr)
|
|
assert.NoError(t, err)
|
|
}
|
|
}()
|
|
},
|
|
expectedError: false,
|
|
validateContent: func(t *testing.T, buf *bytes.Buffer) {
|
|
t.Helper()
|
|
// Logs should be decompressed
|
|
assert.Equal(t, testContent, buf.String())
|
|
},
|
|
},
|
|
{
|
|
name: "successful backup contents download",
|
|
target: velerov1api.DownloadTargetKindBackupContents,
|
|
timeout: 5 * time.Second,
|
|
setupClient: func(t *testing.T, client kbclient.WithWatch) {
|
|
t.Helper()
|
|
// Simulate controller updating the DownloadRequest with URL
|
|
go func() {
|
|
time.Sleep(10 * time.Millisecond)
|
|
list := &velerov1api.DownloadRequestList{}
|
|
err := client.List(t.Context(), list)
|
|
assert.NoError(t, err)
|
|
|
|
for _, dr := range list.Items {
|
|
dr.Status.DownloadURL = downloadServer.URL + "/contents"
|
|
dr.Status.Phase = velerov1api.DownloadRequestPhaseProcessed
|
|
err := client.Update(t.Context(), &dr)
|
|
assert.NoError(t, err)
|
|
}
|
|
}()
|
|
},
|
|
expectedError: false,
|
|
validateContent: func(t *testing.T, buf *bytes.Buffer) {
|
|
t.Helper()
|
|
assert.Equal(t, testContent, buf.String())
|
|
},
|
|
},
|
|
{
|
|
name: "timeout waiting for download URL",
|
|
target: velerov1api.DownloadTargetKindBackupLog,
|
|
timeout: 50 * time.Millisecond,
|
|
setupClient: func(t *testing.T, client kbclient.WithWatch) {
|
|
t.Helper()
|
|
},
|
|
expectedError: true,
|
|
expectedErrMessage: "download request download url timeout",
|
|
},
|
|
}
|
|
|
|
for _, tc := range testCases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
fakeClient := fake.NewClientBuilder().
|
|
WithScheme(util.VeleroScheme).
|
|
Build()
|
|
|
|
if tc.setupClient != nil {
|
|
tc.setupClient(t, fakeClient)
|
|
}
|
|
|
|
var buf bytes.Buffer
|
|
ctx := t.Context()
|
|
|
|
err := Stream(ctx, fakeClient, "test-ns", "test-backup", tc.target, &buf, tc.timeout, false, "")
|
|
|
|
if tc.expectedError {
|
|
require.Error(t, err)
|
|
if tc.expectedErrMessage != "" {
|
|
assert.Contains(t, err.Error(), tc.expectedErrMessage)
|
|
}
|
|
} else {
|
|
require.NoError(t, err)
|
|
if tc.validateContent != nil {
|
|
tc.validateContent(t, &buf)
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestStreamWithBSLCACert(t *testing.T) {
|
|
// Create a test server that returns download content
|
|
testContent := "test backup content with BSL CA cert"
|
|
|
|
// Create self-signed certificate for TLS testing
|
|
tlsCert, serverCACertPEM := createSelfSignedCertificate(t)
|
|
|
|
// Create TLS test server for testing CA certificate functionality
|
|
tlsServer := httptest.NewUnstartedServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
w.WriteHeader(http.StatusOK)
|
|
if strings.Contains(r.URL.Path, "log") {
|
|
// For logs, return gzipped content
|
|
gzipWriter := gzip.NewWriter(w)
|
|
gzipWriter.Write([]byte(testContent))
|
|
gzipWriter.Close()
|
|
} else {
|
|
w.Write([]byte(testContent))
|
|
}
|
|
}))
|
|
tlsServer.TLS = &tls.Config{
|
|
Certificates: []tls.Certificate{tlsCert},
|
|
}
|
|
tlsServer.StartTLS()
|
|
defer tlsServer.Close()
|
|
|
|
// Also create a regular HTTP server for non-TLS tests
|
|
httpServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
w.WriteHeader(http.StatusOK)
|
|
if strings.Contains(r.URL.Path, "log") {
|
|
// For logs, return gzipped content
|
|
gzipWriter := gzip.NewWriter(w)
|
|
gzipWriter.Write([]byte(testContent))
|
|
gzipWriter.Close()
|
|
} else {
|
|
w.Write([]byte(testContent))
|
|
}
|
|
}))
|
|
defer httpServer.Close()
|
|
|
|
testCases := []struct {
|
|
name string
|
|
target velerov1api.DownloadTargetKind
|
|
bslCACert string
|
|
timeout time.Duration
|
|
setupClient func(*testing.T, kbclient.WithWatch)
|
|
useTLS bool
|
|
expectedError bool
|
|
expectedErrMessage string
|
|
validateContent func(*testing.T, *bytes.Buffer)
|
|
}{
|
|
{
|
|
name: "successful TLS backup log download with correct BSL CA cert",
|
|
target: velerov1api.DownloadTargetKindBackupLog,
|
|
bslCACert: string(serverCACertPEM),
|
|
timeout: 5 * time.Second,
|
|
useTLS: true,
|
|
setupClient: func(t *testing.T, client kbclient.WithWatch) {
|
|
t.Helper()
|
|
// Simulate controller updating the DownloadRequest with URL
|
|
go func() {
|
|
time.Sleep(10 * time.Millisecond)
|
|
list := &velerov1api.DownloadRequestList{}
|
|
err := client.List(t.Context(), list)
|
|
assert.NoError(t, err)
|
|
|
|
for _, dr := range list.Items {
|
|
dr.Status.DownloadURL = tlsServer.URL + "/log"
|
|
dr.Status.Phase = velerov1api.DownloadRequestPhaseProcessed
|
|
err := client.Update(t.Context(), &dr)
|
|
assert.NoError(t, err)
|
|
}
|
|
}()
|
|
},
|
|
expectedError: false,
|
|
validateContent: func(t *testing.T, buf *bytes.Buffer) {
|
|
t.Helper()
|
|
// Logs should be decompressed
|
|
assert.Equal(t, testContent, buf.String())
|
|
},
|
|
},
|
|
{
|
|
name: "successful TLS backup contents download with correct BSL CA cert",
|
|
target: velerov1api.DownloadTargetKindBackupContents,
|
|
bslCACert: string(serverCACertPEM),
|
|
timeout: 5 * time.Second,
|
|
useTLS: true,
|
|
setupClient: func(t *testing.T, client kbclient.WithWatch) {
|
|
t.Helper()
|
|
// Simulate controller updating the DownloadRequest with URL
|
|
go func() {
|
|
time.Sleep(10 * time.Millisecond)
|
|
list := &velerov1api.DownloadRequestList{}
|
|
err := client.List(t.Context(), list)
|
|
assert.NoError(t, err)
|
|
|
|
for _, dr := range list.Items {
|
|
dr.Status.DownloadURL = tlsServer.URL + "/contents"
|
|
dr.Status.Phase = velerov1api.DownloadRequestPhaseProcessed
|
|
err := client.Update(t.Context(), &dr)
|
|
assert.NoError(t, err)
|
|
}
|
|
}()
|
|
},
|
|
expectedError: false,
|
|
validateContent: func(t *testing.T, buf *bytes.Buffer) {
|
|
t.Helper()
|
|
assert.Equal(t, testContent, buf.String())
|
|
},
|
|
},
|
|
{
|
|
name: "failed TLS download with wrong BSL CA cert",
|
|
target: velerov1api.DownloadTargetKindBackupContents,
|
|
bslCACert: "-----BEGIN CERTIFICATE-----\nMIIBkTCB+wIJAKHHIgKwERfFMA0GCSqGSIb3DQEBCwUAMBkxFzAVBgNVBAMTDmRp\nZmZlcmVudC1jZXJ0MB4XDTE5MDQwMTAwMDAwMFoXDTI5MDQwMTAwMDAwMFowGTEX\nMBUGA1UEAxMOZGlmZmVyZW50LWNlcnQwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJ\nAoGBAOO4V+XrhVEGbTqnO2FM5eVFaM3KMKc3M9/C1aeg3vvY+Th3OhqJBxEYFxXL\nZoSqkwL/E6BjQb0NdSyJY9wdM4Ie3gElcZBKYVpHXYYAVhrepRCRVJEIHdBN8ybr\nFoBBDjd/ID1qy8Gdp3RihPFNvCNx0RWWqPAJtNXWJvCiNRCDAgMBAAEwDQYJKoZI\nhvcNAQELBQADgYEAGEwwGz7HAmH0J3pAJzQKPCb8HJG8hTjD6qkMon3Bp6gZ\n-----END CERTIFICATE-----\n",
|
|
timeout: 5 * time.Second,
|
|
useTLS: true,
|
|
setupClient: func(t *testing.T, client kbclient.WithWatch) {
|
|
t.Helper()
|
|
// Simulate controller updating the DownloadRequest with URL
|
|
go func() {
|
|
time.Sleep(10 * time.Millisecond)
|
|
list := &velerov1api.DownloadRequestList{}
|
|
err := client.List(t.Context(), list)
|
|
assert.NoError(t, err)
|
|
|
|
for _, dr := range list.Items {
|
|
dr.Status.DownloadURL = tlsServer.URL + "/contents"
|
|
dr.Status.Phase = velerov1api.DownloadRequestPhaseProcessed
|
|
err := client.Update(t.Context(), &dr)
|
|
assert.NoError(t, err)
|
|
}
|
|
}()
|
|
},
|
|
expectedError: true,
|
|
expectedErrMessage: "x509",
|
|
},
|
|
{
|
|
name: "successful HTTP download with empty BSL CA cert",
|
|
target: velerov1api.DownloadTargetKindBackupContents,
|
|
bslCACert: "",
|
|
timeout: 5 * time.Second,
|
|
useTLS: false,
|
|
setupClient: func(t *testing.T, client kbclient.WithWatch) {
|
|
t.Helper()
|
|
// Simulate controller updating the DownloadRequest with URL
|
|
go func() {
|
|
time.Sleep(10 * time.Millisecond)
|
|
list := &velerov1api.DownloadRequestList{}
|
|
err := client.List(t.Context(), list)
|
|
assert.NoError(t, err)
|
|
|
|
for _, dr := range list.Items {
|
|
dr.Status.DownloadURL = httpServer.URL + "/contents"
|
|
dr.Status.Phase = velerov1api.DownloadRequestPhaseProcessed
|
|
err := client.Update(t.Context(), &dr)
|
|
assert.NoError(t, err)
|
|
}
|
|
}()
|
|
},
|
|
expectedError: false,
|
|
validateContent: func(t *testing.T, buf *bytes.Buffer) {
|
|
t.Helper()
|
|
assert.Equal(t, testContent, buf.String())
|
|
},
|
|
},
|
|
{
|
|
name: "timeout waiting for download URL with BSL CA cert",
|
|
target: velerov1api.DownloadTargetKindBackupLog,
|
|
bslCACert: "test-ca-cert-content",
|
|
timeout: 50 * time.Millisecond,
|
|
setupClient: func(t *testing.T, client kbclient.WithWatch) {
|
|
t.Helper()
|
|
},
|
|
expectedError: true,
|
|
expectedErrMessage: "download request download url timeout",
|
|
},
|
|
{
|
|
name: "failed TLS download with malformed BSL CA cert",
|
|
target: velerov1api.DownloadTargetKindBackupLog,
|
|
bslCACert: "-----BEGIN CERTIFICATE-----\nINVALID CERT DATA\n-----END CERTIFICATE-----\n",
|
|
timeout: 5 * time.Second,
|
|
useTLS: true,
|
|
setupClient: func(t *testing.T, client kbclient.WithWatch) {
|
|
t.Helper()
|
|
// Simulate controller updating the DownloadRequest with URL
|
|
go func() {
|
|
time.Sleep(10 * time.Millisecond)
|
|
list := &velerov1api.DownloadRequestList{}
|
|
err := client.List(t.Context(), list)
|
|
assert.NoError(t, err)
|
|
|
|
for _, dr := range list.Items {
|
|
dr.Status.DownloadURL = tlsServer.URL + "/log"
|
|
dr.Status.Phase = velerov1api.DownloadRequestPhaseProcessed
|
|
err := client.Update(t.Context(), &dr)
|
|
assert.NoError(t, err)
|
|
}
|
|
}()
|
|
},
|
|
expectedError: true,
|
|
expectedErrMessage: "x509", // Should fail due to malformed cert not being added to pool
|
|
},
|
|
}
|
|
|
|
for _, tc := range testCases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
fakeClient := fake.NewClientBuilder().
|
|
WithScheme(util.VeleroScheme).
|
|
Build()
|
|
|
|
if tc.setupClient != nil {
|
|
tc.setupClient(t, fakeClient)
|
|
}
|
|
|
|
var buf bytes.Buffer
|
|
ctx := t.Context()
|
|
|
|
err := StreamWithBSLCACert(ctx, fakeClient, "test-ns", "test-backup", tc.target, &buf, tc.timeout, false, "", tc.bslCACert)
|
|
|
|
if tc.expectedError {
|
|
require.Error(t, err)
|
|
if tc.expectedErrMessage != "" {
|
|
assert.Contains(t, err.Error(), tc.expectedErrMessage)
|
|
}
|
|
} else {
|
|
require.NoError(t, err)
|
|
if tc.validateContent != nil {
|
|
tc.validateContent(t, &buf)
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestDownload(t *testing.T) {
|
|
testContent := "test content for download"
|
|
compressedContent := new(bytes.Buffer)
|
|
gzipWriter := gzip.NewWriter(compressedContent)
|
|
gzipWriter.Write([]byte(testContent))
|
|
gzipWriter.Close()
|
|
|
|
testCases := []struct {
|
|
name string
|
|
serverHandler http.HandlerFunc
|
|
target velerov1api.DownloadTargetKind
|
|
insecureSkipTLSVerify bool
|
|
caCertFile string
|
|
bslCACert string
|
|
expectedContent string
|
|
expectedError bool
|
|
errorType error
|
|
}{
|
|
{
|
|
name: "successful download with gzip for logs",
|
|
serverHandler: func(w http.ResponseWriter, r *http.Request) {
|
|
w.WriteHeader(http.StatusOK)
|
|
w.Write(compressedContent.Bytes())
|
|
},
|
|
target: velerov1api.DownloadTargetKindBackupLog,
|
|
expectedContent: testContent,
|
|
expectedError: false,
|
|
},
|
|
{
|
|
name: "successful download without gzip for backup contents",
|
|
serverHandler: func(w http.ResponseWriter, r *http.Request) {
|
|
w.WriteHeader(http.StatusOK)
|
|
w.Write([]byte(testContent))
|
|
},
|
|
target: velerov1api.DownloadTargetKindBackupContents,
|
|
expectedContent: testContent,
|
|
expectedError: false,
|
|
},
|
|
{
|
|
name: "404 not found error",
|
|
serverHandler: func(w http.ResponseWriter, r *http.Request) {
|
|
w.WriteHeader(http.StatusNotFound)
|
|
},
|
|
target: velerov1api.DownloadTargetKindBackupLog,
|
|
expectedError: true,
|
|
errorType: ErrNotFound,
|
|
},
|
|
{
|
|
name: "500 internal server error",
|
|
serverHandler: func(w http.ResponseWriter, r *http.Request) {
|
|
w.WriteHeader(http.StatusInternalServerError)
|
|
w.Write([]byte("internal server error"))
|
|
},
|
|
target: velerov1api.DownloadTargetKindBackupLog,
|
|
expectedError: true,
|
|
},
|
|
}
|
|
|
|
for _, tc := range testCases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
server := httptest.NewServer(tc.serverHandler)
|
|
defer server.Close()
|
|
|
|
var buf bytes.Buffer
|
|
err := download(
|
|
t.Context(),
|
|
server.URL,
|
|
tc.target,
|
|
&buf,
|
|
tc.insecureSkipTLSVerify,
|
|
tc.caCertFile,
|
|
tc.bslCACert,
|
|
)
|
|
|
|
if tc.expectedError {
|
|
require.Error(t, err)
|
|
if tc.errorType != nil {
|
|
assert.Equal(t, tc.errorType, err)
|
|
}
|
|
} else {
|
|
require.NoError(t, err)
|
|
assert.Equal(t, tc.expectedContent, buf.String())
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
// TestStreamWithBSLCACertEndToEnd tests the complete flow from BSL to download with CA cert
|
|
func TestStreamWithBSLCACertEndToEnd(t *testing.T) {
|
|
testContent := "end-to-end test content"
|
|
|
|
// Create self-signed certificate for TLS testing
|
|
tlsCert, serverCACertPEM := createSelfSignedCertificate(t)
|
|
|
|
// Create TLS test server
|
|
tlsServer := httptest.NewUnstartedServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
w.WriteHeader(http.StatusOK)
|
|
if strings.Contains(r.URL.Path, "log") {
|
|
// For logs, return gzipped content
|
|
gzipWriter := gzip.NewWriter(w)
|
|
gzipWriter.Write([]byte(testContent))
|
|
gzipWriter.Close()
|
|
} else {
|
|
w.Write([]byte(testContent))
|
|
}
|
|
}))
|
|
tlsServer.TLS = &tls.Config{
|
|
Certificates: []tls.Certificate{tlsCert},
|
|
}
|
|
tlsServer.StartTLS()
|
|
defer tlsServer.Close()
|
|
|
|
// Create BSL with CA cert
|
|
bsl := builder.ForBackupStorageLocation("test-ns", "test-bsl").
|
|
Provider("aws").
|
|
Bucket("test-bucket").
|
|
CACert(serverCACertPEM).
|
|
Result()
|
|
|
|
// Create backup that references the BSL
|
|
backup := builder.ForBackup("test-ns", "test-backup").
|
|
StorageLocation("test-bsl").
|
|
Result()
|
|
|
|
// Setup fake client with BSL and backup
|
|
fakeClient := fake.NewClientBuilder().
|
|
WithScheme(util.VeleroScheme).
|
|
WithRuntimeObjects(bsl, backup).
|
|
Build()
|
|
|
|
// Helper function to simulate controller updating the DownloadRequest
|
|
simulateControllerUpdate := func() {
|
|
go func() {
|
|
time.Sleep(10 * time.Millisecond)
|
|
list := &velerov1api.DownloadRequestList{}
|
|
err := fakeClient.List(t.Context(), list)
|
|
assert.NoError(t, err)
|
|
|
|
for _, dr := range list.Items {
|
|
dr.Status.DownloadURL = tlsServer.URL + "/log"
|
|
dr.Status.Phase = velerov1api.DownloadRequestPhaseProcessed
|
|
err := fakeClient.Update(t.Context(), &dr)
|
|
assert.NoError(t, err)
|
|
}
|
|
}()
|
|
}
|
|
|
|
// Test the complete flow
|
|
ctx := t.Context()
|
|
|
|
// First, try to download WITHOUT the CA cert - this should fail
|
|
simulateControllerUpdate()
|
|
var bufFail bytes.Buffer
|
|
err := StreamWithBSLCACert(ctx, fakeClient, "test-ns", "test-backup", velerov1api.DownloadTargetKindBackupLog, &bufFail, 5*time.Second, false, "", "")
|
|
require.Error(t, err)
|
|
assert.Contains(t, err.Error(), "x509") // Should fail with certificate validation error
|
|
|
|
// Now get CA cert from BSL through backup
|
|
cacertStr, err := cacert.GetCACertFromBackup(ctx, fakeClient, "test-ns", backup)
|
|
require.NoError(t, err)
|
|
require.Equal(t, string(serverCACertPEM), cacertStr)
|
|
|
|
// Try again with the CA cert - this should succeed
|
|
simulateControllerUpdate()
|
|
var bufSuccess bytes.Buffer
|
|
err = StreamWithBSLCACert(ctx, fakeClient, "test-ns", "test-backup", velerov1api.DownloadTargetKindBackupLog, &bufSuccess, 5*time.Second, false, "", cacertStr)
|
|
require.NoError(t, err)
|
|
|
|
// Verify content was downloaded and decompressed correctly
|
|
assert.Equal(t, testContent, bufSuccess.String())
|
|
}
|
|
|
|
// TestBackwardCompatibilityWithoutBSLCACert tests that old download requests work without BSL CA cert
|
|
func TestBackwardCompatibilityWithoutBSLCACert(t *testing.T) {
|
|
testContent := "backward compatibility test content"
|
|
|
|
// Create HTTP (not HTTPS) server to simulate old behavior where TLS wasn't required
|
|
httpServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
w.WriteHeader(http.StatusOK)
|
|
if strings.Contains(r.URL.Path, "log") {
|
|
// For logs, return gzipped content
|
|
gzipWriter := gzip.NewWriter(w)
|
|
gzipWriter.Write([]byte(testContent))
|
|
gzipWriter.Close()
|
|
} else {
|
|
w.Write([]byte(testContent))
|
|
}
|
|
}))
|
|
defer httpServer.Close()
|
|
|
|
// Create BSL without CA cert (simulating old configuration)
|
|
bsl := builder.ForBackupStorageLocation("test-ns", "test-bsl").
|
|
Provider("aws").
|
|
Bucket("test-bucket").
|
|
// No CACert() call - simulating pre-CA cert support
|
|
Result()
|
|
|
|
// Create backup that references the BSL
|
|
backup := builder.ForBackup("test-ns", "test-backup").
|
|
StorageLocation("test-bsl").
|
|
Result()
|
|
|
|
// Setup fake client with BSL and backup
|
|
fakeClient := fake.NewClientBuilder().
|
|
WithScheme(util.VeleroScheme).
|
|
WithRuntimeObjects(bsl, backup).
|
|
Build()
|
|
|
|
// Simulate controller updating the DownloadRequest with HTTP URL (old behavior)
|
|
go func() {
|
|
time.Sleep(10 * time.Millisecond)
|
|
list := &velerov1api.DownloadRequestList{}
|
|
err := fakeClient.List(t.Context(), list)
|
|
assert.NoError(t, err)
|
|
|
|
for _, dr := range list.Items {
|
|
dr.Status.DownloadURL = httpServer.URL + "/log"
|
|
dr.Status.Phase = velerov1api.DownloadRequestPhaseProcessed
|
|
err := fakeClient.Update(t.Context(), &dr)
|
|
assert.NoError(t, err)
|
|
}
|
|
}()
|
|
|
|
ctx := t.Context()
|
|
|
|
// Test 1: Stream function (without BSL CA cert parameter) should work
|
|
var buf1 bytes.Buffer
|
|
err := Stream(ctx, fakeClient, "test-ns", "test-backup", velerov1api.DownloadTargetKindBackupLog, &buf1, 5*time.Second, false, "")
|
|
require.NoError(t, err)
|
|
assert.Equal(t, testContent, buf1.String())
|
|
|
|
// Test 2: StreamWithBSLCACert with empty BSL CA cert should also work
|
|
go func() {
|
|
time.Sleep(10 * time.Millisecond)
|
|
list := &velerov1api.DownloadRequestList{}
|
|
err := fakeClient.List(t.Context(), list)
|
|
assert.NoError(t, err)
|
|
|
|
for _, dr := range list.Items {
|
|
dr.Status.DownloadURL = httpServer.URL + "/contents"
|
|
dr.Status.Phase = velerov1api.DownloadRequestPhaseProcessed
|
|
err := fakeClient.Update(t.Context(), &dr)
|
|
assert.NoError(t, err)
|
|
}
|
|
}()
|
|
|
|
var buf2 bytes.Buffer
|
|
err = StreamWithBSLCACert(ctx, fakeClient, "test-ns", "test-backup", velerov1api.DownloadTargetKindBackupContents, &buf2, 5*time.Second, false, "", "")
|
|
require.NoError(t, err)
|
|
assert.Equal(t, testContent, buf2.String())
|
|
|
|
// Test 3: Getting CA cert from BSL should return empty string (not error)
|
|
cacert, err := cacert.GetCACertFromBackup(ctx, fakeClient, "test-ns", backup)
|
|
require.NoError(t, err)
|
|
assert.Empty(t, cacert)
|
|
}
|
|
|
|
// TestMixedEnvironmentHTTPAndHTTPS tests environment with both HTTP and HTTPS endpoints
|
|
func TestMixedEnvironmentHTTPAndHTTPS(t *testing.T) {
|
|
testContentHTTP := "http content"
|
|
testContentHTTPS := "https content"
|
|
|
|
// Create HTTP server
|
|
httpServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
w.WriteHeader(http.StatusOK)
|
|
w.Write([]byte(testContentHTTP))
|
|
}))
|
|
defer httpServer.Close()
|
|
|
|
// Create HTTPS server with self-signed cert
|
|
tlsCert, serverCACertPEM := createSelfSignedCertificate(t)
|
|
httpsServer := httptest.NewUnstartedServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
w.WriteHeader(http.StatusOK)
|
|
w.Write([]byte(testContentHTTPS))
|
|
}))
|
|
httpsServer.TLS = &tls.Config{
|
|
Certificates: []tls.Certificate{tlsCert},
|
|
}
|
|
httpsServer.StartTLS()
|
|
defer httpsServer.Close()
|
|
|
|
// Create two BSLs - one for HTTP (old) and one for HTTPS (new)
|
|
bslHTTP := builder.ForBackupStorageLocation("test-ns", "bsl-http").
|
|
Provider("aws").
|
|
Bucket("http-bucket").
|
|
// No CA cert for HTTP
|
|
Result()
|
|
|
|
bslHTTPS := builder.ForBackupStorageLocation("test-ns", "bsl-https").
|
|
Provider("aws").
|
|
Bucket("https-bucket").
|
|
CACert(serverCACertPEM).
|
|
Result()
|
|
|
|
// Create backups for each BSL
|
|
backupHTTP := builder.ForBackup("test-ns", "backup-http").
|
|
StorageLocation("bsl-http").
|
|
Result()
|
|
|
|
backupHTTPS := builder.ForBackup("test-ns", "backup-https").
|
|
StorageLocation("bsl-https").
|
|
Result()
|
|
|
|
// Setup fake client
|
|
fakeClient := fake.NewClientBuilder().
|
|
WithScheme(util.VeleroScheme).
|
|
WithRuntimeObjects(bslHTTP, bslHTTPS, backupHTTP, backupHTTPS).
|
|
Build()
|
|
|
|
ctx := t.Context()
|
|
|
|
// Test HTTP backup download (backward compatible)
|
|
go func() {
|
|
time.Sleep(10 * time.Millisecond)
|
|
list := &velerov1api.DownloadRequestList{}
|
|
err := fakeClient.List(t.Context(), list)
|
|
assert.NoError(t, err)
|
|
|
|
for _, dr := range list.Items {
|
|
if strings.Contains(dr.Name, "backup-http") {
|
|
dr.Status.DownloadURL = httpServer.URL
|
|
dr.Status.Phase = velerov1api.DownloadRequestPhaseProcessed
|
|
err := fakeClient.Update(t.Context(), &dr)
|
|
assert.NoError(t, err)
|
|
}
|
|
}
|
|
}()
|
|
|
|
var bufHTTP bytes.Buffer
|
|
err := Stream(ctx, fakeClient, "test-ns", "backup-http", velerov1api.DownloadTargetKindBackupContents, &bufHTTP, 5*time.Second, false, "")
|
|
require.NoError(t, err)
|
|
assert.Equal(t, testContentHTTP, bufHTTP.String())
|
|
|
|
// Test HTTPS backup download (requires CA cert)
|
|
go func() {
|
|
time.Sleep(10 * time.Millisecond)
|
|
list := &velerov1api.DownloadRequestList{}
|
|
err := fakeClient.List(t.Context(), list)
|
|
assert.NoError(t, err)
|
|
|
|
for _, dr := range list.Items {
|
|
if strings.Contains(dr.Name, "backup-https") {
|
|
dr.Status.DownloadURL = httpsServer.URL
|
|
dr.Status.Phase = velerov1api.DownloadRequestPhaseProcessed
|
|
err := fakeClient.Update(t.Context(), &dr)
|
|
assert.NoError(t, err)
|
|
}
|
|
}
|
|
}()
|
|
|
|
// First try without CA cert - should fail
|
|
var bufHTTPSFail bytes.Buffer
|
|
err = Stream(ctx, fakeClient, "test-ns", "backup-https", velerov1api.DownloadTargetKindBackupContents, &bufHTTPSFail, 5*time.Second, false, "")
|
|
require.Error(t, err)
|
|
assert.Contains(t, err.Error(), "x509")
|
|
|
|
// Get CA cert from HTTPS BSL
|
|
cacertStr, err := cacert.GetCACertFromBackup(ctx, fakeClient, "test-ns", backupHTTPS)
|
|
require.NoError(t, err)
|
|
require.Equal(t, string(serverCACertPEM), cacertStr)
|
|
|
|
// Try again with CA cert - should succeed
|
|
go func() {
|
|
time.Sleep(10 * time.Millisecond)
|
|
list := &velerov1api.DownloadRequestList{}
|
|
err := fakeClient.List(t.Context(), list)
|
|
assert.NoError(t, err)
|
|
|
|
for _, dr := range list.Items {
|
|
if strings.Contains(dr.Name, "backup-https") {
|
|
dr.Status.DownloadURL = httpsServer.URL
|
|
dr.Status.Phase = velerov1api.DownloadRequestPhaseProcessed
|
|
err := fakeClient.Update(t.Context(), &dr)
|
|
assert.NoError(t, err)
|
|
}
|
|
}
|
|
}()
|
|
|
|
var bufHTTPSSuccess bytes.Buffer
|
|
err = StreamWithBSLCACert(ctx, fakeClient, "test-ns", "backup-https", velerov1api.DownloadTargetKindBackupContents, &bufHTTPSSuccess, 5*time.Second, false, "", cacertStr)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, testContentHTTPS, bufHTTPSSuccess.String())
|
|
}
|
|
|
|
// TestBSLUpgradeScenario tests the scenario where a BSL is upgraded to include CA cert
|
|
func TestBSLUpgradeScenario(t *testing.T) {
|
|
testContent := "bsl upgrade test content"
|
|
|
|
// Create self-signed certificate for TLS testing
|
|
tlsCert, serverCACertPEM := createSelfSignedCertificate(t)
|
|
|
|
// Create HTTPS server
|
|
httpsServer := httptest.NewUnstartedServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
w.WriteHeader(http.StatusOK)
|
|
if strings.Contains(r.URL.Path, "log") {
|
|
// For logs, return gzipped content
|
|
gzipWriter := gzip.NewWriter(w)
|
|
gzipWriter.Write([]byte(testContent))
|
|
gzipWriter.Close()
|
|
} else {
|
|
w.Write([]byte(testContent))
|
|
}
|
|
}))
|
|
httpsServer.TLS = &tls.Config{
|
|
Certificates: []tls.Certificate{tlsCert},
|
|
}
|
|
httpsServer.StartTLS()
|
|
defer httpsServer.Close()
|
|
|
|
// Create BSL initially without CA cert (pre-upgrade state)
|
|
bsl := builder.ForBackupStorageLocation("test-ns", "test-bsl").
|
|
Provider("aws").
|
|
Bucket("test-bucket").
|
|
// Initially no CA cert
|
|
Result()
|
|
|
|
// Create an old backup that references this BSL
|
|
oldBackup := builder.ForBackup("test-ns", "old-backup").
|
|
StorageLocation("test-bsl").
|
|
Result()
|
|
|
|
// Setup fake client
|
|
fakeClient := fake.NewClientBuilder().
|
|
WithScheme(util.VeleroScheme).
|
|
WithRuntimeObjects(bsl, oldBackup).
|
|
Build()
|
|
|
|
ctx := t.Context()
|
|
|
|
// Test 1: Old backup with HTTPS URL should fail without CA cert
|
|
go func() {
|
|
time.Sleep(10 * time.Millisecond)
|
|
list := &velerov1api.DownloadRequestList{}
|
|
err := fakeClient.List(t.Context(), list)
|
|
assert.NoError(t, err)
|
|
|
|
for _, dr := range list.Items {
|
|
dr.Status.DownloadURL = httpsServer.URL + "/log"
|
|
dr.Status.Phase = velerov1api.DownloadRequestPhaseProcessed
|
|
err := fakeClient.Update(t.Context(), &dr)
|
|
assert.NoError(t, err)
|
|
}
|
|
}()
|
|
|
|
var bufFail bytes.Buffer
|
|
err := Stream(ctx, fakeClient, "test-ns", "old-backup", velerov1api.DownloadTargetKindBackupLog, &bufFail, 5*time.Second, false, "")
|
|
require.Error(t, err)
|
|
assert.Contains(t, err.Error(), "x509")
|
|
|
|
// Simulate BSL upgrade - get current BSL and update it
|
|
currentBSL := &velerov1api.BackupStorageLocation{}
|
|
err = fakeClient.Get(ctx, kbclient.ObjectKey{Namespace: "test-ns", Name: "test-bsl"}, currentBSL)
|
|
require.NoError(t, err)
|
|
|
|
// Update the BSL to include CA cert
|
|
// Ensure ObjectStorage is initialized
|
|
if currentBSL.Spec.ObjectStorage == nil {
|
|
currentBSL.Spec.ObjectStorage = &velerov1api.ObjectStorageLocation{}
|
|
}
|
|
currentBSL.Spec.ObjectStorage.CACert = serverCACertPEM // CA cert added after upgrade
|
|
|
|
err = fakeClient.Update(ctx, currentBSL)
|
|
require.NoError(t, err)
|
|
|
|
// Test 2: After BSL upgrade, old backup should work with new CA cert
|
|
cacertStr, err := cacert.GetCACertFromBackup(ctx, fakeClient, "test-ns", oldBackup)
|
|
require.NoError(t, err)
|
|
require.Equal(t, string(serverCACertPEM), cacertStr)
|
|
|
|
go func() {
|
|
time.Sleep(10 * time.Millisecond)
|
|
list := &velerov1api.DownloadRequestList{}
|
|
err := fakeClient.List(t.Context(), list)
|
|
assert.NoError(t, err)
|
|
|
|
for _, dr := range list.Items {
|
|
dr.Status.DownloadURL = httpsServer.URL + "/log"
|
|
dr.Status.Phase = velerov1api.DownloadRequestPhaseProcessed
|
|
err := fakeClient.Update(t.Context(), &dr)
|
|
assert.NoError(t, err)
|
|
}
|
|
}()
|
|
|
|
var bufSuccess bytes.Buffer
|
|
err = StreamWithBSLCACert(ctx, fakeClient, "test-ns", "old-backup", velerov1api.DownloadTargetKindBackupLog, &bufSuccess, 5*time.Second, false, "", cacertStr)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, testContent, bufSuccess.String())
|
|
|
|
// Test 3: New backup created after upgrade should also work
|
|
newBackup := builder.ForBackup("test-ns", "new-backup").
|
|
StorageLocation("test-bsl").
|
|
Result()
|
|
|
|
err = fakeClient.Create(ctx, newBackup)
|
|
require.NoError(t, err)
|
|
|
|
cacertStr2, err := cacert.GetCACertFromBackup(ctx, fakeClient, "test-ns", newBackup)
|
|
require.NoError(t, err)
|
|
require.Equal(t, string(serverCACertPEM), cacertStr2)
|
|
}
|
|
|
|
// TestConcurrentDownloadsWithBSLCACert tests multiple concurrent downloads with CA cert
|
|
func TestConcurrentDownloadsWithBSLCACert(t *testing.T) {
|
|
testContent := "concurrent test content"
|
|
|
|
// Create self-signed certificate for TLS testing
|
|
tlsCert, serverCACertPEM := createSelfSignedCertificate(t)
|
|
|
|
// Create TLS test server
|
|
tlsServer := httptest.NewUnstartedServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
// Simulate some processing time
|
|
time.Sleep(10 * time.Millisecond)
|
|
w.WriteHeader(http.StatusOK)
|
|
w.Write([]byte(testContent))
|
|
}))
|
|
tlsServer.TLS = &tls.Config{
|
|
Certificates: []tls.Certificate{tlsCert},
|
|
}
|
|
tlsServer.StartTLS()
|
|
defer tlsServer.Close()
|
|
|
|
// Run multiple concurrent downloads
|
|
numConcurrent := 5
|
|
errors := make(chan error, numConcurrent)
|
|
results := make(chan string, numConcurrent)
|
|
|
|
for i := range numConcurrent {
|
|
go func(idx int) {
|
|
var buf bytes.Buffer
|
|
err := download(
|
|
t.Context(),
|
|
tlsServer.URL,
|
|
velerov1api.DownloadTargetKindBackupContents,
|
|
&buf,
|
|
false,
|
|
"",
|
|
string(serverCACertPEM),
|
|
)
|
|
if err != nil {
|
|
errors <- err
|
|
return
|
|
}
|
|
results <- buf.String()
|
|
}(i)
|
|
}
|
|
|
|
// Collect results
|
|
for i := range numConcurrent {
|
|
select {
|
|
case err := <-errors:
|
|
t.Fatalf("Concurrent download %d failed: %v", i, err)
|
|
case result := <-results:
|
|
assert.Equal(t, testContent, result)
|
|
case <-time.After(5 * time.Second):
|
|
t.Fatal("Timeout waiting for concurrent downloads")
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestDownloadWithBSLCACert(t *testing.T) {
|
|
testContent := "test content with BSL CA cert"
|
|
|
|
// Create self-signed certificate for TLS testing
|
|
tlsCert, serverCACertPEM := createSelfSignedCertificate(t)
|
|
|
|
// Create TLS test server with self-signed cert
|
|
tlsServer := httptest.NewUnstartedServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
w.WriteHeader(http.StatusOK)
|
|
w.Write([]byte(testContent))
|
|
}))
|
|
tlsServer.TLS = &tls.Config{
|
|
Certificates: []tls.Certificate{tlsCert},
|
|
}
|
|
tlsServer.StartTLS()
|
|
defer tlsServer.Close()
|
|
|
|
// Create HTTP test server for non-TLS tests
|
|
httpServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
w.WriteHeader(http.StatusOK)
|
|
w.Write([]byte(testContent))
|
|
}))
|
|
defer httpServer.Close()
|
|
|
|
testCases := []struct {
|
|
name string
|
|
url string
|
|
target velerov1api.DownloadTargetKind
|
|
insecureSkipTLSVerify bool
|
|
bslCACert string
|
|
expectedContent string
|
|
expectedError bool
|
|
expectedErrContains string
|
|
}{
|
|
{
|
|
name: "successful TLS download with correct BSL CA cert",
|
|
url: tlsServer.URL,
|
|
target: velerov1api.DownloadTargetKindBackupContents,
|
|
bslCACert: string(serverCACertPEM),
|
|
expectedContent: testContent,
|
|
expectedError: false,
|
|
},
|
|
{
|
|
name: "successful TLS download with insecureSkipTLSVerify",
|
|
url: tlsServer.URL,
|
|
target: velerov1api.DownloadTargetKindBackupContents,
|
|
insecureSkipTLSVerify: true,
|
|
bslCACert: "",
|
|
expectedContent: testContent,
|
|
expectedError: false,
|
|
},
|
|
{
|
|
name: "failed TLS download without CA cert or insecureSkipTLSVerify",
|
|
url: tlsServer.URL,
|
|
target: velerov1api.DownloadTargetKindBackupContents,
|
|
bslCACert: "",
|
|
expectedError: true,
|
|
expectedErrContains: "x509",
|
|
},
|
|
{
|
|
name: "failed TLS download with wrong BSL CA cert",
|
|
url: tlsServer.URL,
|
|
target: velerov1api.DownloadTargetKindBackupContents,
|
|
bslCACert: "-----BEGIN CERTIFICATE-----\nMIIBkTCB+wIJAKHHIgKwERfFMA0GCSqGSIb3DQEBCwUAMBkxFzAVBgNVBAMTDmRp\nZmZlcmVudC1jZXJ0MB4XDTE5MDQwMTAwMDAwMFoXDTI5MDQwMTAwMDAwMFowGTEX\nMBUGA1UEAxMOZGlmZmVyZW50LWNlcnQwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJ\nAoGBAOO4V+XrhVEGbTqnO2FM5eVFaM3KMKc3M9/C1aeg3vvY+Th3OhqJBxEYFxXL\nZoSqkwL/E6BjQb0NdSyJY9wdM4Ie3gElcZBKYVpHXYYAVhrepRCRVJEIHdBN8ybr\nFoBBDjd/ID1qy8Gdp3RihPFNvCNx0RWWqPAJtNXWJvCiNRCDAgMBAAEwDQYJKoZI\nhvcNAQELBQADgYEAGEwwGz7HAmH0J3pAJzQKPCb8HJG8hTjD6qkMon3Bp6gZ\n-----END CERTIFICATE-----\n",
|
|
expectedError: true,
|
|
expectedErrContains: "x509",
|
|
},
|
|
{
|
|
name: "successful HTTP download with empty BSL CA cert",
|
|
url: httpServer.URL,
|
|
target: velerov1api.DownloadTargetKindBackupContents,
|
|
bslCACert: "",
|
|
expectedContent: testContent,
|
|
expectedError: false,
|
|
},
|
|
{
|
|
name: "successful TLS download with multiple CA certs in PEM block",
|
|
url: tlsServer.URL,
|
|
target: velerov1api.DownloadTargetKindBackupContents,
|
|
bslCACert: "-----BEGIN CERTIFICATE-----\nMIIBkTCB+wIJAKHHIgKwERfFMA0GCSqGSIb3DQEBCwUAMBkxFzAVBgNVBAMTDmRp\nZmZlcmVudC1jZXJ0MB4XDTE5MDQwMTAwMDAwMFoXDTI5MDQwMTAwMDAwMFowGTEX\nMBUGA1UEAxMOZGlmZmVyZW50LWNlcnQwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJ\nAoGBAOO4V+XrhVEGbTqnO2FM5eVFaM3KMKc3M9/C1aeg3vvY+Th3OhqJBxEYFxXL\nZoSqkwL/E6BjQb0NdSyJY9wdM4Ie3gElcZBKYVpHXYYAVhrepRCRVJEIHdBN8ybr\nFoBBDjd/ID1qy8Gdp3RihPFNvCNx0RWWqPAJtNXWJvCiNRCDAgMBAAEwDQYJKoZI\nhvcNAQELBQADgYEAGEwwGz7HAmH0J3pAJzQKPCb8HJG8hTjD6qkMon3Bp6gZ\n-----END CERTIFICATE-----\n" + string(serverCACertPEM), // First cert is wrong, but second is correct
|
|
expectedContent: testContent,
|
|
expectedError: false,
|
|
},
|
|
}
|
|
|
|
for _, tc := range testCases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
var buf bytes.Buffer
|
|
err := download(
|
|
t.Context(),
|
|
tc.url,
|
|
tc.target,
|
|
&buf,
|
|
tc.insecureSkipTLSVerify,
|
|
"",
|
|
tc.bslCACert,
|
|
)
|
|
|
|
if tc.expectedError {
|
|
require.Error(t, err)
|
|
if tc.expectedErrContains != "" {
|
|
assert.Contains(t, err.Error(), tc.expectedErrContains)
|
|
}
|
|
} else {
|
|
require.NoError(t, err)
|
|
assert.Equal(t, tc.expectedContent, buf.String())
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
// TestCACertFallback tests the fallback behavior when caCertFile fails
|
|
func TestCACertFallback(t *testing.T) {
|
|
testContent := "test content for CA cert fallback"
|
|
|
|
// Create self-signed certificate for TLS testing
|
|
tlsCert, serverCACertPEM := createSelfSignedCertificate(t)
|
|
|
|
// Create TLS test server
|
|
tlsServer := httptest.NewUnstartedServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
w.WriteHeader(http.StatusOK)
|
|
w.Write([]byte(testContent))
|
|
}))
|
|
tlsServer.TLS = &tls.Config{
|
|
Certificates: []tls.Certificate{tlsCert},
|
|
}
|
|
tlsServer.StartTLS()
|
|
defer tlsServer.Close()
|
|
|
|
// Create a temporary file path that doesn't exist
|
|
nonExistentFile := "/tmp/non-existent-ca-cert-file.pem"
|
|
|
|
testCases := []struct {
|
|
name string
|
|
caCertFile string
|
|
bslCACert string
|
|
expectedError bool
|
|
expectedErrContains string
|
|
expectedContent string
|
|
}{
|
|
{
|
|
name: "successful download with BSL cert when caCertFile fails",
|
|
caCertFile: nonExistentFile,
|
|
bslCACert: string(serverCACertPEM),
|
|
expectedError: false,
|
|
expectedContent: testContent,
|
|
},
|
|
{
|
|
name: "failed download when both caCertFile and BSL cert are invalid",
|
|
caCertFile: nonExistentFile,
|
|
bslCACert: "",
|
|
expectedError: true,
|
|
expectedErrContains: "couldn't open cacert",
|
|
},
|
|
{
|
|
name: "BSL cert used when caCertFile is empty",
|
|
caCertFile: "",
|
|
bslCACert: string(serverCACertPEM),
|
|
expectedError: false,
|
|
expectedContent: testContent,
|
|
},
|
|
{
|
|
name: "successful download with valid caCertFile (BSL cert ignored)",
|
|
caCertFile: "", // Will be set to a valid temp file in test
|
|
bslCACert: "invalid cert that should be ignored",
|
|
expectedError: false,
|
|
expectedContent: testContent,
|
|
},
|
|
}
|
|
|
|
for _, tc := range testCases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
caCertFile := tc.caCertFile
|
|
|
|
// For the last test case, create a valid temp file
|
|
if tc.name == "successful download with valid caCertFile (BSL cert ignored)" {
|
|
tmpFile, err := os.CreateTemp(t.TempDir(), "test-ca-cert-*.pem")
|
|
require.NoError(t, err)
|
|
defer os.Remove(tmpFile.Name())
|
|
|
|
_, err = tmpFile.Write(serverCACertPEM)
|
|
require.NoError(t, err)
|
|
tmpFile.Close()
|
|
|
|
caCertFile = tmpFile.Name()
|
|
}
|
|
|
|
var buf bytes.Buffer
|
|
err := download(
|
|
t.Context(),
|
|
tlsServer.URL,
|
|
velerov1api.DownloadTargetKindBackupContents,
|
|
&buf,
|
|
false,
|
|
caCertFile,
|
|
tc.bslCACert,
|
|
)
|
|
|
|
if tc.expectedError {
|
|
require.Error(t, err)
|
|
if tc.expectedErrContains != "" {
|
|
assert.Contains(t, err.Error(), tc.expectedErrContains)
|
|
}
|
|
} else {
|
|
require.NoError(t, err)
|
|
assert.Equal(t, tc.expectedContent, buf.String())
|
|
}
|
|
})
|
|
}
|
|
}
|