From fe942bb40c8c14a59ecd76b667359ff1222245f1 Mon Sep 17 00:00:00 2001 From: Guillaume Doussin Date: Thu, 21 Sep 2023 16:17:15 +0200 Subject: [PATCH] feat(openshift): Support extra labels/annotations for deployments and pods Signed-off-by: Guillaume Doussin --- api/v1beta1/cryostat_types.go | 15 ++ api/v1beta1/zz_generated.deepcopy.go | 34 ++++ ...operator.cryostat.io_clustercryostats.yaml | 31 +++- .../bases/operator.cryostat.io_cryostats.yaml | 31 +++- internal/controllers/common/common_utils.go | 20 +++ .../resource_definitions.go | 147 ++++++++++++------ internal/controllers/ingresses.go | 3 +- internal/controllers/pvc.go | 3 +- internal/controllers/reconciler.go | 24 +-- internal/controllers/reconciler_test.go | 90 +++++++++++ internal/controllers/routes.go | 3 +- internal/controllers/services.go | 3 +- internal/test/resources.go | 22 +++ 13 files changed, 345 insertions(+), 81 deletions(-) diff --git a/api/v1beta1/cryostat_types.go b/api/v1beta1/cryostat_types.go index 1bdbb34a8..2a9139fe7 100644 --- a/api/v1beta1/cryostat_types.go +++ b/api/v1beta1/cryostat_types.go @@ -88,6 +88,21 @@ type CryostatSpec struct { // +optional // +operator-sdk:csv:customresourcedefinitions:type=spec,displayName="Credentials Database Options" JmxCredentialsDatabaseOptions *JmxCredentialsDatabaseOptions `json:"jmxCredentialsDatabaseOptions,omitempty"` + // Options to configure the Cryostat resources metadata + // +optional + // +operator-sdk:csv:customresourcedefinitions:type=spec,displayName="Resources metadata" + Metadata *ResourceMetadata `json:"metadata,omitempty"` +} + +type ResourceMetadata struct { + // Annotations to add to the resources during its creation. + // +optional + Annotations map[string]string `json:"annotations,omitempty"` + // Labels to add to the resources during its creation. + // The labels with keys "app" and "component" are reserved + // for use by the operator. + // +optional + Labels map[string]string `json:"labels,omitempty"` } type ResourceConfigList struct { diff --git a/api/v1beta1/zz_generated.deepcopy.go b/api/v1beta1/zz_generated.deepcopy.go index 8876502cf..696b61e54 100644 --- a/api/v1beta1/zz_generated.deepcopy.go +++ b/api/v1beta1/zz_generated.deepcopy.go @@ -352,6 +352,11 @@ func (in *CryostatSpec) DeepCopyInto(out *CryostatSpec) { *out = new(JmxCredentialsDatabaseOptions) (*in).DeepCopyInto(*out) } + if in.Metadata != nil { + in, out := &in.Metadata, &out.Metadata + *out = new(ResourceMetadata) + (*in).DeepCopyInto(*out) + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CryostatSpec. @@ -645,6 +650,35 @@ func (in *ResourceConfigList) DeepCopy() *ResourceConfigList { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ResourceMetadata) DeepCopyInto(out *ResourceMetadata) { + *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 ResourceMetadata. +func (in *ResourceMetadata) DeepCopy() *ResourceMetadata { + if in == nil { + return nil + } + out := new(ResourceMetadata) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *SchedulingConfiguration) DeepCopyInto(out *SchedulingConfiguration) { *out = *in diff --git a/config/crd/bases/operator.cryostat.io_clustercryostats.yaml b/config/crd/bases/operator.cryostat.io_clustercryostats.yaml index a0b52d792..f321148b6 100644 --- a/config/crd/bases/operator.cryostat.io_clustercryostats.yaml +++ b/config/crd/bases/operator.cryostat.io_clustercryostats.yaml @@ -127,6 +127,22 @@ spec: format: int32 minimum: 1 type: integer + metadata: + description: Options to configure the Cryostat resources metadata + properties: + annotations: + additionalProperties: + type: string + description: Annotations to add to the resources during its creation. + type: object + labels: + additionalProperties: + type: string + description: Labels to add to the resources during its creation. + The labels with keys "app" and "component" are reserved for + use by the operator. + type: object + type: object minimal: description: Deploy a pared-down Cryostat instance with no Grafana Dashboard or JFR Data Source. @@ -1097,7 +1113,8 @@ spec: description: "Claims lists the names of resources, defined in spec.resourceClaims, that are used by this container. \n This is an alpha field and requires enabling the DynamicResourceAllocation - feature gate. \n This field is immutable." + feature gate. \n This field is immutable. It can only be + set for containers." items: description: ResourceClaim references one entry in PodSpec.ResourceClaims. properties: @@ -2472,7 +2489,8 @@ spec: description: "Claims lists the names of resources, defined in spec.resourceClaims, that are used by this container. \n This is an alpha field and requires enabling the DynamicResourceAllocation - feature gate. \n This field is immutable." + feature gate. \n This field is immutable. It can only be + set for containers." items: description: ResourceClaim references one entry in PodSpec.ResourceClaims. properties: @@ -2519,7 +2537,8 @@ spec: description: "Claims lists the names of resources, defined in spec.resourceClaims, that are used by this container. \n This is an alpha field and requires enabling the DynamicResourceAllocation - feature gate. \n This field is immutable." + feature gate. \n This field is immutable. It can only be + set for containers." items: description: ResourceClaim references one entry in PodSpec.ResourceClaims. properties: @@ -2566,7 +2585,8 @@ spec: description: "Claims lists the names of resources, defined in spec.resourceClaims, that are used by this container. \n This is an alpha field and requires enabling the DynamicResourceAllocation - feature gate. \n This field is immutable." + feature gate. \n This field is immutable. It can only be + set for containers." items: description: ResourceClaim references one entry in PodSpec.ResourceClaims. properties: @@ -4452,7 +4472,8 @@ spec: defined in spec.resourceClaims, that are used by this container. \n This is an alpha field and requires enabling the DynamicResourceAllocation feature gate. - \n This field is immutable." + \n This field is immutable. It can only be set for + containers." items: description: ResourceClaim references one entry in PodSpec.ResourceClaims. diff --git a/config/crd/bases/operator.cryostat.io_cryostats.yaml b/config/crd/bases/operator.cryostat.io_cryostats.yaml index 2fca14f94..d022240ed 100644 --- a/config/crd/bases/operator.cryostat.io_cryostats.yaml +++ b/config/crd/bases/operator.cryostat.io_cryostats.yaml @@ -123,6 +123,22 @@ spec: format: int32 minimum: 1 type: integer + metadata: + description: Options to configure the Cryostat resources metadata + properties: + annotations: + additionalProperties: + type: string + description: Annotations to add to the resources during its creation. + type: object + labels: + additionalProperties: + type: string + description: Labels to add to the resources during its creation. + The labels with keys "app" and "component" are reserved for + use by the operator. + type: object + type: object minimal: description: Deploy a pared-down Cryostat instance with no Grafana Dashboard or JFR Data Source. @@ -1093,7 +1109,8 @@ spec: description: "Claims lists the names of resources, defined in spec.resourceClaims, that are used by this container. \n This is an alpha field and requires enabling the DynamicResourceAllocation - feature gate. \n This field is immutable." + feature gate. \n This field is immutable. It can only be + set for containers." items: description: ResourceClaim references one entry in PodSpec.ResourceClaims. properties: @@ -2468,7 +2485,8 @@ spec: description: "Claims lists the names of resources, defined in spec.resourceClaims, that are used by this container. \n This is an alpha field and requires enabling the DynamicResourceAllocation - feature gate. \n This field is immutable." + feature gate. \n This field is immutable. It can only be + set for containers." items: description: ResourceClaim references one entry in PodSpec.ResourceClaims. properties: @@ -2515,7 +2533,8 @@ spec: description: "Claims lists the names of resources, defined in spec.resourceClaims, that are used by this container. \n This is an alpha field and requires enabling the DynamicResourceAllocation - feature gate. \n This field is immutable." + feature gate. \n This field is immutable. It can only be + set for containers." items: description: ResourceClaim references one entry in PodSpec.ResourceClaims. properties: @@ -2562,7 +2581,8 @@ spec: description: "Claims lists the names of resources, defined in spec.resourceClaims, that are used by this container. \n This is an alpha field and requires enabling the DynamicResourceAllocation - feature gate. \n This field is immutable." + feature gate. \n This field is immutable. It can only be + set for containers." items: description: ResourceClaim references one entry in PodSpec.ResourceClaims. properties: @@ -4448,7 +4468,8 @@ spec: defined in spec.resourceClaims, that are used by this container. \n This is an alpha field and requires enabling the DynamicResourceAllocation feature gate. - \n This field is immutable." + \n This field is immutable. It can only be set for + containers." items: description: ResourceClaim references one entry in PodSpec.ResourceClaims. diff --git a/internal/controllers/common/common_utils.go b/internal/controllers/common/common_utils.go index 3c0cf3ea5..b6c5310b7 100644 --- a/internal/controllers/common/common_utils.go +++ b/internal/controllers/common/common_utils.go @@ -23,6 +23,7 @@ import ( "strings" "time" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" logf "sigs.k8s.io/controller-runtime/pkg/log" ) @@ -68,3 +69,22 @@ func ClusterUniqueName(kind string, name string, namespace string) string { suffix := fmt.Sprintf("%x", sha256.Sum256([]byte(nn.String()))) return strings.ToLower(kind) + "-" + suffix } + +func MergeLabelsAndAnnotations(dest *metav1.ObjectMeta, srcLabels, srcAnnotations map[string]string) { + // Check and create labels/annotations map if absent + if dest.Labels == nil { + dest.Labels = map[string]string{} + } + if dest.Annotations == nil { + dest.Annotations = map[string]string{} + } + + // Merge labels and annotations, preferring those in the source + for k, v := range srcLabels { + dest.Labels[k] = v + } + + for k, v := range srcAnnotations { + dest.Annotations[k] = v + } +} diff --git a/internal/controllers/common/resource_definitions/resource_definitions.go b/internal/controllers/common/resource_definitions/resource_definitions.go index bf540b5bd..0d7d2f3f1 100644 --- a/internal/controllers/common/resource_definitions/resource_definitions.go +++ b/internal/controllers/common/resource_definitions/resource_definitions.go @@ -22,6 +22,7 @@ import ( "strings" operatorv1beta1 "github.com/cryostatio/cryostat-operator/api/v1beta1" + common "github.com/cryostatio/cryostat-operator/internal/controllers/common" "github.com/cryostatio/cryostat-operator/internal/controllers/constants" "github.com/cryostatio/cryostat-operator/internal/controllers/model" appsv1 "k8s.io/api/apps/v1" @@ -74,20 +75,55 @@ func NewDeploymentForCR(cr *model.CryostatInstance, specs *ServiceSpecs, imageTa tls *TLSConfig, fsGroup int64, openshift bool) *appsv1.Deployment { // Force one replica to avoid lock file and PVC contention replicas := int32(1) + + defaultDeploymentLabels := map[string]string{ + "app": cr.Name, + "kind": "cryostat", + "component": "cryostat", + "app.kubernetes.io/name": "cryostat", + } + defaultDeploymentAnnotations := map[string]string{ + "app.openshift.io/connects-to": "cryostat-operator-controller-manager", + } + defaultPodLabels := map[string]string{ + "app": cr.Name, + "kind": "cryostat", + "component": "cryostat", + } + userDefinedDeploymentLabels := make(map[string]string) + userDefinedDeploymentAnnotations := make(map[string]string) + userDefinedPodTemplateLabels := make(map[string]string) + userDefinedPodTemplateAnnotations := make(map[string]string) + if cr.Spec.Metadata != nil { + for k, v := range cr.Spec.Metadata.Labels { + userDefinedDeploymentLabels[k] = v + userDefinedPodTemplateLabels[k] = v + } + for k, v := range cr.Spec.Metadata.Annotations { + userDefinedDeploymentAnnotations[k] = v + userDefinedPodTemplateAnnotations[k] = v + } + } + + // First set the user defined labels and annotation in the meta, so that the default ones can override them + deploymentMeta := metav1.ObjectMeta{ + Name: cr.Name, + Namespace: cr.InstallNamespace, + Labels: userDefinedDeploymentLabels, + Annotations: userDefinedDeploymentAnnotations, + } + common.MergeLabelsAndAnnotations(&deploymentMeta, defaultDeploymentLabels, defaultDeploymentAnnotations) + + podTemplateMeta := metav1.ObjectMeta{ + Name: cr.Name, + Namespace: cr.InstallNamespace, + Labels: userDefinedPodTemplateLabels, + Annotations: userDefinedPodTemplateAnnotations, + } + common.MergeLabelsAndAnnotations(&podTemplateMeta, defaultPodLabels, nil) + return &appsv1.Deployment{ - ObjectMeta: metav1.ObjectMeta{ - Name: cr.Name, - Namespace: cr.InstallNamespace, - Labels: map[string]string{ - "app": cr.Name, - "kind": "cryostat", - "component": "cryostat", - "app.kubernetes.io/name": "cryostat", - }, - Annotations: map[string]string{ - "app.openshift.io/connects-to": "cryostat-operator-controller-manager", - }, - }, + ObjectMeta: deploymentMeta, Spec: appsv1.DeploymentSpec{ // Selector is immutable, avoid modifying if possible Selector: &metav1.LabelSelector{ @@ -98,16 +134,8 @@ func NewDeploymentForCR(cr *model.CryostatInstance, specs *ServiceSpecs, imageTa }, }, Template: corev1.PodTemplateSpec{ - ObjectMeta: metav1.ObjectMeta{ - Name: cr.Name, - Namespace: cr.InstallNamespace, - Labels: map[string]string{ - "app": cr.Name, - "kind": "cryostat", - "component": "cryostat", - }, - }, - Spec: *NewPodForCR(cr, specs, imageTags, tls, fsGroup, openshift), + ObjectMeta: podTemplateMeta, + Spec: *NewPodForCR(cr, specs, imageTags, tls, fsGroup, openshift), }, Replicas: &replicas, Strategy: appsv1.DeploymentStrategy{ @@ -123,20 +151,55 @@ func NewDeploymentForReports(cr *model.CryostatInstance, imageTags *ImageTags, t if cr.Spec.ReportOptions != nil { replicas = cr.Spec.ReportOptions.Replicas } + + defaultDeploymentLabels := map[string]string{ + "app": cr.Name, + "kind": "cryostat", + "component": "reports", + "app.kubernetes.io/name": "cryostat-reports", + } + defaultDeploymentAnnotations := map[string]string{ + "app.openshift.io/connects-to": cr.Name, + } + defaultPodLabels := map[string]string{ + "app": cr.Name, + "kind": "cryostat", + "component": "reports", + } + userDefinedDeploymentLabels := make(map[string]string) + userDefinedDeploymentAnnotations := make(map[string]string) + userDefinedPodTemplateLabels := make(map[string]string) + userDefinedPodTemplateAnnotations := make(map[string]string) + if cr.Spec.Metadata != nil { + for k, v := range cr.Spec.Metadata.Labels { + userDefinedDeploymentLabels[k] = v + userDefinedPodTemplateLabels[k] = v + } + for k, v := range cr.Spec.Metadata.Annotations { + userDefinedDeploymentAnnotations[k] = v + userDefinedPodTemplateAnnotations[k] = v + } + } + + // First set the user defined labels and annotation in the meta, so that the default ones can override them + deploymentMeta := metav1.ObjectMeta{ + Name: cr.Name + "-reports", + Namespace: cr.InstallNamespace, + Labels: userDefinedDeploymentLabels, + Annotations: userDefinedDeploymentAnnotations, + } + common.MergeLabelsAndAnnotations(&deploymentMeta, defaultDeploymentLabels, defaultDeploymentAnnotations) + + podTemplateMeta := metav1.ObjectMeta{ + Name: cr.Name + "-reports", + Namespace: cr.InstallNamespace, + Labels: userDefinedPodTemplateLabels, + Annotations: userDefinedPodTemplateAnnotations, + } + common.MergeLabelsAndAnnotations(&podTemplateMeta, defaultPodLabels, nil) + return &appsv1.Deployment{ - ObjectMeta: metav1.ObjectMeta{ - Name: cr.Name + "-reports", - Namespace: cr.InstallNamespace, - Labels: map[string]string{ - "app": cr.Name, - "kind": "cryostat", - "component": "reports", - "app.kubernetes.io/name": "cryostat-reports", - }, - Annotations: map[string]string{ - "app.openshift.io/connects-to": cr.Name, - }, - }, + ObjectMeta: deploymentMeta, Spec: appsv1.DeploymentSpec{ // Selector is immutable, avoid modifying if possible Selector: &metav1.LabelSelector{ @@ -147,16 +210,8 @@ func NewDeploymentForReports(cr *model.CryostatInstance, imageTags *ImageTags, t }, }, Template: corev1.PodTemplateSpec{ - ObjectMeta: metav1.ObjectMeta{ - Name: cr.Name + "-reports", - Namespace: cr.InstallNamespace, - Labels: map[string]string{ - "app": cr.Name, - "kind": "cryostat", - "component": "reports", - }, - }, - Spec: *NewPodForReports(cr, imageTags, tls, openshift), + ObjectMeta: podTemplateMeta, + Spec: *NewPodForReports(cr, imageTags, tls, openshift), }, Replicas: &replicas, }, diff --git a/internal/controllers/ingresses.go b/internal/controllers/ingresses.go index 84c7fe67f..cadb91562 100644 --- a/internal/controllers/ingresses.go +++ b/internal/controllers/ingresses.go @@ -20,6 +20,7 @@ import ( "net/url" operatorv1beta1 "github.com/cryostatio/cryostat-operator/api/v1beta1" + common "github.com/cryostatio/cryostat-operator/internal/controllers/common" "github.com/cryostatio/cryostat-operator/internal/controllers/common/resource_definitions" "github.com/cryostatio/cryostat-operator/internal/controllers/model" netv1 "k8s.io/api/networking/v1" @@ -101,7 +102,7 @@ func (r *Reconciler) createOrUpdateIngress(ctx context.Context, ingress *netv1.I config *operatorv1beta1.NetworkConfiguration) (*netv1.Ingress, error) { op, err := controllerutil.CreateOrUpdate(ctx, r.Client, ingress, func() error { // Set labels and annotations from CR - mergeLabelsAndAnnotations(&ingress.ObjectMeta, config.Labels, config.Annotations) + common.MergeLabelsAndAnnotations(&ingress.ObjectMeta, config.Labels, config.Annotations) // Set the Cryostat CR as controller if err := controllerutil.SetControllerReference(owner, ingress, r.Scheme); err != nil { diff --git a/internal/controllers/pvc.go b/internal/controllers/pvc.go index e9758964d..49bdc37b9 100644 --- a/internal/controllers/pvc.go +++ b/internal/controllers/pvc.go @@ -19,6 +19,7 @@ import ( "fmt" operatorv1beta1 "github.com/cryostatio/cryostat-operator/api/v1beta1" + common "github.com/cryostatio/cryostat-operator/internal/controllers/common" "github.com/cryostatio/cryostat-operator/internal/controllers/model" corev1 "k8s.io/api/core/v1" kerrors "k8s.io/apimachinery/pkg/api/errors" @@ -64,7 +65,7 @@ func (r *Reconciler) createOrUpdatePVC(ctx context.Context, pvc *corev1.Persiste owner metav1.Object, config *operatorv1beta1.PersistentVolumeClaimConfig) error { op, err := controllerutil.CreateOrUpdate(ctx, r.Client, pvc, func() error { // Merge labels and annotations to prevent overriding any set by Kubernetes - mergeLabelsAndAnnotations(&pvc.ObjectMeta, config.Labels, config.Annotations) + common.MergeLabelsAndAnnotations(&pvc.ObjectMeta, config.Labels, config.Annotations) // Set the Cryostat CR as controller if err := controllerutil.SetControllerReference(owner, pvc, r.Scheme); err != nil { diff --git a/internal/controllers/reconciler.go b/internal/controllers/reconciler.go index 837db7098..9cf2b0a53 100644 --- a/internal/controllers/reconciler.go +++ b/internal/controllers/reconciler.go @@ -24,7 +24,7 @@ import ( certv1 "github.com/cert-manager/cert-manager/pkg/apis/certmanager/v1" operatorv1beta1 "github.com/cryostatio/cryostat-operator/api/v1beta1" - "github.com/cryostatio/cryostat-operator/internal/controllers/common" + common "github.com/cryostatio/cryostat-operator/internal/controllers/common" resources "github.com/cryostatio/cryostat-operator/internal/controllers/common/resource_definitions" "github.com/cryostatio/cryostat-operator/internal/controllers/model" "github.com/go-logr/logr" @@ -442,7 +442,7 @@ func (r *Reconciler) createOrUpdateDeployment(ctx context.Context, deploy *appsv op, err := controllerutil.CreateOrUpdate(ctx, r.Client, deploy, func() error { // TODO consider managing labels and annotations using CRD // Merge any required labels and annotations - mergeLabelsAndAnnotations(&deploy.ObjectMeta, deployCopy.Labels, deployCopy.Annotations) + common.MergeLabelsAndAnnotations(&deploy.ObjectMeta, deployCopy.Labels, deployCopy.Annotations) // Set the Cryostat CR as controller if err := controllerutil.SetControllerReference(owner, deploy, r.Scheme); err != nil { return err @@ -461,7 +461,7 @@ func (r *Reconciler) createOrUpdateDeployment(ctx context.Context, deploy *appsv // Update pod template spec to propagate any changes from Cryostat CR deploy.Spec.Template.Spec = deployCopy.Spec.Template.Spec // Update pod template metadata - mergeLabelsAndAnnotations(&deploy.Spec.Template.ObjectMeta, deployCopy.Spec.Template.Labels, + common.MergeLabelsAndAnnotations(&deploy.Spec.Template.ObjectMeta, deployCopy.Spec.Template.Labels, deployCopy.Spec.Template.Annotations) return nil }) @@ -543,21 +543,3 @@ func findDeployCondition(conditions []appsv1.DeploymentCondition, condType appsv } return nil } - -func mergeLabelsAndAnnotations(dest *metav1.ObjectMeta, srcLabels, srcAnnotations map[string]string) { - // Check and create labels/annotations map if absent - if dest.Labels == nil { - dest.Labels = map[string]string{} - } - if dest.Annotations == nil { - dest.Annotations = map[string]string{} - } - - // Merge labels and annotations, preferring those in the source - for k, v := range srcLabels { - dest.Labels[k] = v - } - for k, v := range srcAnnotations { - dest.Annotations[k] = v - } -} diff --git a/internal/controllers/reconciler_test.go b/internal/controllers/reconciler_test.go index 064acab84..4d8184519 100644 --- a/internal/controllers/reconciler_test.go +++ b/internal/controllers/reconciler_test.go @@ -2157,6 +2157,19 @@ func (c *controllerTest) commonTests() { Expect(binding.RoleRef).To(Equal(expected.RoleRef)) }) }) + Context("with additionnal label and annotation", func() { + BeforeEach(func() { + t.ReportReplicas = 1 + t.objs = append(t.objs, t.NewCryostatWithAdditionalMetadata().Object) + }) + JustBeforeEach(func() { + t.reconcileCryostatFully() + }) + It("should have added the extra label and annotation to deployments and pods", func() { + t.expectMainDeploymentHasExtraMetadata() + t.expectReportsDeploymentHasExtraMetadata() + }) + }) }) } @@ -2604,6 +2617,30 @@ func (t *cryostatTestInput) expectMainDeployment() { t.checkMainPodTemplate(deployment, cr) } +func (t *cryostatTestInput) expectMainDeploymentHasExtraMetadata() { + deployment := &appsv1.Deployment{} + err := t.Client.Get(context.Background(), types.NamespacedName{Name: t.Name, Namespace: t.Namespace}, deployment) + Expect(err).ToNot(HaveOccurred()) + + cr := t.getCryostatInstance() + + Expect(deployment.Annotations).To(Equal(map[string]string{ + "app.openshift.io/connects-to": "cryostat-operator-controller-manager", + "myExtraAnnotation": "myAnnotation", + "mySecondExtraAnnotation": "mySecondAnnotation", + })) + Expect(deployment.Labels).To(Equal(map[string]string{ + "app": t.Name, + "kind": "cryostat", + "component": "cryostat", + "app.kubernetes.io/name": "cryostat", + "myExtraLabel": "myLabel", + "mySecondExtraLabel": "mySecondLabel", + })) + // compare Pod template + t.expectMainPodTemplateHasExtraMetadata(deployment, cr) +} + func (t *cryostatTestInput) checkMainPodTemplate(deployment *appsv1.Deployment, cr *model.CryostatInstance) { template := deployment.Spec.Template Expect(template.Name).To(Equal(t.Name)) @@ -2666,6 +2703,24 @@ func (t *cryostatTestInput) checkMainPodTemplate(deployment *appsv1.Deployment, } } +func (t *cryostatTestInput) expectMainPodTemplateHasExtraMetadata(deployment *appsv1.Deployment, cr *model.CryostatInstance) { + template := deployment.Spec.Template + Expect(template.Labels).To(Equal(map[string]string{ + "app.kubernetes.io/name": "myName", + "app": t.Name, + "kind": "cryostat", + "component": "cryostat", + "myExtraLabel": "myLabel", + "mySecondExtraLabel": "mySecondLabel", + })) + Expect(template.Annotations).To(Equal(map[string]string{ + "myExtraAnnotation": "myAnnotation", + "mySecondExtraAnnotation": "mySecondAnnotation", + // This one comes from the user defined list, not overriden by the default + "app.openshift.io/connects-to": "connectToMe", + })) +} + func (t *cryostatTestInput) checkReportsDeployment() { deployment := &appsv1.Deployment{} err := t.Client.Get(context.Background(), types.NamespacedName{Name: t.Name + "-reports", Namespace: t.Namespace}, deployment) @@ -2720,6 +2775,41 @@ func (t *cryostatTestInput) checkReportsDeployment() { } } +func (t *cryostatTestInput) expectReportsDeploymentHasExtraMetadata() { + reportDeployment := &appsv1.Deployment{} + err := t.Client.Get(context.Background(), types.NamespacedName{Name: t.Name + "-reports", Namespace: t.Namespace}, reportDeployment) + Expect(err).ToNot(HaveOccurred()) + Expect(reportDeployment.Annotations).To(Equal(map[string]string{ + "app.openshift.io/connects-to": t.Name, + "myExtraAnnotation": "myAnnotation", + "mySecondExtraAnnotation": "mySecondAnnotation", + })) + Expect(reportDeployment.Labels).To(Equal(map[string]string{ + "app": t.Name, + "kind": "cryostat", + "component": "reports", + "app.kubernetes.io/name": "cryostat-reports", + "myExtraLabel": "myLabel", + "mySecondExtraLabel": "mySecondLabel", + })) + template := reportDeployment.Spec.Template + Expect(template.Labels).To(Equal(map[string]string{ + "app": t.Name, + // This one comes from the user defined list, not overriden by the default + "app.kubernetes.io/name": "myName", + "kind": "cryostat", + "component": "reports", + "myExtraLabel": "myLabel", + "mySecondExtraLabel": "mySecondLabel", + })) + Expect(template.Annotations).To(Equal(map[string]string{ + "myExtraAnnotation": "myAnnotation", + "mySecondExtraAnnotation": "mySecondAnnotation", + // This one comes from the user defined list, not overriden by the default + "app.openshift.io/connects-to": "connectToMe", + })) +} + func (t *cryostatTestInput) checkDeploymentHasTemplates() { deployment := &appsv1.Deployment{} err := t.Client.Get(context.Background(), types.NamespacedName{Name: t.Name, Namespace: t.Namespace}, deployment) diff --git a/internal/controllers/routes.go b/internal/controllers/routes.go index a19305525..97564639b 100644 --- a/internal/controllers/routes.go +++ b/internal/controllers/routes.go @@ -21,6 +21,7 @@ import ( "net/url" operatorv1beta1 "github.com/cryostatio/cryostat-operator/api/v1beta1" + common "github.com/cryostatio/cryostat-operator/internal/controllers/common" "github.com/cryostatio/cryostat-operator/internal/controllers/common/resource_definitions" "github.com/cryostatio/cryostat-operator/internal/controllers/constants" "github.com/cryostatio/cryostat-operator/internal/controllers/model" @@ -117,7 +118,7 @@ func (r *Reconciler) createOrUpdateRoute(ctx context.Context, route *routev1.Rou op, err := controllerutil.CreateOrUpdate(ctx, r.Client, route, func() error { // Set labels and annotations from CR - mergeLabelsAndAnnotations(&route.ObjectMeta, config.Labels, config.Annotations) + common.MergeLabelsAndAnnotations(&route.ObjectMeta, config.Labels, config.Annotations) // Set the Cryostat CR as controller if err := controllerutil.SetControllerReference(owner, route, r.Scheme); err != nil { diff --git a/internal/controllers/services.go b/internal/controllers/services.go index 222788481..2332ee0cc 100644 --- a/internal/controllers/services.go +++ b/internal/controllers/services.go @@ -21,6 +21,7 @@ import ( "strconv" operatorv1beta1 "github.com/cryostatio/cryostat-operator/api/v1beta1" + common "github.com/cryostatio/cryostat-operator/internal/controllers/common" "github.com/cryostatio/cryostat-operator/internal/controllers/common/resource_definitions" "github.com/cryostatio/cryostat-operator/internal/controllers/constants" "github.com/cryostatio/cryostat-operator/internal/controllers/model" @@ -246,7 +247,7 @@ func (r *Reconciler) createOrUpdateService(ctx context.Context, svc *corev1.Serv config *operatorv1beta1.ServiceConfig, delegate controllerutil.MutateFn) error { op, err := controllerutil.CreateOrUpdate(ctx, r.Client, svc, func() error { // Update labels and annotations - mergeLabelsAndAnnotations(&svc.ObjectMeta, config.Labels, config.Annotations) + common.MergeLabelsAndAnnotations(&svc.ObjectMeta, config.Labels, config.Annotations) // Set the Cryostat CR as controller if err := controllerutil.SetControllerReference(owner, svc, r.Scheme); err != nil { diff --git a/internal/test/resources.go b/internal/test/resources.go index fae7503e3..08a76db69 100644 --- a/internal/test/resources.go +++ b/internal/test/resources.go @@ -654,6 +654,28 @@ func (r *TestResources) NewCryostatWithDatabaseSecretProvided() *model.CryostatI return cr } +func (r *TestResources) NewCryostatWithAdditionalMetadata() *model.CryostatInstance { + cr := r.NewCryostat() + cr.Spec.Metadata = &operatorv1beta1.ResourceMetadata{ + Labels: map[string]string{ + "myExtraLabel": "myLabel", + "mySecondExtraLabel": "mySecondLabel", + // below, labels that should be discarded as overriden by the default + "app": "myApp", + "component": "myComponent", + "kind": "myKind", + "app.kubernetes.io/name": "myName", + }, + Annotations: map[string]string{ + "myExtraAnnotation": "myAnnotation", + "mySecondExtraAnnotation": "mySecondAnnotation", + // below, annotation that should be discarded as overriden by the default + "app.openshift.io/connects-to": "connectToMe", + }, + } + return cr +} + func (r *TestResources) NewCryostatService() *corev1.Service { c := true return &corev1.Service{