From 98c17f8de7c11b5a7d1070d3a03d056b02910325 Mon Sep 17 00:00:00 2001 From: Peng Zhou Date: Fri, 25 Jul 2025 07:25:29 -0700 Subject: [PATCH 1/9] MLE-22946: Fix the bug of cluster level labels propagate from other cluster. --- pkg/k8sutil/common.go | 52 ------------- pkg/k8sutil/configmap.go | 4 +- pkg/k8sutil/context.go | 110 +++++++++++++++++++++++---- pkg/k8sutil/haProxy.go | 6 +- pkg/k8sutil/handler.go | 2 - pkg/k8sutil/ingress.go | 8 +- pkg/k8sutil/marklogicServer.go | 8 +- pkg/k8sutil/networkPolicy.go | 8 +- pkg/k8sutil/secret.go | 4 +- pkg/k8sutil/service.go | 12 ++- test/e2e/2_marklogic_cluster_test.go | 2 +- 11 files changed, 121 insertions(+), 95 deletions(-) diff --git a/pkg/k8sutil/common.go b/pkg/k8sutil/common.go index 5172c80..635c255 100644 --- a/pkg/k8sutil/common.go +++ b/pkg/k8sutil/common.go @@ -9,9 +9,6 @@ import ( "sigs.k8s.io/controller-runtime/pkg/client" ) -var CustomLabels = map[string]string{} -var CustomAnnotations = map[string]string{} - // generateTypeMeta generates the TyeMeta func generateTypeMeta(resourceKind string, apiVersion string) metav1.TypeMeta { return metav1.TypeMeta{ @@ -49,15 +46,6 @@ func LabelSelectors(labels map[string]string) *metav1.LabelSelector { return &metav1.LabelSelector{MatchLabels: labels} } -func SetCommonLabels(labels map[string]string) { - CustomLabels = labels -} - -func SetCommonAnnotations(annotations map[string]string) { - delete(annotations, "kubectl.kubernetes.io/last-applied-configuration") - CustomAnnotations = annotations -} - func getSelectorLabels(name string) map[string]string { selectorLabels := map[string]string{ "app.kubernetes.io/name": "marklogic", @@ -78,46 +66,6 @@ func getHAProxySelectorLabels(name string) map[string]string { return selectorLabels } -func getHAProxyLabels(name string) map[string]string { - defaultHaproxyLabels := getHAProxySelectorLabels(name) - mergedLabels := map[string]string{} - if len(CustomLabels) > 0 { - for k, v := range defaultHaproxyLabels { - mergedLabels[k] = v - } - for k, v := range CustomLabels { - if _, ok := defaultHaproxyLabels[k]; !ok { - mergedLabels[k] = v - } - } - } else { - return defaultHaproxyLabels - } - return mergedLabels -} - -func getCommonLabels(name string) map[string]string { - defaultLabels := getSelectorLabels(name) - mergedLabels := map[string]string{} - if len(CustomLabels) > 0 { - for k, v := range defaultLabels { - mergedLabels[k] = v - } - for k, v := range CustomLabels { - if _, ok := defaultLabels[k]; !ok { - mergedLabels[k] = v - } - } - } else { - return defaultLabels - } - return mergedLabels -} - -func getCommonAnnotations() map[string]string { - return CustomAnnotations -} - func getFluentBitLabels(name string) map[string]string { return map[string]string{ "app.kubernetes.io/name": "fluent-bit", diff --git a/pkg/k8sutil/configmap.go b/pkg/k8sutil/configmap.go index 2c225dc..795642e 100644 --- a/pkg/k8sutil/configmap.go +++ b/pkg/k8sutil/configmap.go @@ -19,8 +19,8 @@ func (oc *OperatorContext) ReconcileConfigMap() result.ReconcileResult { cr := oc.MarklogicGroup logger.Info("Reconciling MarkLogic ConfigMap") - labels := getCommonLabels(cr.Spec.Name) - annotations := getCommonAnnotations() + labels := oc.GetOperatorLabels(cr.Spec.Name) + annotations := oc.GetOperatorAnnotations() configMapName := cr.Spec.Name + "-scripts" objectMeta := generateObjectMeta(configMapName, cr.Namespace, labels, annotations) nsName := types.NamespacedName{Name: objectMeta.Name, Namespace: objectMeta.Namespace} diff --git a/pkg/k8sutil/context.go b/pkg/k8sutil/context.go index 73b2f75..710a549 100644 --- a/pkg/k8sutil/context.go +++ b/pkg/k8sutil/context.go @@ -15,22 +15,23 @@ import ( ) type OperatorContext struct { - Ctx context.Context - + Ctx context.Context + Labels map[string]string + Annotations map[string]string Request *reconcile.Request Client controllerClient.Client Scheme *runtime.Scheme MarklogicGroup *marklogicv1.MarklogicGroup ReqLogger logr.Logger Recorder record.EventRecorder - - Services []*corev1.Service - StatefulSets []*appsv1.StatefulSet + Services []*corev1.Service + StatefulSets []*appsv1.StatefulSet } type ClusterContext struct { - Ctx context.Context - + Ctx context.Context + Labels map[string]string + Annotations map[string]string Request *reconcile.Request Client controllerClient.Client Scheme *runtime.Scheme @@ -57,17 +58,16 @@ func CreateOperatorContext( oc.Scheme = scheme oc.ReqLogger = reqLogger oc.Recorder = rec + oc.Labels = map[string]string{} + oc.Annotations = map[string]string{} mlg := &marklogicv1.MarklogicGroup{} if err := retrieveMarkLogicGroup(oc, request, mlg); err != nil { oc.ReqLogger.Error(err, "Failed to retrieve MarkLogicServer") return nil, err } - // if err := retrieveMarklogicCluster(oc, request, mlc); err != nil { - // oc.ReqLogger.Error(err, "Failed to retrieve MarkLogicCluster") - // return nil, err - // } oc.MarklogicGroup = mlg - // oc.MarklogicCluster = mlc + oc.SetOperatorLabels(oc.MarklogicGroup.GetLabels()) + oc.SetOperatorAnnotations(oc.MarklogicGroup.GetAnnotations()) oc.ReqLogger.Info("==== CreateOperatorContext") @@ -92,14 +92,16 @@ func CreateClusterContext( cc.Scheme = scheme cc.ReqLogger = reqLogger cc.Recorder = rec + cc.Labels = map[string]string{} + cc.Annotations = map[string]string{} mlc := &marklogicv1.MarklogicCluster{} - if err := retrieveMarklogicCluster(cc, request, mlc); err != nil { cc.ReqLogger.Error(err, "Failed to retrieve MarkLogicCluster") return nil, err } cc.MarklogicCluster = mlc - + cc.SetClusterLabels(cc.MarklogicCluster.GetLabels()) + cc.SetClusterAnnotations(cc.MarklogicCluster.GetAnnotations()) cc.ReqLogger.Info("==== CreateOperatorContext") // cc.ReqLogger = cc.ReqLogger.WithValues("ML server name") @@ -137,3 +139,83 @@ func (oc *OperatorContext) GetClient() controllerClient.Client { func (oc *OperatorContext) GetContext() context.Context { return oc.Ctx } + +func (cc *ClusterContext) GetClusterLabels(name string) map[string]string { + defaultLabels := getSelectorLabels(name) + mergedLabels := map[string]string{} + if len(cc.Labels) > 0 { + for k, v := range defaultLabels { + mergedLabels[k] = v + } + for k, v := range cc.Labels { + if _, ok := defaultLabels[k]; !ok { + mergedLabels[k] = v + } + } + } else { + return defaultLabels + } + return mergedLabels +} + +func (cc *ClusterContext) GetHAProxyLabels(name string) map[string]string { + defaultHaproxyLabels := getHAProxySelectorLabels(name) + mergedLabels := map[string]string{} + if len(cc.Labels) > 0 { + for k, v := range defaultHaproxyLabels { + mergedLabels[k] = v + } + for k, v := range cc.Labels { + if _, ok := defaultHaproxyLabels[k]; !ok { + mergedLabels[k] = v + } + } + } else { + return defaultHaproxyLabels + } + return mergedLabels +} + +func (cc *ClusterContext) GetClusterAnnotations() map[string]string { + return cc.Annotations +} + +func (cc *ClusterContext) SetClusterLabels(labels map[string]string) { + cc.Labels = labels +} + +func (cc *ClusterContext) SetClusterAnnotations(annotations map[string]string) { + delete(annotations, "kubectl.kubernetes.io/last-applied-configuration") + cc.Annotations = annotations +} + +func (oc *OperatorContext) GetOperatorLabels(name string) map[string]string { + defaultLabels := getSelectorLabels(name) + mergedLabels := map[string]string{} + if len(oc.Labels) > 0 { + for k, v := range defaultLabels { + mergedLabels[k] = v + } + for k, v := range oc.Labels { + if _, ok := defaultLabels[k]; !ok { + mergedLabels[k] = v + } + } + } else { + return defaultLabels + } + return mergedLabels +} + +func (oc *OperatorContext) GetOperatorAnnotations() map[string]string { + return oc.Annotations +} + +func (oc *OperatorContext) SetOperatorLabels(labels map[string]string) { + oc.Labels = labels +} + +func (oc *OperatorContext) SetOperatorAnnotations(annotations map[string]string) { + delete(annotations, "kubectl.kubernetes.io/last-applied-configuration") + oc.Annotations = annotations +} diff --git a/pkg/k8sutil/haProxy.go b/pkg/k8sutil/haProxy.go index 4de8312..19e3cb2 100644 --- a/pkg/k8sutil/haProxy.go +++ b/pkg/k8sutil/haProxy.go @@ -24,8 +24,8 @@ func (cc *ClusterContext) ReconcileHAProxy() result.ReconcileResult { logger.Info("Reconciling HAProxy Config") - labels := getHAProxyLabels(cr.GetObjectMeta().GetName()) - annotations := getCommonAnnotations() + labels := cc.GetHAProxyLabels(cr.GetObjectMeta().GetName()) + annotations := cc.GetClusterAnnotations() configMapName := "marklogic-haproxy" objectMeta := generateObjectMeta(configMapName, cr.Namespace, labels, annotations) nsName := types.NamespacedName{Name: objectMeta.Name, Namespace: objectMeta.Namespace} @@ -363,7 +363,7 @@ func (cc *ClusterContext) generateHaproxyServiceDef(meta metav1.ObjectMeta) *cor Port: cr.Spec.HAProxy.Stats.Port, }) } - selectorLabels := getHAProxyLabels(cr.GetObjectMeta().GetName()) + selectorLabels := getHAProxySelectorLabels(cr.GetObjectMeta().GetName()) serviceDef := &corev1.Service{ ObjectMeta: metav1.ObjectMeta{ Name: "marklogic-haproxy", diff --git a/pkg/k8sutil/handler.go b/pkg/k8sutil/handler.go index 4474e64..2bc8fbb 100644 --- a/pkg/k8sutil/handler.go +++ b/pkg/k8sutil/handler.go @@ -28,8 +28,6 @@ func (oc *OperatorContext) ReconsileMarklogicGroupHandler() (reconcile.Result, e } func (cc *ClusterContext) ReconsileMarklogicClusterHandler() (reconcile.Result, error) { - SetCommonAnnotations(cc.MarklogicCluster.GetAnnotations()) - SetCommonLabels(cc.MarklogicCluster.GetLabels()) if result := cc.ReconcileServiceAccount(); result.Completed() { return result.Output() } diff --git a/pkg/k8sutil/ingress.go b/pkg/k8sutil/ingress.go index a6bb30d..df2d34a 100644 --- a/pkg/k8sutil/ingress.go +++ b/pkg/k8sutil/ingress.go @@ -72,8 +72,8 @@ func (cc *ClusterContext) getIngress(namespace string, ingressName string) (*net return ingress, nil } -func generateIngress(ingressName string, cr *marklogicv1.MarklogicCluster) *networkingv1.Ingress { - labels := getCommonLabels(cr.GetObjectMeta().GetName()) +func (cc *ClusterContext) generateIngress(ingressName string, cr *marklogicv1.MarklogicCluster) *networkingv1.Ingress { + labels := cc.GetClusterLabels(cr.GetObjectMeta().GetName()) annotations := cr.Spec.HAProxy.Ingress.Annotations ingressObjectMeta := generateObjectMeta(ingressName, cr.Namespace, labels, annotations) ingress := generateIngressDef(ingressObjectMeta, marklogicClusterAsOwner(cr), cr) @@ -87,7 +87,7 @@ func (cc *ClusterContext) ReconcileIngress() result.ReconcileResult { cr := cc.MarklogicCluster ingressName := cr.ObjectMeta.Name currentIngress, err := cc.getIngress(cr.Namespace, ingressName) - ingressDef := generateIngress(ingressName, cr) + ingressDef := cc.generateIngress(ingressName, cr) if err != nil { if errors.IsNotFound(err) { logger.Info("MarkLogic Ingress not found, creating a new one") @@ -133,7 +133,7 @@ func (cc *ClusterContext) createIngress(namespace string) error { client := cc.Client cr := cc.MarklogicCluster ingressName := cr.ObjectMeta.Name + "-ingress" - ingress := generateIngress(ingressName, cr) + ingress := cc.generateIngress(ingressName, cr) err := client.Create(cc.Ctx, ingress) if err != nil { logger.Error(err, "MarkLogic ingress creation has failed") diff --git a/pkg/k8sutil/marklogicServer.go b/pkg/k8sutil/marklogicServer.go index 66f86dd..5e2997e 100644 --- a/pkg/k8sutil/marklogicServer.go +++ b/pkg/k8sutil/marklogicServer.go @@ -86,11 +86,11 @@ func MarkLogicGroupLogger(namespace string, name string) logr.Logger { return reqLogger } -func GenerateMarkLogicGroupDef(cr *marklogicv1.MarklogicCluster, index int, params *MarkLogicGroupParameters) *marklogicv1.MarklogicGroup { +func (cc *ClusterContext) GenerateMarkLogicGroupDef(cr *marklogicv1.MarklogicCluster, index int, params *MarkLogicGroupParameters) *marklogicv1.MarklogicGroup { logger := MarkLogicGroupLogger(cr.Namespace, cr.ObjectMeta.Name) logger.Info("ReconcileMarkLogicCluster") - labels := getCommonLabels(cr.ObjectMeta.Name) - annotations := getCommonAnnotations() + labels := cc.GetClusterLabels(cr.ObjectMeta.Name) + annotations := cc.GetClusterAnnotations() if params.Labels != nil { for key, value := range params.Labels { labels[key] = value @@ -172,7 +172,7 @@ func (cc *ClusterContext) ReconsileMarklogicCluster() (reconcile.Result, error) namespacedName := types.NamespacedName{Name: name, Namespace: namespace} clusterParams := generateMarkLogicClusterParams(cr) params := generateMarkLogicGroupParams(cr, i, clusterParams) - markLogicGroupDef := GenerateMarkLogicGroupDef(operatorCR, i, params) + markLogicGroupDef := cc.GenerateMarkLogicGroupDef(operatorCR, i, params) err := cc.Client.Get(cc.Ctx, namespacedName, currentMlg) if err != nil { if apierrors.IsNotFound(err) { diff --git a/pkg/k8sutil/networkPolicy.go b/pkg/k8sutil/networkPolicy.go index b904c79..56578da 100644 --- a/pkg/k8sutil/networkPolicy.go +++ b/pkg/k8sutil/networkPolicy.go @@ -44,9 +44,9 @@ func (cc *ClusterContext) getNetworkPolicy(namespace string, networkPolicyName s return networkPolicy, nil } -func generateNetworkPolicy(networkPolicyName string, cr *marklogicv1.MarklogicCluster) *networkingv1.NetworkPolicy { - labels := getCommonLabels(cr.GetObjectMeta().GetName()) - annotations := getCommonAnnotations() +func (cc *ClusterContext) generateNetworkPolicy(networkPolicyName string, cr *marklogicv1.MarklogicCluster) *networkingv1.NetworkPolicy { + labels := cc.GetClusterLabels(cr.GetObjectMeta().GetName()) + annotations := cc.GetClusterAnnotations() netObjectMeta := generateObjectMeta(networkPolicyName, cr.Namespace, labels, annotations) networkPolicy := generateNetworkPolicyDef(netObjectMeta, marklogicClusterAsOwner(cr), cr) return networkPolicy @@ -59,7 +59,7 @@ func (cc *ClusterContext) ReconcileNetworkPolicy() result.ReconcileResult { cr := cc.MarklogicCluster networkPolicyName := cr.ObjectMeta.Name currentNetworkPolicy, err := cc.getNetworkPolicy(cr.Namespace, networkPolicyName) - networkPolicyDef := generateNetworkPolicy(networkPolicyName, cr) + networkPolicyDef := cc.generateNetworkPolicy(networkPolicyName, cr) if err != nil { if errors.IsNotFound(err) { logger.Info("MarkLogic NetworkPolicy not found, creating a new one") diff --git a/pkg/k8sutil/secret.go b/pkg/k8sutil/secret.go index c7a6a95..720cc7b 100644 --- a/pkg/k8sutil/secret.go +++ b/pkg/k8sutil/secret.go @@ -19,8 +19,8 @@ func (cc *ClusterContext) ReconcileSecret() result.ReconcileResult { } logger.Info("Reconciling MarkLogic Secret") - labels := getCommonLabels(mlc.ObjectMeta.Name) - annotations := getCommonAnnotations() + labels := cc.GetClusterLabels(mlc.ObjectMeta.Name) + annotations := cc.GetClusterAnnotations() secretName := mlc.ObjectMeta.Name + "-admin" objectMeta := generateObjectMeta(secretName, mlc.Namespace, labels, annotations) nsName := types.NamespacedName{Name: objectMeta.Name, Namespace: objectMeta.Namespace} diff --git a/pkg/k8sutil/service.go b/pkg/k8sutil/service.go index 35bc807..1a1569d 100644 --- a/pkg/k8sutil/service.go +++ b/pkg/k8sutil/service.go @@ -95,13 +95,11 @@ func generateServiceDef(serviceMeta metav1.ObjectMeta, ownerRef metav1.OwnerRefe return service } -func generateService(svcName string, cr *marklogicv1.MarklogicGroup) *corev1.Service { - labels := getCommonLabels(cr.Spec.Name) +func (oc *OperatorContext) generateService(svcName string, cr *marklogicv1.MarklogicGroup) *corev1.Service { + labels := oc.GetOperatorLabels(cr.Spec.Name) groupLabels := cr.Spec.Labels - if groupLabels != nil { - for key, value := range groupLabels { - labels[key] = value - } + for key, value := range groupLabels { + labels[key] = value } var svcParams serviceParameters = serviceParameters{} svcParams = generateServiceParams(cr) @@ -122,7 +120,7 @@ func (oc *OperatorContext) ReconcileServices() result.ReconcileResult { for _, service := range services { svcNsName := types.NamespacedName{Name: service, Namespace: cr.Namespace} err := client.Get(oc.Ctx, svcNsName, currentSvc) - svcDef := generateService(service, cr) + svcDef := oc.generateService(service, cr) if err != nil { if errors.IsNotFound(err) { logger.Info("MarkLogic service not found, creating a new one") diff --git a/test/e2e/2_marklogic_cluster_test.go b/test/e2e/2_marklogic_cluster_test.go index 4e3986e..4171742 100644 --- a/test/e2e/2_marklogic_cluster_test.go +++ b/test/e2e/2_marklogic_cluster_test.go @@ -344,7 +344,7 @@ func TestMarklogicCluster(t *testing.T) { if err := client.Resources().Get(ctx, "marklogicclusters", mlNamespace, &mlcluster); err != nil { t.Fatal(err) } - + mlcluster.Spec.MarkLogicGroups[0].Resources = &resources if err := client.Resources().Update(ctx, &mlcluster); err != nil { t.Log("Failed to update MarkLogic group resources") From 5cfc00543bca90c6c3960b157f6ae45ecda98f74 Mon Sep 17 00:00:00 2001 From: Peng Zhou <27710236+pengzhouml@users.noreply.github.com> Date: Wed, 30 Jul 2025 22:59:18 -0700 Subject: [PATCH 2/9] Update pkg/k8sutil/service.go Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- pkg/k8sutil/service.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pkg/k8sutil/service.go b/pkg/k8sutil/service.go index 1a1569d..0ee477b 100644 --- a/pkg/k8sutil/service.go +++ b/pkg/k8sutil/service.go @@ -98,6 +98,9 @@ func generateServiceDef(serviceMeta metav1.ObjectMeta, ownerRef metav1.OwnerRefe func (oc *OperatorContext) generateService(svcName string, cr *marklogicv1.MarklogicGroup) *corev1.Service { labels := oc.GetOperatorLabels(cr.Spec.Name) groupLabels := cr.Spec.Labels + if groupLabels == nil { + groupLabels = make(map[string]string) + } for key, value := range groupLabels { labels[key] = value } From 4343f764f604553ca66c66185eea8007e22018dd Mon Sep 17 00:00:00 2001 From: Peng Zhou <27710236+pengzhouml@users.noreply.github.com> Date: Wed, 30 Jul 2025 22:59:34 -0700 Subject: [PATCH 3/9] Update pkg/k8sutil/context.go Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- pkg/k8sutil/context.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pkg/k8sutil/context.go b/pkg/k8sutil/context.go index 710a549..6c68c25 100644 --- a/pkg/k8sutil/context.go +++ b/pkg/k8sutil/context.go @@ -185,6 +185,9 @@ func (cc *ClusterContext) SetClusterLabels(labels map[string]string) { } func (cc *ClusterContext) SetClusterAnnotations(annotations map[string]string) { + if annotations == nil { + annotations = make(map[string]string) + } delete(annotations, "kubectl.kubernetes.io/last-applied-configuration") cc.Annotations = annotations } From 29990a67baf3e041014000519e2eef82570155a4 Mon Sep 17 00:00:00 2001 From: Peng Zhou <27710236+pengzhouml@users.noreply.github.com> Date: Wed, 30 Jul 2025 22:59:44 -0700 Subject: [PATCH 4/9] Update pkg/k8sutil/context.go Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- pkg/k8sutil/context.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pkg/k8sutil/context.go b/pkg/k8sutil/context.go index 6c68c25..7cd4312 100644 --- a/pkg/k8sutil/context.go +++ b/pkg/k8sutil/context.go @@ -219,6 +219,9 @@ func (oc *OperatorContext) SetOperatorLabels(labels map[string]string) { } func (oc *OperatorContext) SetOperatorAnnotations(annotations map[string]string) { + if annotations == nil { + annotations = make(map[string]string) + } delete(annotations, "kubectl.kubernetes.io/last-applied-configuration") oc.Annotations = annotations } From 1afab37d1c6ef383199b8f9b9fc4f583b5aa223e Mon Sep 17 00:00:00 2001 From: Peng Zhou Date: Tue, 12 Aug 2025 23:14:17 -0700 Subject: [PATCH 5/9] revert copilot changes --- pkg/k8sutil/context.go | 6 ------ pkg/k8sutil/service.go | 3 --- 2 files changed, 9 deletions(-) diff --git a/pkg/k8sutil/context.go b/pkg/k8sutil/context.go index 7cd4312..710a549 100644 --- a/pkg/k8sutil/context.go +++ b/pkg/k8sutil/context.go @@ -185,9 +185,6 @@ func (cc *ClusterContext) SetClusterLabels(labels map[string]string) { } func (cc *ClusterContext) SetClusterAnnotations(annotations map[string]string) { - if annotations == nil { - annotations = make(map[string]string) - } delete(annotations, "kubectl.kubernetes.io/last-applied-configuration") cc.Annotations = annotations } @@ -219,9 +216,6 @@ func (oc *OperatorContext) SetOperatorLabels(labels map[string]string) { } func (oc *OperatorContext) SetOperatorAnnotations(annotations map[string]string) { - if annotations == nil { - annotations = make(map[string]string) - } delete(annotations, "kubectl.kubernetes.io/last-applied-configuration") oc.Annotations = annotations } diff --git a/pkg/k8sutil/service.go b/pkg/k8sutil/service.go index 0ee477b..1a1569d 100644 --- a/pkg/k8sutil/service.go +++ b/pkg/k8sutil/service.go @@ -98,9 +98,6 @@ func generateServiceDef(serviceMeta metav1.ObjectMeta, ownerRef metav1.OwnerRefe func (oc *OperatorContext) generateService(svcName string, cr *marklogicv1.MarklogicGroup) *corev1.Service { labels := oc.GetOperatorLabels(cr.Spec.Name) groupLabels := cr.Spec.Labels - if groupLabels == nil { - groupLabels = make(map[string]string) - } for key, value := range groupLabels { labels[key] = value } From 043e92c9dfac45057fdcadaef62c4af4658dc48a Mon Sep 17 00:00:00 2001 From: Peng Zhou Date: Wed, 13 Aug 2025 21:04:26 -0700 Subject: [PATCH 6/9] Fix bug MLE-23487: Log Collection Default Value Override --- api/v1/common_types.go | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/api/v1/common_types.go b/api/v1/common_types.go index 73d66d1..26be781 100644 --- a/api/v1/common_types.go +++ b/api/v1/common_types.go @@ -57,12 +57,17 @@ type AdminAuth struct { } type LogCollection struct { - Enabled bool `json:"enabled,omitempty"` + // +kubebuilder:default:=false + Enabled bool `json:"enabled,omitempty"` + // +kubebuilder:default:="fluent/fluent-bit:3.2.5" Image string `json:"image,omitempty"` ImagePullSecrets []corev1.LocalObjectReference `json:"imagePullSecrets,omitempty"` - Resources *corev1.ResourceRequirements `json:"resources,omitempty"` - Files LogFilesConfig `json:"files,omitempty"` - Outputs string `json:"outputs,omitempty"` + // +kubebuilder:default:={"requests":{"cpu":"100m","memory":"200Mi"},"limits":{"cpu":"200m","memory":"500Mi"}} + Resources *corev1.ResourceRequirements `json:"resources,omitempty"` + // +kubebuilder:default:={errorLogs: true, accessLogs: true, requestLogs: true} + Files LogFilesConfig `json:"files,omitempty"` + // +kubebuilder:default:="[OUTPUT]\n name loki\n match *\n host loki.default.svc.cluster.local\n port 3100\n labels job=fluent-bit\n http_user admin\n http_passwd admin" + Outputs string `json:"outputs,omitempty"` } type LogFilesConfig struct { From 4238ee24add3ad979b0415ba22374992f44b78f7 Mon Sep 17 00:00:00 2001 From: Peng Zhou Date: Wed, 13 Aug 2025 22:13:33 -0700 Subject: [PATCH 7/9] MLE-23487 default values for LogCollection --- api/v1/common_types.go | 2 +- ...klogic.progress.com_marklogicclusters.yaml | 48 +++++++++++++++++++ ...arklogic.progress.com_marklogicgroups.yaml | 24 ++++++++++ 3 files changed, 73 insertions(+), 1 deletion(-) diff --git a/api/v1/common_types.go b/api/v1/common_types.go index 26be781..f0d6888 100644 --- a/api/v1/common_types.go +++ b/api/v1/common_types.go @@ -64,7 +64,7 @@ type LogCollection struct { ImagePullSecrets []corev1.LocalObjectReference `json:"imagePullSecrets,omitempty"` // +kubebuilder:default:={"requests":{"cpu":"100m","memory":"200Mi"},"limits":{"cpu":"200m","memory":"500Mi"}} Resources *corev1.ResourceRequirements `json:"resources,omitempty"` - // +kubebuilder:default:={errorLogs: true, accessLogs: true, requestLogs: true} + // +kubebuilder:default:={errorLogs: true, accessLogs: true, requestLogs: true, crashLogs: true, auditLogs: true} Files LogFilesConfig `json:"files,omitempty"` // +kubebuilder:default:="[OUTPUT]\n name loki\n match *\n host loki.default.svc.cluster.local\n port 3100\n labels job=fluent-bit\n http_user admin\n http_passwd admin" Outputs string `json:"outputs,omitempty"` diff --git a/config/crd/bases/marklogic.progress.com_marklogicclusters.yaml b/config/crd/bases/marklogic.progress.com_marklogicclusters.yaml index 8eee952..9d27b2f 100644 --- a/config/crd/bases/marklogic.progress.com_marklogicclusters.yaml +++ b/config/crd/bases/marklogic.progress.com_marklogicclusters.yaml @@ -4639,8 +4639,15 @@ spec: memory: 200Mi properties: enabled: + default: false type: boolean files: + default: + accessLogs: true + auditLogs: true + crashLogs: true + errorLogs: true + requestLogs: true properties: accessLogs: type: boolean @@ -4654,6 +4661,7 @@ spec: type: boolean type: object image: + default: fluent/fluent-bit:3.2.5 type: string imagePullSecrets: items: @@ -4674,8 +4682,24 @@ spec: x-kubernetes-map-type: atomic type: array outputs: + default: |- + [OUTPUT] + name loki + match * + host loki.default.svc.cluster.local + port 3100 + labels job=fluent-bit + http_user admin + http_passwd admin type: string resources: + default: + limits: + cpu: 200m + memory: 500Mi + requests: + cpu: 100m + memory: 200Mi description: ResourceRequirements describes the compute resource requirements. properties: @@ -9367,8 +9391,15 @@ spec: logCollection: properties: enabled: + default: false type: boolean files: + default: + accessLogs: true + auditLogs: true + crashLogs: true + errorLogs: true + requestLogs: true properties: accessLogs: type: boolean @@ -9382,6 +9413,7 @@ spec: type: boolean type: object image: + default: fluent/fluent-bit:3.2.5 type: string imagePullSecrets: items: @@ -9402,8 +9434,24 @@ spec: x-kubernetes-map-type: atomic type: array outputs: + default: |- + [OUTPUT] + name loki + match * + host loki.default.svc.cluster.local + port 3100 + labels job=fluent-bit + http_user admin + http_passwd admin type: string resources: + default: + limits: + cpu: 200m + memory: 500Mi + requests: + cpu: 100m + memory: 200Mi description: ResourceRequirements describes the compute resource requirements. properties: diff --git a/config/crd/bases/marklogic.progress.com_marklogicgroups.yaml b/config/crd/bases/marklogic.progress.com_marklogicgroups.yaml index 3e17273..7ec9c3b 100644 --- a/config/crd/bases/marklogic.progress.com_marklogicgroups.yaml +++ b/config/crd/bases/marklogic.progress.com_marklogicgroups.yaml @@ -3382,8 +3382,15 @@ spec: memory: 200Mi properties: enabled: + default: false type: boolean files: + default: + accessLogs: true + auditLogs: true + crashLogs: true + errorLogs: true + requestLogs: true properties: accessLogs: type: boolean @@ -3397,6 +3404,7 @@ spec: type: boolean type: object image: + default: fluent/fluent-bit:3.2.5 type: string imagePullSecrets: items: @@ -3417,8 +3425,24 @@ spec: x-kubernetes-map-type: atomic type: array outputs: + default: |- + [OUTPUT] + name loki + match * + host loki.default.svc.cluster.local + port 3100 + labels job=fluent-bit + http_user admin + http_passwd admin type: string resources: + default: + limits: + cpu: 200m + memory: 500Mi + requests: + cpu: 100m + memory: 200Mi description: ResourceRequirements describes the compute resource requirements. properties: From a20112d039ab06c254f9c70bda6a69bcb1e738fa Mon Sep 17 00:00:00 2001 From: Peng Zhou Date: Wed, 13 Aug 2025 22:14:07 -0700 Subject: [PATCH 8/9] MLE-23484: Fix Log Collection Test Failure --- test/e2e/2_marklogic_cluster_test.go | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/test/e2e/2_marklogic_cluster_test.go b/test/e2e/2_marklogic_cluster_test.go index 4171742..c1bace1 100644 --- a/test/e2e/2_marklogic_cluster_test.go +++ b/test/e2e/2_marklogic_cluster_test.go @@ -186,12 +186,15 @@ func TestMarklogicCluster(t *testing.T) { } if !(strings.Contains(string(output), "Datasource added") && strings.Contains(string(output), "Loki")) { t.Fatal("Failed to create datasource for Grafana") + } else { + t.Logf("Datasource created successfully: %s", output) } var dataSourceResponse DataSourceResponse if err := json.Unmarshal([]byte(output), &dataSourceResponse); err != nil { t.Fatalf("Failed to unmarshal JSON response: %v", err) } dataSourceUID = dataSourceResponse.DataSource.UID + t.Logf("Datasource UID: %s", dataSourceUID) return ctx }) @@ -272,6 +275,8 @@ func TestMarklogicCluster(t *testing.T) { dashboardUID = dashboardResponse.UID if dashboardResponse.Status != "success" { t.Fatal("Failed to create dashboard with loki and fluent-bit") + } else { + t.Logf("Dashboard created successfully with UID: %s", dashboardResponse.UID) } // Create query to verify MarkLogic logs in Grafana @@ -279,7 +284,7 @@ func TestMarklogicCluster(t *testing.T) { "queries": []map[string]interface{}{ { "refId": "A", - "expr": "{job=\"fluent-bit\"} |= ``", + "expr": "{job=\"fluent-bit\"} |= `Starting MarkLogic Server`", "queryType": "range", "datasource": map[string]string{ "type": "loki", @@ -293,7 +298,7 @@ func TestMarklogicCluster(t *testing.T) { "maxDataPoints": 1073, }, }, - "from": "now-5m", + "from": "now-1h", "to": "now", } From 352f3d9efd6f46d6be484041aae4c0403cf4c3f3 Mon Sep 17 00:00:00 2001 From: Peng Zhou Date: Wed, 13 Aug 2025 22:31:55 -0700 Subject: [PATCH 9/9] add retry for the Loki Grafana query --- test/e2e/2_marklogic_cluster_test.go | 30 ++++++++++++++++++++-------- 1 file changed, 22 insertions(+), 8 deletions(-) diff --git a/test/e2e/2_marklogic_cluster_test.go b/test/e2e/2_marklogic_cluster_test.go index c1bace1..f05c448 100644 --- a/test/e2e/2_marklogic_cluster_test.go +++ b/test/e2e/2_marklogic_cluster_test.go @@ -308,14 +308,28 @@ func TestMarklogicCluster(t *testing.T) { } queryUrl := fmt.Sprintf("%s/api/ds/query?ds_type=loki", grafanaURL) curlCommand = fmt.Sprintf(`curl -X POST %s -u %s:%s -H "Content-Type: application/json" -d '%s'`, queryUrl, grafanaAdminUser, grafanaAdminPassword, payloadBytes) - output, err = utils.ExecCmdInPod(grafanaPodName, "grafana", "grafana", curlCommand) - if err != nil { - t.Fatalf("Failed to execute kubectl command in grafana pod: %v", err) - } - t.Logf("Query datasource response: %s", output) - // Verify MarkLogic logs in Grafana using Loki and Fluent Bit - if !(strings.Contains(string(output), "Starting MarkLogic Server")) { - t.Fatal("Failed to Query datasource") + maxRetries := 5 + for attempt := 1; attempt <= maxRetries; attempt++ { + t.Logf("Attempt %d to query datasource", attempt) + output, err = utils.ExecCmdInPod(grafanaPodName, "grafana", "grafana", curlCommand) + if err != nil { + t.Logf("Attempt %d/%d Failed to execute kubectl command in grafana pod: %v", attempt, 5, err) + if attempt == maxRetries { + t.Fatalf("failed to execute kubectl command after %d attempts: %v", maxRetries, err) + } + // Exponential backoff: 1s, 2s, 4s, 8s, 16s + time.Sleep(time.Duration(1<<(attempt-1)) * time.Second) + } + t.Logf("Query datasource response: %s", output) + // Verify MarkLogic logs in Grafana using Loki and Fluent Bit + if strings.Contains(string(output), "Starting MarkLogic Server") { + t.Logf("Successfully found MarkLogic logs on attempt %d", attempt) + } else if attempt == maxRetries { + t.Fatalf("Failed to find MarkLogic logs in Grafana after %d attempts", maxRetries) + } else { + t.Logf("MarkLogic logs not found, retrying...") + time.Sleep(time.Duration(1<<(attempt-1)) * time.Second) // Exponential backoff + } } curlCommand = fmt.Sprintf(`curl -u %s:%s %s/api/dashboards/uid/%s`, grafanaAdminUser, grafanaAdminPassword, grafanaURL, dashboardUID)