Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

OADP-639: Pass custom CA configuration to restic replication source. #198

Merged
merged 6 commits into from
Apr 11, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
11 changes: 11 additions & 0 deletions controllers/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ const (
GoogleApplicationCredentials = "GOOGLE_APPLICATION_CREDENTIALS"

// Restic repo vars
ResticCustomCA = "RESTIC_CUSTOM_CA"
ResticPassword = "RESTIC_PASSWORD"
ResticRepository = "RESTIC_REPOSITORY"
ResticPruneInterval = "restic-prune-interval"
Expand Down Expand Up @@ -103,6 +104,7 @@ var (

GoogleApplicationCredentialsValue []byte

ResticCustomCAValue []byte
ResticPasswordValue []byte
ResticRepoValue string
ResticPruneIntervalValue []byte
Expand Down Expand Up @@ -161,6 +163,8 @@ func BuildResticSecret(givensecret *corev1.Secret, secret *corev1.Secret, restic
AWSDefaultRegionValue = val
case key == ResticPassword:
ResticPasswordValue = val
case key == ResticCustomCA:
ResticCustomCAValue = val
}
}

Expand All @@ -170,6 +174,7 @@ func BuildResticSecret(givensecret *corev1.Secret, secret *corev1.Secret, restic
AWSAccessKey: AWSAccessValue,
AWSSecretKey: AWSSecretValue,
AWSDefaultRegion: AWSDefaultRegionValue,
ResticCustomCA: ResticCustomCAValue,
ResticPassword: ResticPasswordValue,
ResticRepository: []byte(resticrepo),
ResticPruneInterval: []byte(pruneInterval),
Expand All @@ -188,6 +193,8 @@ func BuildResticSecret(givensecret *corev1.Secret, secret *corev1.Secret, restic
AzureAccountKeyValue = val
case key == ResticPassword:
ResticPasswordValue = val
case key == ResticCustomCA:
ResticCustomCAValue = val
}
}

Expand All @@ -196,6 +203,7 @@ func BuildResticSecret(givensecret *corev1.Secret, secret *corev1.Secret, restic
Data: map[string][]byte{
AzureAccountName: AzureAccountNameValue,
AzureAccountKey: AzureAccountKeyValue,
ResticCustomCA: ResticCustomCAValue,
ResticPassword: ResticPasswordValue,
ResticRepository: []byte(resticrepo),
ResticPruneInterval: []byte(pruneInterval),
Expand All @@ -212,13 +220,16 @@ func BuildResticSecret(givensecret *corev1.Secret, secret *corev1.Secret, restic
GoogleApplicationCredentialsValue = val
case key == ResticPassword:
ResticPasswordValue = val
case key == ResticCustomCA:
ResticCustomCAValue = val
}
}

// build new Restic secret
resticSecretData := &corev1.Secret{
Data: map[string][]byte{
GoogleApplicationCredentials: GoogleApplicationCredentialsValue,
ResticCustomCA: ResticCustomCAValue,
ResticPassword: ResticPasswordValue,
ResticRepository: []byte(resticrepo),
ResticPruneInterval: []byte(pruneInterval),
Expand Down
7 changes: 7 additions & 0 deletions controllers/replicationdestination.go
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,13 @@ func (r *VolumeSnapshotRestoreReconciler) buildReplicationDestination(replicatio
replicationDestination.Spec = replicationDestinationSpec
}

// include custom CA if specified
resticCustomCA := resticSecret.Data[ResticCustomCA]
if len(resticCustomCA) > 0 {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The existing tests weren't really set up to check this sort of thing, so I tried adding a field to the test input struct to hold a validation function. I didn't see an easier way to do it.

replicationDestination.Spec.Restic.CustomCA.SecretName = resticSecret.Name
replicationDestination.Spec.Restic.CustomCA.Key = ResticCustomCA
}

return nil
}

Expand Down
68 changes: 68 additions & 0 deletions controllers/replicationdestination_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package controllers

import (
"context"
"fmt"
"testing"

controllerruntime "sigs.k8s.io/controller-runtime"
Expand Down Expand Up @@ -35,6 +36,7 @@ func TestVolumeSnapshotRestoreReconciler_buildReplicationDestination(t *testing.
Scheme *runtime.Scheme
want bool
wantErr bool
validate func(*volsyncv1alpha1.ReplicationDestination) error
}{
// TODO: Add test cases
{
Expand Down Expand Up @@ -201,6 +203,66 @@ func TestVolumeSnapshotRestoreReconciler_buildReplicationDestination(t *testing.
want: false,
wantErr: true,
},
{
name: "Should pass custom CA field through to restic secret",
vsr: &volsnapmoverv1alpha1.VolumeSnapshotRestore{
ObjectMeta: v1.ObjectMeta{
Name: "sample-vsr",
Namespace: "bar",
},
Spec: volsnapmoverv1alpha1.VolumeSnapshotRestoreSpec{
ResticSecretRef: corev1.LocalObjectReference{
Name: "secret",
},
VolumeSnapshotMoverBackupref: volsnapmoverv1alpha1.VSBRef{
BackedUpPVCData: volsnapmoverv1alpha1.PVCData{
Name: "test-pvc",
Size: "1G",
StorageClassName: "test-class",
},
},
ProtectedNamespace: "test-ns",
},
},
repDest: &volsyncv1alpha1.ReplicationDestination{
ObjectMeta: v1.ObjectMeta{
Name: "sample-vsb-rep-src",
Namespace: namespace,
},
},
secret: &corev1.Secret{
ObjectMeta: v1.ObjectMeta{
Name: "test-secret",
Namespace: "test-ns",
},
Data: map[string][]byte{
ResticCustomCA: []byte("test-secret"),
},
},
configMap: &corev1.ConfigMap{
ObjectMeta: v1.ObjectMeta{
Name: "datamover-config",
Namespace: namespace,
},
},
serviceAcct: &corev1.ServiceAccount{
ObjectMeta: v1.ObjectMeta{
Name: "velero",
Namespace: namespace,
},
},
want: true,
wantErr: false,
validate: func(rd *volsyncv1alpha1.ReplicationDestination) error {
if rd.Spec.Restic.CustomCA.Key != ResticCustomCA {
return fmt.Errorf("restic custom CA key name mismatch, got %s, expected %s", rd.Spec.Restic.CustomCA.Key, ResticCustomCA)
}
if rd.Spec.Restic.CustomCA.SecretName != "test-secret" {
return fmt.Errorf("restic custom CA secret name mismatch, got %s, expected %s", rd.Spec.Restic.CustomCA.SecretName, "test-secret")
}
return nil
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
Expand All @@ -222,6 +284,12 @@ func TestVolumeSnapshotRestoreReconciler_buildReplicationDestination(t *testing.
if err == nil && tt.want && !tt.wantErr {
t.Logf("buildReplicationDestination() err = %v, wantErr %v", err, tt.wantErr)
}

if tt.validate != nil {
if err = tt.validate(tt.repDest); err != nil {
t.Errorf("validation error: %v", err)
}
}
})
}
}
7 changes: 7 additions & 0 deletions controllers/replicationsource.go
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,13 @@ func (r *VolumeSnapshotBackupReconciler) buildReplicationSource(replicationSourc
replicationSource.Spec = replicationSourceSpec
}

// pass along a custom CA if specified
resticCustomCA := resticSecret.Data[ResticCustomCA]
if len(resticCustomCA) > 0 {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

replicationSource.Spec.Restic.CustomCA.SecretName = resticSecret.Name
replicationSource.Spec.Restic.CustomCA.Key = ResticCustomCA
}

return nil
}

Expand Down
84 changes: 79 additions & 5 deletions controllers/replicationsource_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package controllers

import (
"context"
"fmt"
"testing"

controllerruntime "sigs.k8s.io/controller-runtime"
Expand All @@ -25,18 +26,19 @@ import (
const (
aws_access_key_id = "some_aws_access_key_id"
aws_secret_access_key = "some_aws_secret_access_key"
restic_custom_ca = "sample-vsb-secret"
restic_password = "some_restic_password"
restic_repo = "some_restic_repo"
namespace = "foo"
)

var (
secretData = map[string][]byte{
"data": []byte(
"AWS_ACCESS_KEY_ID:" + aws_access_key_id + "\n" +
"AWS_SECRET_ACCESS_KEY:" + aws_secret_access_key + "\n" +
"RESTIC_PASSWORD:" + restic_password + "\n" +
"RESTIC_REPOSITORY:" + restic_repo),
"AWS_ACCESS_KEY_ID": []byte(aws_access_key_id),
"AWS_SECRET_ACCESS_KEY": []byte(aws_secret_access_key),
"RESTIC_PASSWORD": []byte(restic_password),
"RESTIC_REPOSITORY": []byte(restic_repo),
"RESTIC_CUSTOM_CA": []byte(restic_custom_ca),
}
)

Expand Down Expand Up @@ -76,6 +78,7 @@ func TestVolumeSnapshotMoverBackupReconciler_BuildReplicationSource(t *testing.T
configMap *corev1.ConfigMap
serviceAcct *corev1.ServiceAccount
wantErr bool
validate func(*volsyncv1alpha1.ReplicationSource) error
}{
// TODO: Add test cases.
{
Expand Down Expand Up @@ -286,6 +289,71 @@ func TestVolumeSnapshotMoverBackupReconciler_BuildReplicationSource(t *testing.T
serviceAcct: nil,
wantErr: true,
},
{
name: "Should pass custom CA field through to restic secret",
vsb: &volsnapmoverv1alpha1.VolumeSnapshotBackup{
ObjectMeta: v1.ObjectMeta{
Name: "sample-vsb",
Namespace: "bar",
},
Spec: volsnapmoverv1alpha1.VolumeSnapshotBackupSpec{
VolumeSnapshotContent: corev1.ObjectReference{
Name: "sample-snapshot",
},
ProtectedNamespace: "foo",
},
},
pvc: &corev1.PersistentVolumeClaim{
ObjectMeta: v1.ObjectMeta{
Name: "sample-pvc",
Namespace: namespace,
},
Spec: corev1.PersistentVolumeClaimSpec{
AccessModes: []corev1.PersistentVolumeAccessMode{corev1.ReadWriteOnce},
Resources: corev1.ResourceRequirements{
Requests: corev1.ResourceList{
corev1.ResourceName(corev1.ResourceStorage): resource.MustParse("10Gi"),
},
},
},
},
secret: &corev1.Secret{
ObjectMeta: v1.ObjectMeta{
Name: restic_custom_ca,
Namespace: namespace,
},
Data: secretData,
},
repsrc: &volsyncv1alpha1.ReplicationSource{
ObjectMeta: v1.ObjectMeta{
Name: "sample-vsb-rep-src",
Namespace: namespace,
},
},
configMap: &corev1.ConfigMap{
ObjectMeta: v1.ObjectMeta{
Name: "datamover-config",
Namespace: namespace,
},
},
serviceAcct: &corev1.ServiceAccount{
ObjectMeta: v1.ObjectMeta{
Name: "velero",
Namespace: namespace,
},
},
wantErr: false,
validate: func(rs *volsyncv1alpha1.ReplicationSource) error {
t.Logf("Secret data: %s", secretData)
if rs.Spec.Restic.CustomCA.Key != ResticCustomCA {
return fmt.Errorf("restic custom CA key name mismatch, got %s, expected %s", rs.Spec.Restic.CustomCA.Key, ResticCustomCA)
}
if rs.Spec.Restic.CustomCA.SecretName != restic_custom_ca {
return fmt.Errorf("restic custom CA secret name mismatch, got %s, expected %s", rs.Spec.Restic.CustomCA.SecretName, restic_custom_ca)
}
return nil
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
Expand Down Expand Up @@ -315,6 +383,12 @@ func TestVolumeSnapshotMoverBackupReconciler_BuildReplicationSource(t *testing.T
t.Errorf("VolumeSnapshotMoverBackupReconciler.buildReplicationSource() error = %v, wantErr %v", err, tt.wantErr)
return
}

if tt.validate != nil {
if err = tt.validate(tt.repsrc); err != nil {
t.Errorf("validation error: %v", err)
}
}
})
}
}
Expand Down