Skip to content

Commit

Permalink
feat(ec): DeleteBackup supports deleting new Replicated Backups (#5044
Browse files Browse the repository at this point in the history
)

* feat(ec): `DeleteBackup` supports deleting new Replicated Backups

* chore: add tests
  • Loading branch information
JGAntunes authored Dec 13, 2024
1 parent 83d3f09 commit b6046a0
Show file tree
Hide file tree
Showing 2 changed files with 230 additions and 11 deletions.
41 changes: 30 additions & 11 deletions pkg/kotsadmsnapshot/backup.go
Original file line number Diff line number Diff line change
Expand Up @@ -1103,19 +1103,38 @@ func DeleteBackup(ctx context.Context, kotsadmNamespace string, backupID string)
}

veleroNamespace := bsl.Namespace
veleroDeleteBackupRequest := &velerov1.DeleteBackupRequest{
ObjectMeta: metav1.ObjectMeta{
Name: backupID,
Namespace: veleroNamespace,
},
Spec: velerov1.DeleteBackupRequestSpec{
BackupName: backupID,
},
}
// Default legacy behaviour is to delete the backup whose name matches the backupID
backupsToDelete := []string{backupID}

_, err = veleroClient.DeleteBackupRequests(veleroNamespace).Create(context.TODO(), veleroDeleteBackupRequest, metav1.CreateOptions{})
listOptions := metav1.ListOptions{LabelSelector: fmt.Sprintf("%s=%s", types.InstanceBackupNameLabel, backupID)}
veleroBackups, err := veleroClient.Backups(veleroNamespace).List(ctx, listOptions)
if err != nil {
return errors.Wrap(err, "failed to create delete backup request")
return errors.Wrap(err, "failed to list velero backups for deletion")
}

// If the backupID is a replicated backup, delete all backups with the same name
if len(veleroBackups.Items) > 0 {
backupsToDelete = make([]string, len(veleroBackups.Items))
for i, veleroBackup := range veleroBackups.Items {
backupsToDelete[i] = veleroBackup.Name
}
}

for _, backupToDelete := range backupsToDelete {
veleroDeleteBackupRequest := &velerov1.DeleteBackupRequest{
ObjectMeta: metav1.ObjectMeta{
Name: backupToDelete,
Namespace: veleroNamespace,
},
Spec: velerov1.DeleteBackupRequestSpec{
BackupName: backupToDelete,
},
}

_, err = veleroClient.DeleteBackupRequests(veleroNamespace).Create(ctx, veleroDeleteBackupRequest, metav1.CreateOptions{})
if err != nil {
return errors.Wrap(err, fmt.Sprintf("failed to create delete backup request for backup %s", backupToDelete))
}
}

return nil
Expand Down
200 changes: 200 additions & 0 deletions pkg/kotsadmsnapshot/backup_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3473,3 +3473,203 @@ func TestGetInstanceBackupRestore(t *testing.T) {
})
}
}

