diff --git a/api/v1alpha1/dragonfly_types.go b/api/v1alpha1/dragonfly_types.go index 0f1c575..d3147a4 100644 --- a/api/v1alpha1/dragonfly_types.go +++ b/api/v1alpha1/dragonfly_types.go @@ -169,6 +169,16 @@ type DragonflySpec struct { // +optional // +kubebuilder:validation:Optional InitContainers []corev1.Container `json:"initContainers,omitempty"` + + // (Optional) Dragonfly direct child resources additional annotations and labels + // +optional + // +kubebuilder:validation:Optional + OwnedObjectsMetadata *OwnedObjectsMetadata `json:"ownedObjectsMetadata,omitempty"` +} + +type OwnedObjectsMetadata struct { + Annotations map[string]string `json:"annotations,omitempty"` + Labels map[string]string `json:"labels,omitempty"` } type ServiceSpec struct { diff --git a/api/v1alpha1/zz_generated.deepcopy.go b/api/v1alpha1/zz_generated.deepcopy.go index 629251f..b608815 100644 --- a/api/v1alpha1/zz_generated.deepcopy.go +++ b/api/v1alpha1/zz_generated.deepcopy.go @@ -235,6 +235,11 @@ func (in *DragonflySpec) DeepCopyInto(out *DragonflySpec) { (*in)[i].DeepCopyInto(&(*out)[i]) } } + if in.OwnedObjectsMetadata != nil { + in, out := &in.OwnedObjectsMetadata, &out.OwnedObjectsMetadata + *out = new(OwnedObjectsMetadata) + (*in).DeepCopyInto(*out) + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DragonflySpec. @@ -262,6 +267,35 @@ func (in *DragonflyStatus) DeepCopy() *DragonflyStatus { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *OwnedObjectsMetadata) DeepCopyInto(out *OwnedObjectsMetadata) { + *out = *in + if in.Annotations != nil { + in, out := &in.Annotations, &out.Annotations + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + if in.Labels != nil { + in, out := &in.Labels, &out.Labels + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OwnedObjectsMetadata. +func (in *OwnedObjectsMetadata) DeepCopy() *OwnedObjectsMetadata { + if in == nil { + return nil + } + out := new(OwnedObjectsMetadata) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ServiceSpec) DeepCopyInto(out *ServiceSpec) { *out = *in diff --git a/config/crd/bases/dragonflydb.io_dragonflies.yaml b/config/crd/bases/dragonflydb.io_dragonflies.yaml index 84d72d9..62b1dbb 100644 --- a/config/crd/bases/dragonflydb.io_dragonflies.yaml +++ b/config/crd/bases/dragonflydb.io_dragonflies.yaml @@ -6043,6 +6043,19 @@ spec: type: string description: (Optional) Dragonfly pod node selector type: object + ownedObjectsMetadata: + description: (Optional) Dragonfly direct child resources additional + annotations and labels + properties: + annotations: + additionalProperties: + type: string + type: object + labels: + additionalProperties: + type: string + type: object + type: object podSecurityContext: description: (Optional) Dragonfly pod security context properties: diff --git a/e2e/dragonfly_controller_test.go b/e2e/dragonfly_controller_test.go index e1b142f..fc8623e 100644 --- a/e2e/dragonfly_controller_test.go +++ b/e2e/dragonfly_controller_test.go @@ -60,12 +60,22 @@ var _ = Describe("Dragonfly Lifecycle tests", Ordered, FlakeAttempts(3), func() "--vmodule=replica=1,server_family=1", } + customLabelName := "a.custom/label" + customLabelValue := "my-value" + + labels := map[string]string{ + customLabelName: customLabelValue, + } + df := resourcesv1.Dragonfly{ ObjectMeta: metav1.ObjectMeta{ Name: name, Namespace: namespace, }, Spec: resourcesv1.DragonflySpec{ + OwnedObjectsMetadata: &resourcesv1.OwnedObjectsMetadata{ + Labels: labels, + }, Replicas: 3, Resources: &resourcesReq, Args: args, @@ -146,6 +156,10 @@ var _ = Describe("Dragonfly Lifecycle tests", Ordered, FlakeAttempts(3), func() }, &svc) Expect(err).To(BeNil()) + // check labels of statefulset + Expect(ss.Labels[customLabelName]).To(Equal(df.Spec.OwnedObjectsMetadata.Labels[customLabelName])) + Expect(svc.Labels[customLabelName]).To(Equal(df.Spec.OwnedObjectsMetadata.Labels[customLabelName])) + // check resource requirements of statefulset Expect(ss.Spec.Template.Spec.Containers[0].Resources).To(Equal(*df.Spec.Resources)) // check args of statefulset diff --git a/internal/resources/resources.go b/internal/resources/resources.go index 96273bb..70796e1 100644 --- a/internal/resources/resources.go +++ b/internal/resources/resources.go @@ -56,15 +56,8 @@ func GenerateDragonflyResources(df *resourcesv1.Dragonfly) ([]client.Object, err UID: df.UID, }, }, - Labels: map[string]string{ - KubernetesAppComponentLabelKey: KubernetesAppComponent, - KubernetesAppInstanceLabelKey: df.Name, - KubernetesAppNameLabelKey: KubernetesAppName, - KubernetesAppVersionLabelKey: Version, - KubernetesPartOfLabelKey: KubernetesPartOf, - KubernetesManagedByLabelKey: DragonflyOperatorName, - DragonflyNameLabelKey: df.Name, - }, + Labels: generateResourceLabels(df), + Annotations: generateResourceAnnotations(df), }, Spec: appsv1.StatefulSetSpec{ Replicas: &df.Spec.Replicas, @@ -218,8 +211,9 @@ func GenerateDragonflyResources(df *resourcesv1.Dragonfly) ([]client.Object, err if df.Spec.Tiering.PersistentVolumeClaimSpec != nil { statefulset.Spec.VolumeClaimTemplates = append(statefulset.Spec.VolumeClaimTemplates, corev1.PersistentVolumeClaim{ ObjectMeta: metav1.ObjectMeta{ - Name: tieringVolumeName, - Labels: generateResourceLabels(df), + Name: tieringVolumeName, + Labels: generateResourceLabels(df), + Annotations: generateResourceAnnotations(df), }, Spec: *df.Spec.Tiering.PersistentVolumeClaimSpec, }) @@ -401,7 +395,8 @@ func GenerateDragonflyResources(df *resourcesv1.Dragonfly) ([]client.Object, err UID: df.UID, }, }, - Labels: generateResourceLabels(df), + Labels: generateResourceLabels(df), + Annotations: generateResourceAnnotations(df), }, Spec: corev1.ServiceSpec{ Selector: map[string]string{ @@ -446,7 +441,8 @@ func GenerateDragonflyResources(df *resourcesv1.Dragonfly) ([]client.Object, err UID: df.UID, }, }, - Labels: generateResourceLabels(df), + Labels: generateResourceLabels(df), + Annotations: generateResourceAnnotations(df), }, Spec: policyv1.PodDisruptionBudgetSpec{ MaxUnavailable: &intstr.IntOrString{ @@ -490,7 +486,7 @@ func mergeNamedSlices[T any](base, override []T, getName func(T) string) []T { } func generateResourceLabels(df *resourcesv1.Dragonfly) map[string]string { - return map[string]string{ + labels := map[string]string{ KubernetesAppComponentLabelKey: KubernetesAppComponent, KubernetesAppInstanceLabelKey: df.Name, KubernetesAppNameLabelKey: KubernetesAppName, @@ -499,4 +495,25 @@ func generateResourceLabels(df *resourcesv1.Dragonfly) map[string]string { KubernetesManagedByLabelKey: DragonflyOperatorName, DragonflyNameLabelKey: df.Name, } + + if df.Spec.OwnedObjectsMetadata != nil { + for key, value := range df.Spec.OwnedObjectsMetadata.Labels { + if _, ok := labels[key]; !ok { + labels[key] = value + } + } + } + + return labels +} + +func generateResourceAnnotations(df *resourcesv1.Dragonfly) map[string]string { + annotations := map[string]string{} + if df.Spec.OwnedObjectsMetadata != nil { + for key, value := range df.Spec.OwnedObjectsMetadata.Annotations { + annotations[key] = value + } + } + + return annotations }