From 7fba71bd2ed98ad1f7c48b2537539dd9bf5ae80b Mon Sep 17 00:00:00 2001 From: Nicolas Bigler Date: Tue, 4 Jul 2023 18:23:24 +0200 Subject: [PATCH] Add support for restoring redis backups Signed-off-by: Nicolas Bigler --- apis/vshn/v1/common_types.go | 11 + apis/vshn/v1/dbaas_vshn_redis.go | 3 + apis/vshn/v1/zz_generated.deepcopy.go | 16 + cmd/grpc.go | 4 + crds/vshn.appcat.vshn.io_vshnredis.yaml | 10 + .../functions/vshnredis/restore.go | 314 ++++++++++++++++++ .../functions/vshnredis/restore_test.go | 80 +++++ .../vshnredis/script/cleanupRestore.sh | 20 ++ .../functions/vshnredis/script/prepRestore.sh | 25 ++ .../functions/vshnredis/script/restore.sh | 12 + .../restore/01-GivenNoRestoreConfig.yaml | 103 ++++++ .../restore/01-GivenRestoreConfig.yaml | 108 ++++++ .../restore/01-GivenRestoreConfigNoBN.yaml | 107 ++++++ .../restore/01-GivenRestoreConfigNoCN.yaml | 107 ++++++ 14 files changed, 920 insertions(+) create mode 100644 pkg/comp-functions/functions/vshnredis/restore.go create mode 100644 pkg/comp-functions/functions/vshnredis/restore_test.go create mode 100644 pkg/comp-functions/functions/vshnredis/script/cleanupRestore.sh create mode 100644 pkg/comp-functions/functions/vshnredis/script/prepRestore.sh create mode 100644 pkg/comp-functions/functions/vshnredis/script/restore.sh create mode 100644 test/transforms/vshnredis/restore/01-GivenNoRestoreConfig.yaml create mode 100644 test/transforms/vshnredis/restore/01-GivenRestoreConfig.yaml create mode 100644 test/transforms/vshnredis/restore/01-GivenRestoreConfigNoBN.yaml create mode 100644 test/transforms/vshnredis/restore/01-GivenRestoreConfigNoCN.yaml diff --git a/apis/vshn/v1/common_types.go b/apis/vshn/v1/common_types.go index f8a152154..7d16bc321 100644 --- a/apis/vshn/v1/common_types.go +++ b/apis/vshn/v1/common_types.go @@ -30,6 +30,17 @@ type K8upRetentionPolicy struct { KeepYearly int `json:"keepYearly,omitempty"` } +// K8upRestoreSpec contains restore specific parameters. +type K8upRestoreSpec struct { + + // ClaimName specifies the name of the instance you want to restore from. + // The claim has to be in the same namespace as this new instance. + ClaimName string `json:"claimName,omitempty"` + + // BackupName is the name of the specific backup you want to restore. + BackupName string `json:"backupName,omitempty"` +} + // VSHNDBaaSMaintenanceScheduleSpec contains settings to control the maintenance of an instance. type VSHNDBaaSMaintenanceScheduleSpec struct { // +kubebuilder:validation:Enum=monday;tuesday;wednesday;thursday;friday;saturday;sunday diff --git a/apis/vshn/v1/dbaas_vshn_redis.go b/apis/vshn/v1/dbaas_vshn_redis.go index f6ac7dd9b..24726b06d 100644 --- a/apis/vshn/v1/dbaas_vshn_redis.go +++ b/apis/vshn/v1/dbaas_vshn_redis.go @@ -62,6 +62,9 @@ type VSHNRedisParameters struct { // Backup contains settings to control how the instance should get backed up. Backup K8upBackupSpec `json:"backup,omitempty"` + // Restore contains settings to control the restore of an instance. + Restore K8upRestoreSpec `json:"restore,omitempty"` + // Maintenance contains settings to control the maintenance of an instance. Maintenance VSHNDBaaSMaintenanceScheduleSpec `json:"maintenance,omitempty"` } diff --git a/apis/vshn/v1/zz_generated.deepcopy.go b/apis/vshn/v1/zz_generated.deepcopy.go index 7142080fb..a656adae1 100644 --- a/apis/vshn/v1/zz_generated.deepcopy.go +++ b/apis/vshn/v1/zz_generated.deepcopy.go @@ -27,6 +27,21 @@ func (in *K8upBackupSpec) DeepCopy() *K8upBackupSpec { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *K8upRestoreSpec) DeepCopyInto(out *K8upRestoreSpec) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new K8upRestoreSpec. +func (in *K8upRestoreSpec) DeepCopy() *K8upRestoreSpec { + if in == nil { + return nil + } + out := new(K8upRestoreSpec) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *K8upRetentionPolicy) DeepCopyInto(out *K8upRetentionPolicy) { *out = *in @@ -507,6 +522,7 @@ func (in *VSHNRedisParameters) DeepCopyInto(out *VSHNRedisParameters) { in.Scheduling.DeepCopyInto(&out.Scheduling) out.TLS = in.TLS out.Backup = in.Backup + out.Restore = in.Restore out.Maintenance = in.Maintenance } diff --git a/cmd/grpc.go b/cmd/grpc.go index 39febdb51..a0f7ff526 100644 --- a/cmd/grpc.go +++ b/cmd/grpc.go @@ -111,6 +111,10 @@ var images = map[string][]runtime.Transform{ Name: "backup", TransformFunc: vshnredis.AddBackup, }, + { + Name: "restore", + TransformFunc: vshnredis.RestoreBackup, + }, { Name: "maintenance", TransformFunc: vshnredis.AddMaintenanceJob, diff --git a/crds/vshn.appcat.vshn.io_vshnredis.yaml b/crds/vshn.appcat.vshn.io_vshnredis.yaml index c6dfc5929..9ee8adf69 100644 --- a/crds/vshn.appcat.vshn.io_vshnredis.yaml +++ b/crds/vshn.appcat.vshn.io_vshnredis.yaml @@ -78,6 +78,16 @@ spec: pattern: ^([0-1]?[0-9]|2[0-3]):([0-5][0-9]):([0-5][0-9])$ type: string type: object + restore: + description: Rewstore contains settings to control the restore of an instance. + properties: + backupName: + description: BackupName is the name of the specific backup you want to restore. + type: string + claimName: + description: ClaimName specifies the name of the instance you want to restore from. The claim has to be in the same namespace as this new instance. + type: string + type: object scheduling: description: Scheduling contains settings to control the scheduling of an instance. properties: diff --git a/pkg/comp-functions/functions/vshnredis/restore.go b/pkg/comp-functions/functions/vshnredis/restore.go new file mode 100644 index 000000000..53304548b --- /dev/null +++ b/pkg/comp-functions/functions/vshnredis/restore.go @@ -0,0 +1,314 @@ +package vshnredis + +import ( + "context" + _ "embed" + "strings" + + vshnv1 "github.com/vshn/appcat/apis/vshn/v1" + runtime "github.com/vshn/appcat/pkg/comp-functions/runtime" + batchv1 "k8s.io/api/batch/v1" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + controllerruntime "sigs.k8s.io/controller-runtime" +) + +//go:embed script/prepRestore.sh +var prepRestoreScript string + +//go:embed script/restore.sh +var restoreScript string + +//go:embed script/cleanupRestore.sh +var cleanupRestoreScript string + +func RestoreBackup(ctx context.Context, iof *runtime.Runtime) runtime.Result { + log := controllerruntime.LoggerFrom(ctx) + log.Info("Starting RestoreBackup function") + + comp := &vshnv1.VSHNRedis{} + err := iof.Observed.GetComposite(ctx, comp) + if err != nil { + return runtime.NewFatalErr(ctx, "Cannot get composite from function io", err) + } + + // Wait for the next reconciliation in case instance namespace is missing + if comp.Status.InstanceNamespace == "" { + return runtime.NewWarning(ctx, "Composite is missing instance namespace, skipping transformation") + } + + if comp.Spec.Parameters.Restore.BackupName == "" && comp.Spec.Parameters.Restore.ClaimName == "" { + return runtime.NewNormal() + } + + if comp.Spec.Parameters.Restore.ClaimName == "" { + return runtime.NewWarning(ctx, "Composite is missing claimName parameter to restore from backup") + } + + if comp.Spec.Parameters.Restore.BackupName == "" { + return runtime.NewWarning(ctx, "Composite is missing backupName parameter to restore from backup") + } + + log.Info("Prepare for restore") + + err = prepareRestoreJob(ctx, comp, iof) + if err != nil { + return runtime.NewFatalErr(ctx, "Can't deploy restore Job", err) + } + + log.Info("Restoring from backup") + + err = deployRestoreJob(ctx, comp, iof) + if err != nil { + return runtime.NewFatalErr(ctx, "Can't restore from backup", err) + } + + log.Info("Cleanup restore") + + err = cleanUpJob(ctx, comp, iof) + if err != nil { + return runtime.NewFatalErr(ctx, "Can't cleanup after restore", err) + } + + log.Info("Finishing Restoring RestoreBackup function") + + return runtime.NewNormal() + +} + +func prepareRestoreJob(ctx context.Context, comp *vshnv1.VSHNRedis, iof *runtime.Runtime) error { + prepRestoreJobName := truncateObjectName(comp.Name + "-" + comp.Spec.Parameters.Restore.BackupName + "-prepare-job") + claimNamespaceLabel := "crossplane.io/claim-namespace" + + prepJob := &batchv1.Job{ + ObjectMeta: metav1.ObjectMeta{ + Name: prepRestoreJobName, + Namespace: iof.Config.Data["controlNamespace"], + }, + Spec: batchv1.JobSpec{ + Template: corev1.PodTemplateSpec{ + Spec: corev1.PodSpec{ + RestartPolicy: "Never", + ServiceAccountName: iof.Config.Data["restoreSA"], + Containers: []corev1.Container{ + { + Name: "copyjob", + Image: "bitnami/kubectl:latest", + Command: []string{ + "bash", + "-c", + }, + Args: []string{prepRestoreScript}, + Env: []corev1.EnvVar{ + { + Name: "CLAIM_NAMESPACE", + Value: comp.ObjectMeta.Labels[claimNamespaceLabel], + }, + { + Name: "CLAIM_NAME", + Value: comp.Spec.Parameters.Restore.ClaimName, + }, + { + Name: "BACKUP_NAME", + Value: comp.Spec.Parameters.Restore.BackupName, + }, + { + Name: "TARGET_NAMESPACE", + Value: comp.Status.InstanceNamespace, + }, + }, + }, + }, + }, + }, + }, + } + + return iof.Desired.PutIntoObject(ctx, prepJob, prepRestoreJobName) +} + +func deployRestoreJob(ctx context.Context, comp *vshnv1.VSHNRedis, iof *runtime.Runtime) error { + restoreJobName := truncateObjectName(comp.Name + "-" + comp.Spec.Parameters.Restore.BackupName + "-restore-job") + restoreSecret := "restore-credentials-" + comp.Spec.Parameters.Restore.BackupName + + job := &batchv1.Job{ + ObjectMeta: metav1.ObjectMeta{ + Name: restoreJobName, + Namespace: comp.Status.InstanceNamespace, + }, + Spec: batchv1.JobSpec{ + Template: corev1.PodTemplateSpec{ + Spec: corev1.PodSpec{ + Volumes: []corev1.Volume{ + { + Name: "data", + VolumeSource: corev1.VolumeSource{ + PersistentVolumeClaim: &corev1.PersistentVolumeClaimVolumeSource{ + ClaimName: "redis-data-redis-master-0", + }, + }, + }, + }, + RestartPolicy: "Never", + Containers: []corev1.Container{ + { + Name: "restic", + Image: "ghcr.io/k8up-io/k8up:master", + Command: []string{ + "bash", + "-c", + }, + Args: []string{restoreScript}, + Env: []corev1.EnvVar{ + { + Name: "AWS_ACCESS_KEY_ID", + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: restoreSecret, + }, + Key: "AWS_ACCESS_KEY_ID", + }, + }, + }, + { + Name: "AWS_SECRET_ACCESS_KEY", + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: restoreSecret, + }, + Key: "AWS_SECRET_ACCESS_KEY", + }, + }, + }, + { + Name: "RESTIC_PASSWORD", + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: restoreSecret, + }, + Key: "RESTIC_PASSWORD", + }, + }, + }, + { + Name: "RESTIC_REPOSITORY", + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: restoreSecret, + }, + Key: "RESTIC_REPOSITORY", + }, + }, + }, + { + Name: "BACKUP_NAME", + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: restoreSecret, + }, + Key: "BACKUP_NAME", + }, + }, + }, + { + Name: "BACKUP_PATH", + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: restoreSecret, + }, + Key: "BACKUP_PATH", + }, + }, + }, + }, + VolumeMounts: []corev1.VolumeMount{ + { + Name: "data", + MountPath: "/data", + }, + }, + }, + }, + }, + }, + }, + } + + return iof.Desired.PutIntoObject(ctx, job, restoreJobName) +} + +func cleanUpJob(ctx context.Context, comp *vshnv1.VSHNRedis, iof *runtime.Runtime) error { + cleanupRestoreJobName := truncateObjectName(comp.Name + "-" + comp.Spec.Parameters.Restore.BackupName + "-cleanup-job") + restoreJobName := truncateObjectName(comp.Name + "-" + comp.Spec.Parameters.Restore.BackupName + "-restore-job") + restoreSecret := "statefulset-replicas-" + comp.Spec.Parameters.Restore.ClaimName + "-" + comp.Spec.Parameters.Restore.BackupName + + prepJob := &batchv1.Job{ + ObjectMeta: metav1.ObjectMeta{ + Name: cleanupRestoreJobName, + Namespace: iof.Config.Data["controlNamespace"], + }, + Spec: batchv1.JobSpec{ + Template: corev1.PodTemplateSpec{ + Spec: corev1.PodSpec{ + RestartPolicy: "Never", + ServiceAccountName: iof.Config.Data["restoreSA"], + Containers: []corev1.Container{ + { + Name: "copyjob", + Image: "bitnami/kubectl:latest", + Command: []string{ + "bash", + "-c", + }, + Args: []string{cleanupRestoreScript}, + Env: []corev1.EnvVar{ + { + Name: "CLAIM_NAME", + Value: comp.Spec.Parameters.Restore.ClaimName, + }, + { + Name: "RESTORE_JOB_NAME", + Value: restoreJobName, + }, + { + Name: "BACKUP_NAME", + Value: comp.Spec.Parameters.Restore.BackupName, + }, + { + Name: "TARGET_NAMESPACE", + Value: comp.Status.InstanceNamespace, + }, + { + Name: "NUM_REPLICAS", + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: restoreSecret, + }, + Key: "NUM_REPLICAS", + }, + }, + }, + }, + }, + }, + }, + }, + }, + } + + return iof.Desired.PutIntoObject(ctx, prepJob, cleanupRestoreJobName) +} + +func truncateObjectName(s string) string { + if 63 > len(s) { + return s + } + return s[:strings.LastIndex(s[:63], " ")] +} diff --git a/pkg/comp-functions/functions/vshnredis/restore_test.go b/pkg/comp-functions/functions/vshnredis/restore_test.go new file mode 100644 index 000000000..16e23e496 --- /dev/null +++ b/pkg/comp-functions/functions/vshnredis/restore_test.go @@ -0,0 +1,80 @@ +package vshnredis + +// test cases for the function RestoreBackup + +import ( + "context" + + "testing" + + xkube "github.com/crossplane-contrib/provider-kubernetes/apis/object/v1alpha1" + "github.com/vshn/appcat/pkg/comp-functions/functions/commontest" + "github.com/vshn/appcat/pkg/comp-functions/runtime" + batchv1 "k8s.io/api/batch/v1" + "sigs.k8s.io/yaml" + + "github.com/stretchr/testify/assert" +) + +func TestRestoreBackup_NoConfig(t *testing.T) { + ctx := context.Background() + expectResult := runtime.NewWarning(ctx, "Composite is missing instance namespace, skipping transformation") + + t.Run("WhenNoRestore_ThenNoErrorAndNoChanges", func(t *testing.T) { + + // Given + io := commontest.LoadRuntimeFromFile(t, "vshnredis/restore/01-GivenNoRestoreConfig.yaml") + + // When + result := RestoreBackup(ctx, io) + + // Then + assert.Equal(t, expectResult, result) + }) +} + +func TestRestoreBackup_IncompleteConfig(t *testing.T) { + ctx := context.Background() + + expectResultCN := runtime.NewWarning(ctx, "Composite is missing claimName parameter to restore from backup") + expectResultBN := runtime.NewWarning(ctx, "Composite is missing backupName parameter to restore from backup") + + t.Run("WhenNoRestore_ThenNoErrorAndNoChanges", func(t *testing.T) { + + // Given + io := commontest.LoadRuntimeFromFile(t, "vshnredis/restore/01-GivenRestoreConfigNoCN.yaml") + // When + resultCN := RestoreBackup(ctx, io) + + // Then + assert.Equal(t, expectResultCN, resultCN) + + // Given + io = commontest.LoadRuntimeFromFile(t, "vshnredis/restore/01-GivenRestoreConfigNoBN.yaml") + + // When + resultBN := RestoreBackup(ctx, io) + + // Then + assert.Equal(t, expectResultBN, resultBN) + }) +} + +func TestRestoreBackup(t *testing.T) { + ctx := context.Background() + + // return Normal and new job resources in Desired + io := commontest.LoadRuntimeFromFile(t, "vshnredis/restore/01-GivenRestoreConfig.yaml") + + result := RestoreBackup(ctx, io) + assert.Equal(t, runtime.NewNormal(), result) + + resNamePrepJob := "redis-gc9x4-bar-prepare-job" + kubeObjectPrepJob := &xkube.Object{} + assert.NoError(t, io.Desired.Get(ctx, kubeObjectPrepJob, resNamePrepJob)) + + j := &batchv1.Job{} + + assert.NoError(t, yaml.Unmarshal(kubeObjectPrepJob.Spec.ForProvider.Manifest.Raw, j)) + assert.Equal(t, resNamePrepJob, j.ObjectMeta.Name) +} diff --git a/pkg/comp-functions/functions/vshnredis/script/cleanupRestore.sh b/pkg/comp-functions/functions/vshnredis/script/cleanupRestore.sh new file mode 100644 index 000000000..ffdd0189d --- /dev/null +++ b/pkg/comp-functions/functions/vshnredis/script/cleanupRestore.sh @@ -0,0 +1,20 @@ +#!/bin/bash + +set -euo pipefail + +echo "Wait for restore to complete" + +until [ "$(kubectl -n "${TARGET_NAMESPACE}" get job "${RESTORE_JOB_NAME}" -o jsonpath='{.status.succeeded}')" -eq 1 ]; +do + sleep 1 +done + +echo "scaling up redis" + +kubectl -n "${TARGET_NAMESPACE}" scale statefulset redis-master --replicas "${NUM_REPLICAS}" + +echo "cleanup secret" + +kubectl -n "${TARGET_NAMESPACE}" delete secret "restore-credentials-${BACKUP_NAME}" +kubectl delete secret "statefulset-replicas-${CLAIM_NAME}-${BACKUP_NAME}" + diff --git a/pkg/comp-functions/functions/vshnredis/script/prepRestore.sh b/pkg/comp-functions/functions/vshnredis/script/prepRestore.sh new file mode 100644 index 000000000..a5e1c279f --- /dev/null +++ b/pkg/comp-functions/functions/vshnredis/script/prepRestore.sh @@ -0,0 +1,25 @@ +#!/bin/bash + +set -euo pipefail + +source_namespace=$(kubectl -n "${CLAIM_NAMESPACE}" get vshnredis "${CLAIM_NAME}" -ojson | jq -r '.status.instanceNamespace') + +echo "copy secret" + +access_key=$(kubectl -n "${source_namespace}" get secret backup-bucket-credentials -o template='{{ .data.AWS_ACCESS_KEY_ID | base64decode }}') +secret_key=$(kubectl -n "${source_namespace}" get secret backup-bucket-credentials -o template='{{ .data.AWS_SECRET_ACCESS_KEY | base64decode }}') +restic_password=$(kubectl -n "${source_namespace}" get secret k8up-repository-password -o template='{{ .data.password | base64decode }}') +restic_repository=$(kubectl -n "${source_namespace}" get snapshots.k8up.io "${BACKUP_NAME}" -o jsonpath='{.spec.repository}') +backup_path=$(kubectl -n "${source_namespace}" get snapshots.k8up.io "${BACKUP_NAME}" -o jsonpath='{.spec.paths[0]}') +backup_name=$(kubectl -n "${source_namespace}" get snapshots.k8up.io "${BACKUP_NAME}" -o jsonpath='{.spec.id}') +num_replicas=$(kubectl -n "${TARGET_NAMESPACE}" get statefulset redis-master -o jsonpath='{.spec.replicas}') +kubectl -n "${TARGET_NAMESPACE}" create secret generic "restore-credentials-${BACKUP_NAME}" --from-literal AWS_ACCESS_KEY_ID="${access_key}" --from-literal AWS_SECRET_ACCESS_KEY="${secret_key}" --from-literal RESTIC_PASSWORD="${restic_password}" --from-literal RESTIC_REPOSITORY="${restic_repository}" --from-literal BACKUP_PATH="${backup_path}" --from-literal BACKUP_NAME="${backup_name}" +kubectl create secret generic "statefulset-replicas-${CLAIM_NAME}-${BACKUP_NAME}" --from-literal NUM_REPLICAS="${num_replicas}" +echo "scaling down redis" + +until kubectl -n "${TARGET_NAMESPACE}" get statefulset redis-master +do + sleep 1 +done + +kubectl -n "${TARGET_NAMESPACE}" scale statefulset redis-master --replicas 0 diff --git a/pkg/comp-functions/functions/vshnredis/script/restore.sh b/pkg/comp-functions/functions/vshnredis/script/restore.sh new file mode 100644 index 000000000..4923f029a --- /dev/null +++ b/pkg/comp-functions/functions/vshnredis/script/restore.sh @@ -0,0 +1,12 @@ +#!/bin/bash + +set -euo pipefail + +rm -rf /data/* +restic dump "${BACKUP_NAME}" "${BACKUP_PATH}" > /data/restore.tar +cd /data +tar xvf restore.tar +mv data/* . +rmdir data +rm restore.tar + diff --git a/test/transforms/vshnredis/restore/01-GivenNoRestoreConfig.yaml b/test/transforms/vshnredis/restore/01-GivenNoRestoreConfig.yaml new file mode 100644 index 000000000..76110e401 --- /dev/null +++ b/test/transforms/vshnredis/restore/01-GivenNoRestoreConfig.yaml @@ -0,0 +1,103 @@ +apiVersion: apiextensions.crossplane.io/v1alpha1 +kind: FunctionIO +observed: + composite: + resource: + apiVersion: vshn.appcat.vshn.io/v1 + kind: XVSHNRedis + metadata: + annotations: + creationTimestamp: "2023-03-21T16:52:31Z" + finalizers: + - composite.apiextensions.crossplane.io + generateName: redis- + generation: 13 + labels: + appuio.io/organization: vshn + crossplane.io/claim-name: redis + crossplane.io/claim-namespace: unit-test + crossplane.io/composite: redis-gc9x4 + name: redis-gc9x4 + spec: + claimRef: + apiVersion: vshn.appcat.vshn.io/v1 + kind: VSHNRedis + name: redis + namespace: unit-test + compositionRef: + name: vshnredis.vshn.appcat.vshn.io + compositionRevisionRef: + name: vshnredis.vshn.appcat.vshn.io-ce52f13 + compositionUpdatePolicy: Automatic + parameters: null +desired: + composite: + connectionDetails: null + resource: + apiVersion: vshn.appcat.vshn.io/v1 + kind: XVSHNRedis + metadata: + creationTimestamp: "2023-03-21T16:52:31Z" + finalizers: + - composite.apiextensions.crossplane.io + generateName: redis- + generation: 13 + labels: + appuio.io/organization: vshn + crossplane.io/claim-name: redis + crossplane.io/claim-namespace: unit-test + crossplane.io/composite: redis-gc9x4 + name: redis-gc9x4 + spec: + parameters: null + writeConnectionSecretToRef: {} + status: {} + resources: + - name: release + resource: + apiVersion: helm.crossplane.io/v1beta1 + kind: Release + spec: + forProvider: + chart: + name: redis + repository: https://charts.bitnami.com/bitnami + version: 17.6.0 + values: + architecture: standalone + commonConfiguration: 'dummy' + fullnameOverride: redis + global: + imageRegistry: 'dummy' + image: + repository: bitnami/redis + tag: 'dummy' + master: + containerSecurityContext: + enabled: true + persistence: + size: '7Gi' + podSecurityContext: + enabled: true + resources: + limits: + cpu: '10m' + memory: '1Gi' + requests: + cpu: '10m' + memory: '1Gi' + networkPolicy: + allowExternal: false + enabled: true + ingressNSMatchLabels: + kubernetes.io/metadata.name: 'dummy' + tls: + authClients: true + autoGenerated: false + certCAFilename: ca.crt + certFilename: tls.crt + certKeyFilename: tls.key + enabled: true + existingSecret: tls-server-certificate + providerConfigRef: + name: helm diff --git a/test/transforms/vshnredis/restore/01-GivenRestoreConfig.yaml b/test/transforms/vshnredis/restore/01-GivenRestoreConfig.yaml new file mode 100644 index 000000000..f38012ec4 --- /dev/null +++ b/test/transforms/vshnredis/restore/01-GivenRestoreConfig.yaml @@ -0,0 +1,108 @@ +apiVersion: apiextensions.crossplane.io/v1alpha1 +kind: FunctionIO +observed: + composite: + resource: + apiVersion: vshn.appcat.vshn.io/v1 + kind: XVSHNRedis + metadata: + annotations: + creationTimestamp: "2023-03-21T16:52:31Z" + finalizers: + - composite.apiextensions.crossplane.io + generateName: redis- + generation: 13 + labels: + appuio.io/organization: vshn + crossplane.io/claim-name: redis + crossplane.io/claim-namespace: unit-test + crossplane.io/composite: redis-gc9x4 + name: redis-gc9x4 + spec: + claimRef: + apiVersion: vshn.appcat.vshn.io/v1 + kind: VSHNRedis + name: redis + namespace: unit-test + compositionRef: + name: vshnredis.vshn.appcat.vshn.io + compositionRevisionRef: + name: vshnredis.vshn.appcat.vshn.io-ce52f13 + compositionUpdatePolicy: Automatic + parameters: + restore: + claimName: foo + backupName: bar + status: + instanceNamespace: my-redis +desired: + composite: + connectionDetails: null + resource: + apiVersion: vshn.appcat.vshn.io/v1 + kind: XVSHNRedis + metadata: + creationTimestamp: "2023-03-21T16:52:31Z" + finalizers: + - composite.apiextensions.crossplane.io + generateName: redis- + generation: 13 + labels: + appuio.io/organization: vshn + crossplane.io/claim-name: redis + crossplane.io/claim-namespace: unit-test + crossplane.io/composite: redis-gc9x4 + name: redis-gc9x4 + spec: + parameters: null + writeConnectionSecretToRef: {} + status: {} + resources: + - name: release + resource: + apiVersion: helm.crossplane.io/v1beta1 + kind: Release + spec: + forProvider: + chart: + name: redis + repository: https://charts.bitnami.com/bitnami + version: 17.6.0 + values: + architecture: standalone + commonConfiguration: 'dummy' + fullnameOverride: redis + global: + imageRegistry: 'dummy' + image: + repository: bitnami/redis + tag: 'dummy' + master: + containerSecurityContext: + enabled: true + persistence: + size: '7Gi' + podSecurityContext: + enabled: true + resources: + limits: + cpu: '10m' + memory: '1Gi' + requests: + cpu: '10m' + memory: '1Gi' + networkPolicy: + allowExternal: false + enabled: true + ingressNSMatchLabels: + kubernetes.io/metadata.name: 'dummy' + tls: + authClients: true + autoGenerated: false + certCAFilename: ca.crt + certFilename: tls.crt + certKeyFilename: tls.key + enabled: true + existingSecret: tls-server-certificate + providerConfigRef: + name: helm diff --git a/test/transforms/vshnredis/restore/01-GivenRestoreConfigNoBN.yaml b/test/transforms/vshnredis/restore/01-GivenRestoreConfigNoBN.yaml new file mode 100644 index 000000000..8830fff44 --- /dev/null +++ b/test/transforms/vshnredis/restore/01-GivenRestoreConfigNoBN.yaml @@ -0,0 +1,107 @@ +apiVersion: apiextensions.crossplane.io/v1alpha1 +kind: FunctionIO +observed: + composite: + resource: + apiVersion: vshn.appcat.vshn.io/v1 + kind: XVSHNRedis + metadata: + annotations: + creationTimestamp: "2023-03-21T16:52:31Z" + finalizers: + - composite.apiextensions.crossplane.io + generateName: redis- + generation: 13 + labels: + appuio.io/organization: vshn + crossplane.io/claim-name: redis + crossplane.io/claim-namespace: unit-test + crossplane.io/composite: redis-gc9x4 + name: redis-gc9x4 + spec: + claimRef: + apiVersion: vshn.appcat.vshn.io/v1 + kind: VSHNRedis + name: redis + namespace: unit-test + compositionRef: + name: vshnredis.vshn.appcat.vshn.io + compositionRevisionRef: + name: vshnredis.vshn.appcat.vshn.io-ce52f13 + compositionUpdatePolicy: Automatic + parameters: + restore: + claimName: foobar + status: + instanceNamespace: my-redis +desired: + composite: + connectionDetails: null + resource: + apiVersion: vshn.appcat.vshn.io/v1 + kind: XVSHNRedis + metadata: + creationTimestamp: "2023-03-21T16:52:31Z" + finalizers: + - composite.apiextensions.crossplane.io + generateName: redis- + generation: 13 + labels: + appuio.io/organization: vshn + crossplane.io/claim-name: redis + crossplane.io/claim-namespace: unit-test + crossplane.io/composite: redis-gc9x4 + name: redis-gc9x4 + spec: + parameters: null + writeConnectionSecretToRef: {} + status: {} + resources: + - name: release + resource: + apiVersion: helm.crossplane.io/v1beta1 + kind: Release + spec: + forProvider: + chart: + name: redis + repository: https://charts.bitnami.com/bitnami + version: 17.6.0 + values: + architecture: standalone + commonConfiguration: 'dummy' + fullnameOverride: redis + global: + imageRegistry: 'dummy' + image: + repository: bitnami/redis + tag: 'dummy' + master: + containerSecurityContext: + enabled: true + persistence: + size: '7Gi' + podSecurityContext: + enabled: true + resources: + limits: + cpu: '10m' + memory: '1Gi' + requests: + cpu: '10m' + memory: '1Gi' + networkPolicy: + allowExternal: false + enabled: true + ingressNSMatchLabels: + kubernetes.io/metadata.name: 'dummy' + tls: + authClients: true + autoGenerated: false + certCAFilename: ca.crt + certFilename: tls.crt + certKeyFilename: tls.key + enabled: true + existingSecret: tls-server-certificate + providerConfigRef: + name: helm diff --git a/test/transforms/vshnredis/restore/01-GivenRestoreConfigNoCN.yaml b/test/transforms/vshnredis/restore/01-GivenRestoreConfigNoCN.yaml new file mode 100644 index 000000000..a8bd91765 --- /dev/null +++ b/test/transforms/vshnredis/restore/01-GivenRestoreConfigNoCN.yaml @@ -0,0 +1,107 @@ +apiVersion: apiextensions.crossplane.io/v1alpha1 +kind: FunctionIO +observed: + composite: + resource: + apiVersion: vshn.appcat.vshn.io/v1 + kind: XVSHNRedis + metadata: + annotations: + creationTimestamp: "2023-03-21T16:52:31Z" + finalizers: + - composite.apiextensions.crossplane.io + generateName: redis- + generation: 13 + labels: + appuio.io/organization: vshn + crossplane.io/claim-name: redis + crossplane.io/claim-namespace: unit-test + crossplane.io/composite: redis-gc9x4 + name: redis-gc9x4 + spec: + claimRef: + apiVersion: vshn.appcat.vshn.io/v1 + kind: VSHNRedis + name: redis + namespace: unit-test + compositionRef: + name: vshnredis.vshn.appcat.vshn.io + compositionRevisionRef: + name: vshnredis.vshn.appcat.vshn.io-ce52f13 + compositionUpdatePolicy: Automatic + parameters: + restore: + backupName: foobar + status: + instanceNamespace: my-redis +desired: + composite: + connectionDetails: null + resource: + apiVersion: vshn.appcat.vshn.io/v1 + kind: XVSHNRedis + metadata: + creationTimestamp: "2023-03-21T16:52:31Z" + finalizers: + - composite.apiextensions.crossplane.io + generateName: redis- + generation: 13 + labels: + appuio.io/organization: vshn + crossplane.io/claim-name: redis + crossplane.io/claim-namespace: unit-test + crossplane.io/composite: redis-gc9x4 + name: redis-gc9x4 + spec: + parameters: null + writeConnectionSecretToRef: {} + status: {} + resources: + - name: release + resource: + apiVersion: helm.crossplane.io/v1beta1 + kind: Release + spec: + forProvider: + chart: + name: redis + repository: https://charts.bitnami.com/bitnami + version: 17.6.0 + values: + architecture: standalone + commonConfiguration: 'dummy' + fullnameOverride: redis + global: + imageRegistry: 'dummy' + image: + repository: bitnami/redis + tag: 'dummy' + master: + containerSecurityContext: + enabled: true + persistence: + size: '7Gi' + podSecurityContext: + enabled: true + resources: + limits: + cpu: '10m' + memory: '1Gi' + requests: + cpu: '10m' + memory: '1Gi' + networkPolicy: + allowExternal: false + enabled: true + ingressNSMatchLabels: + kubernetes.io/metadata.name: 'dummy' + tls: + authClients: true + autoGenerated: false + certCAFilename: ca.crt + certFilename: tls.crt + certKeyFilename: tls.key + enabled: true + existingSecret: tls-server-certificate + providerConfigRef: + name: helm