func TestDeleteBackup(t *testing.T) {
scheme := runtime.NewScheme()
corev1.AddToScheme(scheme)
embeddedclusterv1beta1.AddToScheme(scheme)

// setup common mock objects
kotsadmNamespace := "kotsadm-test"
testBsl := &velerov1.BackupStorageLocation{
ObjectMeta: metav1.ObjectMeta{
Name: "default",
Namespace: "velero",
},
Spec: velerov1.BackupStorageLocationSpec{
Provider: "aws",
Default: true,
},
}
veleroNamespaceConfigmap := &corev1.ConfigMap{
ObjectMeta: metav1.ObjectMeta{
Name: "kotsadm-velero-namespace",
Namespace: kotsadmNamespace,
},
Data: map[string]string{
"veleroNamespace": "velero",
},
}
veleroDeployment := &appsv1.Deployment{
ObjectMeta: metav1.ObjectMeta{
Name: "velero",
Namespace: "velero",
},
}

tests := []struct {
name string
backupToDelete string
veleroClientBuilder veleroclient.VeleroClientBuilder
k8sClientBuilder k8sclient.K8sClientsetBuilder
expectedDeleteBackupRequests []velerov1.DeleteBackupRequest
wantErr string
}{
{
name: "fails to create k8s clientset",
k8sClientBuilder: &k8sclient.MockBuilder{
Client: nil,
Err: fmt.Errorf("error creating k8s clientset"),
},
veleroClientBuilder: &veleroclient.MockBuilder{
Client: velerofake.NewSimpleClientset().VeleroV1(),
},
wantErr: "failed to create clientset",
},
{
name: "fails to create velero client",
k8sClientBuilder: &k8sclient.MockBuilder{
Client: fake.NewSimpleClientset(),
},
veleroClientBuilder: &veleroclient.MockBuilder{
Client: nil,
Err: fmt.Errorf("error creating velero client"),
},
wantErr: "failed to create velero clientset",
},
{
name: "fails to find backup storage location",
k8sClientBuilder: &k8sclient.MockBuilder{
Client: fake.NewSimpleClientset(),
},
veleroClientBuilder: &veleroclient.MockBuilder{
Client: velerofake.NewSimpleClientset().VeleroV1(),
},
wantErr: "no backup store location found",
},
{
name: "should issue a single delete backup request for the provided id if no backups with label name exist",
backupToDelete: "test-backup",
k8sClientBuilder: &k8sclient.MockBuilder{
Client: fake.NewSimpleClientset(
veleroNamespaceConfigmap,
veleroDeployment,
),
},
veleroClientBuilder: &veleroclient.MockBuilder{
Client: velerofake.NewSimpleClientset(
testBsl,
).VeleroV1(),
},
expectedDeleteBackupRequests: []velerov1.DeleteBackupRequest{
{
ObjectMeta: metav1.ObjectMeta{
Name: "test-backup",
Namespace: "velero",
},
Spec: velerov1.DeleteBackupRequestSpec{
BackupName: "test-backup",
},
},
},
},
{
name: "should issue two delete backup requests for the provided id if two backups with label name exist",
backupToDelete: "aggregated-repl-backup",
k8sClientBuilder: &k8sclient.MockBuilder{
Client: fake.NewSimpleClientset(
veleroNamespaceConfigmap,
veleroDeployment,
),
},
veleroClientBuilder: &veleroclient.MockBuilder{
Client: velerofake.NewSimpleClientset(
testBsl,
&velerov1.Backup{
ObjectMeta: metav1.ObjectMeta{
Name: "infra-backup",
Namespace: "velero",
Labels: map[string]string{
types.InstanceBackupNameLabel: "aggregated-repl-backup",
},
Annotations: map[string]string{
types.InstanceBackupAnnotation: "true",
types.InstanceBackupTypeAnnotation: types.InstanceBackupTypeInfra,
types.InstanceBackupCountAnnotation: "2",
},
},
Status: velerov1.BackupStatus{
Phase: velerov1.BackupPhaseCompleted,
},
},
&velerov1.Backup{
ObjectMeta: metav1.ObjectMeta{
Name: "app-backup",
Namespace: "velero",
Labels: map[string]string{
types.InstanceBackupNameLabel: "aggregated-repl-backup",
},
Annotations: map[string]string{
types.InstanceBackupAnnotation: "true",
types.InstanceBackupTypeAnnotation: types.InstanceBackupTypeApp,
types.InstanceBackupCountAnnotation: "2",
},
},
Status: velerov1.BackupStatus{
Phase: velerov1.BackupPhaseCompleted,
},
},
).VeleroV1(),
},
expectedDeleteBackupRequests: []velerov1.DeleteBackupRequest{
{
ObjectMeta: metav1.ObjectMeta{
Name: "app-backup",
Namespace: "velero",
},
Spec: velerov1.DeleteBackupRequestSpec{
BackupName: "app-backup",
},
},
{
ObjectMeta: metav1.ObjectMeta{
Name: "infra-backup",
Namespace: "velero",
},
Spec: velerov1.DeleteBackupRequestSpec{
BackupName: "infra-backup",
},
},
},
},
}

for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
asrt := assert.New(t)
req := require.New(t)
// setup mock clients
k8sclient.SetBuilder(test.k8sClientBuilder)
veleroclient.SetBuilder(test.veleroClientBuilder)

err := DeleteBackup(context.Background(), kotsadmNamespace, test.backupToDelete)

if test.wantErr != "" {
asrt.Error(err)
asrt.Contains(err.Error(), test.wantErr)
} else {
asrt.NoError(err)
}

if test.expectedDeleteBackupRequests != nil {
// verify delete backup requests
veleroClient, err := test.veleroClientBuilder.GetVeleroClient(nil)
req.NoError(err)

requests, err := veleroClient.DeleteBackupRequests("velero").List(context.Background(), metav1.ListOptions{})
req.NoError(err)
asrt.Equal(test.expectedDeleteBackupRequests, requests.Items)
}
})
}
}

0 comments on commit b6046a0

Please sign in to comment.