diff --git a/Makefile b/Makefile index 5cb3c63..72d373c 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ # Image URL to use all building/pushing image targets -IMG ?= desmo999r/formolcontroller:latest +IMG ?= desmo999r/formolcontroller:0.2.0 # Produce CRDs that work back to Kubernetes 1.11 (no version conversion) #CRD_OPTIONS ?= "crd:trivialVersions=true" CRD_OPTIONS ?= "crd:trivialVersions=true,crdVersions=v1" diff --git a/api/v1alpha1/backupconfiguration_types.go b/api/v1alpha1/backupconfiguration_types.go index 7370544..af85281 100644 --- a/api/v1alpha1/backupconfiguration_types.go +++ b/api/v1alpha1/backupconfiguration_types.go @@ -30,8 +30,6 @@ const ( type Step struct { Name string `json:"name"` // +optional - Env []corev1.EnvVar `json:"env,omitempty"` - // +optional Finalize *bool `json:"finalize,omitempty"` } @@ -46,6 +44,8 @@ type Target struct { Kind string `json:"kind"` Name string `json:"name"` // +optional + ContainerName string `json:"containerName"` + // +optional ApiVersion string `json:"apiVersion,omitempty"` // +optional VolumeMounts []corev1.VolumeMount `json:"volumeMounts,omitempty"` diff --git a/api/v1alpha1/common.go b/api/v1alpha1/common.go index dc49db0..d3b88d4 100644 --- a/api/v1alpha1/common.go +++ b/api/v1alpha1/common.go @@ -19,6 +19,8 @@ const ( RESTORE_ANNOTATION = "restore" // the name of the sidecar container SIDECARCONTAINER_NAME string = "formol" + // the name of the container we backup when there are more than 1 container in the pod + TARGETCONTAINER_TAG string = "FORMOL_TARGET" // Used by both the backupsession and restoresession controllers to identified the target deployment TARGET_NAME string = "TARGET_NAME" // Used by restoresession controller diff --git a/api/v1alpha1/zz_generated.deepcopy.go b/api/v1alpha1/zz_generated.deepcopy.go index 743bb90..52c08e3 100644 --- a/api/v1alpha1/zz_generated.deepcopy.go +++ b/api/v1alpha1/zz_generated.deepcopy.go @@ -569,13 +569,6 @@ func (in *S3) DeepCopy() *S3 { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *Step) DeepCopyInto(out *Step) { *out = *in - if in.Env != nil { - in, out := &in.Env, &out.Env - *out = make([]v1.EnvVar, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } if in.Finalize != nil { in, out := &in.Finalize, &out.Finalize *out = new(bool) diff --git a/config/manager/kustomization.yaml b/config/manager/kustomization.yaml index 985346c..32c70dc 100644 --- a/config/manager/kustomization.yaml +++ b/config/manager/kustomization.yaml @@ -5,4 +5,4 @@ kind: Kustomization images: - name: controller newName: desmo999r/formolcontroller - newTag: latest + newTag: 0.2.0 diff --git a/controllers/backupconfiguration_controller.go b/controllers/backupconfiguration_controller.go index 35fb39e..8aaedf4 100644 --- a/controllers/backupconfiguration_controller.go +++ b/controllers/backupconfiguration_controller.go @@ -51,6 +51,8 @@ var _ reconcile.Reconciler = &BackupConfigurationReconciler{} // +kubebuilder:rbac:groups=apps,resources=deployments,verbs=get;list;watch;create;update;patch;delete // +kubebuilder:rbac:groups=apps,resources=replicasets,verbs=get;list;watch;create;update;patch;delete // +kubebuilder:rbac:groups=core,resources=pods,verbs=get;list;watch +// +kubebuilder:rbac:groups=core,resources=secrets,verbs=get;list;watch +// +kubebuilder:rbac:groups=core,resources=configmaps,verbs=get;list;watch // +kubebuilder:rbac:groups=core,resources=serviceaccounts,verbs=get;list;watch;create;update;patch;delete // +kubebuilder:rbac:groups=rbac.authorization.k8s.io,resources=roles,verbs=get;list;watch;create;update;patch;delete // +kubebuilder:rbac:groups=rbac.authorization.k8s.io,resources=rolebindings,verbs=get;list;watch;create;update;patch;delete @@ -212,11 +214,19 @@ func (r *BackupConfigurationReconciler) Reconcile(ctx context.Context, req recon return err } log.V(1).Info("got deployment", "Deployment", deployment) - for _, container := range deployment.Spec.Template.Spec.Containers { + for i, container := range deployment.Spec.Template.Spec.Containers { if container.Name == formolv1alpha1.SIDECARCONTAINER_NAME { log.V(0).Info("There is already a backup sidecar container. Skipping", "container", container) return nil } + if target.ContainerName != "" && target.ContainerName == container.Name { + // Put a tag so we can find what container we are supposed to backup + // and what process we are supposed to chroot to run the init steps + deployment.Spec.Template.Spec.Containers[i].Env = append(container.Env, corev1.EnvVar{ + Name: formolv1alpha1.TARGETCONTAINER_TAG, + Value: "True", + }) + } } sidecar := corev1.Container{ Name: formolv1alpha1.SIDECARCONTAINER_NAME, diff --git a/controllers/backupconfiguration_controller_test.go b/controllers/backupconfiguration_controller_test.go index 319d6d6..1bd6ac9 100644 --- a/controllers/backupconfiguration_controller_test.go +++ b/controllers/backupconfiguration_controller_test.go @@ -62,12 +62,6 @@ var _ = Describe("Testing BackupConf controller", func() { Steps: []formolv1alpha1.Step{ formolv1alpha1.Step{ Name: TestBackupFuncName, - Env: []corev1.EnvVar{ - corev1.EnvVar{ - Name: "foo", - Value: "bar", - }, - }, }, }, }, diff --git a/controllers/backupsession_controller.go b/controllers/backupsession_controller.go index 4700c9d..029a15c 100644 --- a/controllers/backupsession_controller.go +++ b/controllers/backupsession_controller.go @@ -124,8 +124,8 @@ func (r *BackupSessionReconciler) Reconcile(ctx context.Context, req reconcile.R TTLSecondsAfterFinished: &jobTtl, Template: corev1.PodTemplateSpec{ Spec: corev1.PodSpec{ - InitContainers: []corev1.Container{}, - Containers: deleteSnapshots, + InitContainers: deleteSnapshots[1:], + Containers: []corev1.Container{deleteSnapshots[0]}, RestartPolicy: corev1.RestartPolicyOnFailure, }, }, @@ -212,7 +212,7 @@ func (r *BackupSessionReconciler) Reconcile(ctx context.Context, req reconcile.R return err } function.Spec.Name = function.Name - function.Spec.Env = append(step.Env, backupSessionEnv...) + function.Spec.Env = append(function.Spec.Env, backupSessionEnv...) function.Spec.VolumeMounts = append(function.Spec.VolumeMounts, output) job.Spec.Template.Spec.InitContainers = append(job.Spec.Template.Spec.InitContainers, function.Spec) } diff --git a/controllers/restoresession_controller.go b/controllers/restoresession_controller.go index 0f119c7..eb3f917 100644 --- a/controllers/restoresession_controller.go +++ b/controllers/restoresession_controller.go @@ -200,7 +200,7 @@ func (r *RestoreSessionReconciler) Reconcile(ctx context.Context, req reconcile. return err } function.Spec.Name = function.Name - function.Spec.Env = append(step.Env, restoreSessionEnv...) + function.Spec.Env = append(function.Spec.Env, restoreSessionEnv...) function.Spec.VolumeMounts = append(function.Spec.VolumeMounts, output) job.Spec.Template.Spec.InitContainers = append(job.Spec.Template.Spec.InitContainers, function.Spec) } diff --git a/controllers/suite_test.go b/controllers/suite_test.go index 0fdc255..0eb694a 100644 --- a/controllers/suite_test.go +++ b/controllers/suite_test.go @@ -159,6 +159,12 @@ var ( Name: "backup-func", Image: "myimage", Args: []string{"a", "set", "of", "args"}, + Env: []corev1.EnvVar{ + corev1.EnvVar{ + Name: "foo", + Value: "bar", + }, + }, }, } testBackupConf = &formolv1alpha1.BackupConfiguration{ @@ -201,12 +207,6 @@ var ( }, formolv1alpha1.Step{ Name: TestBackupFuncName, - Env: []corev1.EnvVar{ - corev1.EnvVar{ - Name: "foo", - Value: "bar", - }, - }, }, }, }, diff --git a/pkg/rbac/backupconfiguration.go b/pkg/rbac/backupconfiguration.go index a03f66c..a3d729d 100644 --- a/pkg/rbac/backupconfiguration.go +++ b/pkg/rbac/backupconfiguration.go @@ -217,7 +217,7 @@ func CreateFormolRBAC(cl client.Client, saName string, namespace string) error { rbacv1.PolicyRule{ Verbs: []string{"get", "list", "watch"}, APIGroups: []string{""}, - Resources: []string{"pods"}, + Resources: []string{"pods", "secrets", "configmaps"}, }, rbacv1.PolicyRule{ Verbs: []string{"get", "list", "watch"}, @@ -281,7 +281,7 @@ func CreateBackupSessionListenerRBAC(cl client.Client, saName string, namespace rbacv1.PolicyRule{ Verbs: []string{"get", "list", "watch"}, APIGroups: []string{""}, - Resources: []string{"pods"}, + Resources: []string{"pods", "secrets", "configmaps"}, }, rbacv1.PolicyRule{ Verbs: []string{"get", "list", "watch"}, diff --git a/test/00-setup.yaml b/test/00-setup.yaml index 5aff4be..f6d11b1 100644 --- a/test/00-setup.yaml +++ b/test/00-setup.yaml @@ -17,6 +17,14 @@ data: node.session.auth.password: VHJtK1lZaXZvMUNZSGszcGFGVWMrcTdCMmdJPQo= --- apiVersion: v1 +kind: Secret +metadata: + namespace: demo + name: with-envfrom-secret +data: + title: dmVyeXNlY3JldA== +--- +apiVersion: v1 kind: PersistentVolume metadata: name: demo-pv @@ -82,6 +90,39 @@ spec: name: restore-pg image: desmo999r/formolcli:latest args: ["postgres", "restore", "--hostname", $(PGHOST), "--database", $(PGDATABASE), "--username", $(PGUSER), "--password", $(PGPASSWD), "--file", "/output/backup-pg.sql"] + env: + - name: PGHOST + value: postgres + - name: PGDATABASE + value: demopostgres + - name: PGUSER + value: demopostgres + - name: PGPASSWD + value: password123! +--- +apiVersion: formol.desmojim.fr/v1alpha1 +kind: Function +metadata: + name: with-envfrom + namespace: demo +spec: + name: with-envfrom + command: ["touch", $(title)] + envFrom: + - secretRef: + name: with-envfrom-secret +--- +apiVersion: formol.desmojim.fr/v1alpha1 +kind: Function +metadata: + name: with-env + namespace: demo +spec: + name: with-env + command: ["touch", $(TESTFILE)] + env: + - name: TESTFILE + value: /data/testfile --- apiVersion: formol.desmojim.fr/v1alpha1 kind: Function @@ -92,6 +133,15 @@ spec: name: backup-pg image: desmo999r/formolcli:latest args: ["postgres", "backup", "--hostname", $(PGHOST), "--database", $(PGDATABASE), "--username", $(PGUSER), "--password", $(PGPASSWD), "--file", "/output/backup-pg.sql"] + env: + - name: PGHOST + value: postgres + - name: PGDATABASE + value: demopostgres + - name: PGUSER + value: demopostgres + - name: PGPASSWD + value: password123! --- apiVersion: formol.desmojim.fr/v1alpha1 kind: Function diff --git a/test/02-backupconf.yaml b/test/02-backupconf.yaml index e1a04fd..f037c82 100644 --- a/test/02-backupconf.yaml +++ b/test/02-backupconf.yaml @@ -6,6 +6,7 @@ metadata: namespace: demo spec: suspend: true + image: desmo999r/formolcli:latest repository: repo-minio schedule: "15 * * * *" targets: @@ -14,6 +15,8 @@ spec: name: nginx-deployment steps: - name: maintenance-on + - name: with-env + - name: with-envfrom - name: maintenance-off finalize: true volumeMounts: @@ -21,19 +24,10 @@ spec: mountPath: /data paths: - /data - - kind: Job - name: backup-pg - steps: - - name: backup-pg - env: - - name: PGHOST - value: postgres - - name: PGDATABASE - value: demopostgres - - name: PGUSER - value: demopostgres - - name: PGPASSWD - value: password123! +# - kind: Job +# name: backup-pg +# steps: +# - name: backup-pg keep: last: 5 daily: 2 diff --git a/test/README b/test/README index abc00e5..a6874fb 100644 --- a/test/README +++ b/test/README @@ -1 +1,2 @@ NAMESPACE=demo; for i in $(kubectl -n $NAMESPACE get bs | awk 'NR>1 { print $1 }'); do kubectl -n $NAMESPACE get bs -o json $i | sed '/finalizers/,+2d' | curl -vvv -X PUT -H 'Content-type: application/json' -d @- http://127.0.0.1:8001/apis/formol.desmojim.fr/v1alpha1/namespaces/$NAMESPACE/backupsessions/$i; kubectl -n $NAMESPACE delete bs $i; done +NAMESPACE=demo; for i in $(kubectl -n $NAMESPACE get bs | awk 'NR>1 { if ($3 == "New") { print $1 }}'); do kubectl -n $NAMESPACE get bs -o json $i | sed '/finalizers/,+2d' | curl -vvv -X PUT -H 'Content-type: application/json' -d @- http://127.0.0.1:8001/apis/formol.desmojim.fr/v1alpha1/namespaces/$NAMESPACE/backupsessions/$i; kubectl -n $NAMESPACE delete bs $i; done