diff --git a/api/v1alpha1/backupconfiguration_types.go b/api/v1alpha1/backupconfiguration_types.go index a5b0c72..a73ba08 100644 --- a/api/v1alpha1/backupconfiguration_types.go +++ b/api/v1alpha1/backupconfiguration_types.go @@ -38,7 +38,10 @@ const ( JobKind BackupType = "Job" ) -const BACKUP_PREFIX_PATH = `backup` +const ( + BACKUP_PREFIX_PATH = `backup` + FORMOL_SHARED_VOLUME = `formol-shared` +) type Step struct { Name string `json:"name"` @@ -52,6 +55,8 @@ type TargetContainer struct { Paths []string `json:"paths,omitempty"` // +optional Steps []Step `json:"steps,omitempty"` + // +kubebuilder:default:=/formol-shared + SharePath string `json:"sharePath"` } type Target struct { diff --git a/api/v1alpha1/repo_types.go b/api/v1alpha1/repo_types.go index 224befe..b4de2d8 100644 --- a/api/v1alpha1/repo_types.go +++ b/api/v1alpha1/repo_types.go @@ -40,7 +40,8 @@ type S3 struct { } type Local struct { - corev1.VolumeSource `json:"source"` + //corev1.VolumeSource `json:"source"` + corev1.VolumeSource `json:",inline"` } type Backend struct { diff --git a/controllers/backupconfiguration_controller_helpers.go b/controllers/backupconfiguration_controller_helpers.go index 0ee8e8f..d6545bf 100644 --- a/controllers/backupconfiguration_controller_helpers.go +++ b/controllers/backupconfiguration_controller_helpers.go @@ -175,25 +175,48 @@ func (r *BackupConfigurationReconciler) DeleteSidecar(backupConf formolv1alpha1. targetObject = &deployment targetPodSpec = &deployment.Spec.Template.Spec + case formolv1alpha1.StatefulSet: + statefulSet := appsv1.StatefulSet{} + if err := r.Get(r.Context, client.ObjectKey{ + Namespace: backupConf.Namespace, + Name: target.TargetName, + }, &statefulSet); err != nil { + r.Log.Error(err, "cannot get deployment", "Deployment", target.TargetName) + return err + } + targetObject = &statefulSet + targetPodSpec = &statefulSet.Spec.Template.Spec + } restoreContainers := []corev1.Container{} for _, container := range targetPodSpec.Containers { if container.Name == formolv1alpha1.SIDECARCONTAINER_NAME { continue } + restoreVms := []corev1.VolumeMount{} + for _, vm := range container.VolumeMounts { + if vm.Name == formolv1alpha1.FORMOL_SHARED_VOLUME { + r.Log.V(0).Info("cleanup VolumeMounts", "container", container.Name, "VolumeMount", vm.Name) + continue + } + restoreVms = append(restoreVms, vm) + } + r.Log.V(0).Info("cleanup VolumeMounts", "container", container.Name, "restoreVms", restoreVms) + container.VolumeMounts = restoreVms restoreContainers = append(restoreContainers, container) } targetPodSpec.Containers = restoreContainers - if repo.Spec.Backend.Local != nil { - restoreVolumes := []corev1.Volume{} - for _, volume := range targetPodSpec.Volumes { - if volume.Name == formolv1alpha1.RESTIC_REPO_VOLUME { - continue - } - restoreVolumes = append(restoreVolumes, volume) + restoreVolumes := []corev1.Volume{} + for _, volume := range targetPodSpec.Volumes { + if volume.Name == formolv1alpha1.RESTIC_REPO_VOLUME { + continue } - targetPodSpec.Volumes = restoreVolumes + if volume.Name == formolv1alpha1.FORMOL_SHARED_VOLUME { + continue + } + restoreVolumes = append(restoreVolumes, volume) } + targetPodSpec.Volumes = restoreVolumes removeTags(targetPodSpec, target) if err := r.Update(r.Context, targetObject); err != nil { r.Log.Error(err, "unable to remove sidecar", "targetObject", targetObject) @@ -257,6 +280,17 @@ func (r *BackupConfigurationReconciler) addSidecar(backupConf formolv1alpha1.Bac } targetObject = &deployment targetPodSpec = &deployment.Spec.Template.Spec + case formolv1alpha1.StatefulSet: + statefulSet := appsv1.StatefulSet{} + if err = r.Get(r.Context, client.ObjectKey{ + Namespace: backupConf.Namespace, + Name: target.TargetName, + }, &statefulSet); err != nil { + r.Log.Error(err, "cannot get deployment", "Deployment", target.TargetName) + return + } + targetObject = &statefulSet + targetPodSpec = &statefulSet.Spec.Template.Spec } if !hasSidecar(targetPodSpec) { if err = r.createRBACSidecar(corev1.ServiceAccount{ @@ -276,6 +310,8 @@ func (r *BackupConfigurationReconciler) addSidecar(backupConf formolv1alpha1.Bac Value: strings.Join(sidecarPaths, string(os.PathListSeparator)), }) sidecar.VolumeMounts = vms + case formolv1alpha1.JobKind: + sidecar.VolumeMounts = addJobSidecarTags(targetPodSpec, target) } if repo.Spec.Backend.Local != nil { sidecar.VolumeMounts = append(sidecar.VolumeMounts, corev1.VolumeMount{ @@ -400,6 +436,45 @@ func (r *BackupConfigurationReconciler) createRBACSidecar(sa corev1.ServiceAccou return nil } +func addJobSidecarTags(podSpec *corev1.PodSpec, target formolv1alpha1.Target) (vms []corev1.VolumeMount) { + for i, container := range podSpec.Containers { + for _, targetContainer := range target.Containers { + if targetContainer.Name == container.Name { + // Found a target container. Tag it. + podSpec.Containers[i].Env = append(container.Env, corev1.EnvVar{ + Name: formolv1alpha1.TARGETCONTAINER_TAG, + Value: container.Name, + }) + // Create a shared mount between the target and sidecar container + // the output of the Job will be saved in the shared volume + // and restic will then backup the content of the volume + var addSharedVol bool = true + for _, vol := range podSpec.Volumes { + if vol.Name == formolv1alpha1.FORMOL_SHARED_VOLUME { + addSharedVol = false + } + } + if addSharedVol { + podSpec.Volumes = append(podSpec.Volumes, + corev1.Volume{ + Name: formolv1alpha1.FORMOL_SHARED_VOLUME, + VolumeSource: corev1.VolumeSource{EmptyDir: &corev1.EmptyDirVolumeSource{}}, + }) + } + podSpec.Containers[i].VolumeMounts = append(podSpec.Containers[i].VolumeMounts, corev1.VolumeMount{ + Name: formolv1alpha1.FORMOL_SHARED_VOLUME, + MountPath: targetContainer.SharePath, + }) + vms = append(vms, corev1.VolumeMount{ + Name: formolv1alpha1.FORMOL_SHARED_VOLUME, + MountPath: targetContainer.SharePath, + }) + } + } + } + return +} + func addOnlineSidecarTags(podSpec *corev1.PodSpec, target formolv1alpha1.Target) (sidecarPaths []string, vms []corev1.VolumeMount) { for i, container := range podSpec.Containers { for _, targetContainer := range target.Containers { diff --git a/test/00-setup.yaml b/test/00-setup.yaml index d186669..2fe5c66 100644 --- a/test/00-setup.yaml +++ b/test/00-setup.yaml @@ -79,8 +79,7 @@ metadata: spec: backend: local: - source: - emptyDir: + emptyDir: repositorySecrets: secret-minio --- apiVersion: formol.desmojim.fr/v1alpha1 diff --git a/test/02-backupconf.yaml b/test/02-backupconf.yaml index 952a6d2..a6a8816 100644 --- a/test/02-backupconf.yaml +++ b/test/02-backupconf.yaml @@ -27,6 +27,11 @@ spec: finalize: true paths: - /data + - backupType: Job + targetKind: StatefulSet + targetName: postgres-demo + containers: + - name: postgres # - kind: Job # name: backup-pg # steps: