From 873dd548ffcd1956b709e75ebe4e1eb555dcce2f Mon Sep 17 00:00:00 2001 From: Raphael Torquato <89878688+raphaeltorquat0@users.noreply.github.com> Date: Wed, 10 Jun 2026 13:18:54 -0300 Subject: [PATCH] Add cluster_labels and annotations to logical backup CronJob and Jobs (#3085) * Add cluster_labels and annotations to logical backup CronJob and Jobs When using the logical backup feature, the CronJob and its created Jobs were missing the cluster_labels and annotations that are applied to other cluster resources. This made it difficult to filter or identify backup jobs using the same labels as other cluster components. Changes: - Added ObjectMeta with labels and annotations to JobTemplateSpec - Updated CronJob ObjectMeta to use the merged labels (including 'application: spilo-logical-backup') - Updated tests to expect the new labels --- e2e/tests/k8s_api.py | 2 +- pkg/cluster/cluster.go | 10 ++++++++++ pkg/cluster/k8sres.go | 8 ++++++-- pkg/cluster/k8sres_test.go | 12 ++++++------ pkg/cluster/sync.go | 10 ++++++++++ pkg/cluster/util.go | 8 ++++++++ 6 files changed, 41 insertions(+), 9 deletions(-) diff --git a/e2e/tests/k8s_api.py b/e2e/tests/k8s_api.py index 1f42ad4bc..0ef3d6315 100644 --- a/e2e/tests/k8s_api.py +++ b/e2e/tests/k8s_api.py @@ -240,7 +240,7 @@ class K8s: time.sleep(self.RETRY_TIMEOUT_SEC) def get_logical_backup_job(self, namespace='default'): - return self.api.batch_v1.list_namespaced_cron_job(namespace, label_selector="application=spilo") + return self.api.batch_v1.list_namespaced_cron_job(namespace, label_selector="application=spilo-logical-backup") def wait_for_logical_backup_job(self, expected_num_of_jobs): while (len(self.get_logical_backup_job().items) != expected_num_of_jobs): diff --git a/pkg/cluster/cluster.go b/pkg/cluster/cluster.go index b7a7b8e56..95f16d922 100644 --- a/pkg/cluster/cluster.go +++ b/pkg/cluster/cluster.go @@ -893,6 +893,16 @@ func (c *Cluster) compareLogicalBackupJob(cur, new *batchv1.CronJob) *compareLog reasons = append(reasons, fmt.Sprintf("new job's env PG_VERSION %q does not match the current one %q", newPgVersion, curPgVersion)) } + if !reflect.DeepEqual(cur.Labels, new.Labels) { + match = false + reasons = append(reasons, "new job's labels do not match the current ones") + } + + if !reflect.DeepEqual(cur.Spec.JobTemplate.Labels, new.Spec.JobTemplate.Labels) { + match = false + reasons = append(reasons, "new job's template labels do not match the current ones") + } + needsReplace := false contReasons := make([]string, 0) needsReplace, contReasons = c.compareContainers("cronjob container", cur.Spec.JobTemplate.Spec.Template.Spec.Containers, new.Spec.JobTemplate.Spec.Template.Spec.Containers, needsReplace, contReasons) diff --git a/pkg/cluster/k8sres.go b/pkg/cluster/k8sres.go index 866eeb752..8d3a40d9a 100644 --- a/pkg/cluster/k8sres.go +++ b/pkg/cluster/k8sres.go @@ -2414,6 +2414,10 @@ func (c *Cluster) generateLogicalBackupJob() (*batchv1.CronJob, error) { // configure a cron job jobTemplateSpec := batchv1.JobTemplateSpec{ + ObjectMeta: metav1.ObjectMeta{ + Labels: labels, + Annotations: c.annotationsSet(annotations), + }, Spec: jobSpec, } @@ -2426,8 +2430,8 @@ func (c *Cluster) generateLogicalBackupJob() (*batchv1.CronJob, error) { ObjectMeta: metav1.ObjectMeta{ Name: c.getLogicalBackupJobName(), Namespace: c.Namespace, - Labels: c.labelsSet(true), - Annotations: c.annotationsSet(nil), + Labels: labels, + Annotations: c.annotationsSet(annotations), OwnerReferences: c.ownerReferences(), }, Spec: batchv1.CronJobSpec{ diff --git a/pkg/cluster/k8sres_test.go b/pkg/cluster/k8sres_test.go index 2010de067..ef408da4d 100644 --- a/pkg/cluster/k8sres_test.go +++ b/pkg/cluster/k8sres_test.go @@ -3875,7 +3875,7 @@ func TestGenerateLogicalBackupJob(t *testing.T) { ResourceRequests: acidv1.ResourceDescription{CPU: k8sutil.StringToPointer("100m"), Memory: k8sutil.StringToPointer("100Mi")}, ResourceLimits: acidv1.ResourceDescription{CPU: k8sutil.StringToPointer("1"), Memory: k8sutil.StringToPointer("500Mi")}, }, - expectedLabel: map[string]string{configResources.ClusterNameLabel: clusterName, "team": teamId}, + expectedLabel: map[string]string{"application": "spilo-logical-backup", configResources.ClusterNameLabel: clusterName, "team": teamId}, expectedAnnotation: nil, }, { @@ -3900,7 +3900,7 @@ func TestGenerateLogicalBackupJob(t *testing.T) { ResourceRequests: acidv1.ResourceDescription{CPU: k8sutil.StringToPointer("10m"), Memory: k8sutil.StringToPointer("50Mi")}, ResourceLimits: acidv1.ResourceDescription{CPU: k8sutil.StringToPointer("300m"), Memory: k8sutil.StringToPointer("300Mi")}, }, - expectedLabel: map[string]string{configResources.ClusterNameLabel: clusterName, "team": teamId}, + expectedLabel: map[string]string{"application": "spilo-logical-backup", configResources.ClusterNameLabel: clusterName, "team": teamId}, expectedAnnotation: nil, }, { @@ -3923,7 +3923,7 @@ func TestGenerateLogicalBackupJob(t *testing.T) { ResourceRequests: acidv1.ResourceDescription{CPU: k8sutil.StringToPointer("50m"), Memory: k8sutil.StringToPointer("100Mi")}, ResourceLimits: acidv1.ResourceDescription{CPU: k8sutil.StringToPointer("250m"), Memory: k8sutil.StringToPointer("500Mi")}, }, - expectedLabel: map[string]string{configResources.ClusterNameLabel: clusterName, "team": teamId}, + expectedLabel: map[string]string{"application": "spilo-logical-backup", configResources.ClusterNameLabel: clusterName, "team": teamId}, expectedAnnotation: nil, }, { @@ -3946,7 +3946,7 @@ func TestGenerateLogicalBackupJob(t *testing.T) { ResourceRequests: acidv1.ResourceDescription{CPU: k8sutil.StringToPointer("100m"), Memory: k8sutil.StringToPointer("200Mi")}, ResourceLimits: acidv1.ResourceDescription{CPU: k8sutil.StringToPointer("1"), Memory: k8sutil.StringToPointer("200Mi")}, }, - expectedLabel: map[string]string{configResources.ClusterNameLabel: clusterName, "team": teamId}, + expectedLabel: map[string]string{"application": "spilo-logical-backup", configResources.ClusterNameLabel: clusterName, "team": teamId}, expectedAnnotation: nil, }, { @@ -3968,7 +3968,7 @@ func TestGenerateLogicalBackupJob(t *testing.T) { ResourceRequests: acidv1.ResourceDescription{CPU: k8sutil.StringToPointer("100m"), Memory: k8sutil.StringToPointer("100Mi")}, ResourceLimits: acidv1.ResourceDescription{CPU: k8sutil.StringToPointer("1"), Memory: k8sutil.StringToPointer("500Mi")}, }, - expectedLabel: map[string]string{"labelKey": "labelValue", "cluster-name": clusterName, "team": teamId}, + expectedLabel: map[string]string{"application": "spilo-logical-backup", "labelKey": "labelValue", "cluster-name": clusterName, "team": teamId}, expectedAnnotation: nil, }, { @@ -3990,7 +3990,7 @@ func TestGenerateLogicalBackupJob(t *testing.T) { ResourceRequests: acidv1.ResourceDescription{CPU: k8sutil.StringToPointer("100m"), Memory: k8sutil.StringToPointer("100Mi")}, ResourceLimits: acidv1.ResourceDescription{CPU: k8sutil.StringToPointer("1"), Memory: k8sutil.StringToPointer("500Mi")}, }, - expectedLabel: map[string]string{configResources.ClusterNameLabel: clusterName, "team": teamId}, + expectedLabel: map[string]string{"application": "spilo-logical-backup", configResources.ClusterNameLabel: clusterName, "team": teamId}, expectedAnnotation: map[string]string{"annotationKey": "annotationValue"}, }, } diff --git a/pkg/cluster/sync.go b/pkg/cluster/sync.go index 7c478477a..e15b5fedc 100644 --- a/pkg/cluster/sync.go +++ b/pkg/cluster/sync.go @@ -1769,6 +1769,16 @@ func (c *Cluster) syncLogicalBackupJob() error { } c.logger.Info("the logical backup job is synced") } + if !reflect.DeepEqual(job.Labels, desiredJob.Labels) { + patchData, err := metaLabelsPatch(desiredJob.Labels) + if err != nil { + return fmt.Errorf("could not form patch for the logical backup job %q labels: %v", jobName, err) + } + _, err = c.KubeClient.CronJobs(c.Namespace).Patch(context.TODO(), jobName, types.MergePatchType, []byte(patchData), metav1.PatchOptions{}) + if err != nil { + return fmt.Errorf("could not patch labels of the logical backup job %q: %v", jobName, err) + } + } if changed, _ := c.compareAnnotations(job.Annotations, desiredJob.Annotations, nil); changed { patchData, err := metaAnnotationsPatch(desiredJob.Annotations) if err != nil { diff --git a/pkg/cluster/util.go b/pkg/cluster/util.go index 9c830129d..cbcccd16e 100644 --- a/pkg/cluster/util.go +++ b/pkg/cluster/util.go @@ -167,6 +167,14 @@ func metaAnnotationsPatch(annotations map[string]string) ([]byte, error) { }{&meta}) } +func metaLabelsPatch(labels map[string]string) ([]byte, error) { + var meta metav1.ObjectMeta + meta.Labels = labels + return json.Marshal(struct { + ObjMeta interface{} `json:"metadata"` + }{&meta}) +} + func (c *Cluster) logPDBChanges(old, new *policyv1.PodDisruptionBudget, isUpdate bool, reason string) { if isUpdate { c.logger.Infof("pod disruption budget %q has been changed", util.NameFromMeta(old.ObjectMeta))