diff --git a/operators/multiclusterobservability/api/v1beta2/multiclusterobservability_types.go b/operators/multiclusterobservability/api/v1beta2/multiclusterobservability_types.go index 5d074e09b..ad679a15d 100644 --- a/operators/multiclusterobservability/api/v1beta2/multiclusterobservability_types.go +++ b/operators/multiclusterobservability/api/v1beta2/multiclusterobservability_types.go @@ -68,6 +68,16 @@ type PlatformLogsCollectionSpec struct { Enabled bool `json:"enabled,omitempty"` } +// PlatformMetricsCollectionSpec defines the spec for the addon to collect and forward metrics +// from fleet managed clusters. +type PlatformMetricsCollectionSpec struct { + // Enabled defines a flag to enable/disable the platform metrics collection. + // + // +optional + // +kubebuilder:validation:Optional + Enabled bool `json:"enabled,omitempty"` +} + // PlatformLogsSpec defines the spec for the addon to collect, forward and store logs // from fleet managed clusters. type PlatformLogsSpec struct { @@ -79,6 +89,17 @@ type PlatformLogsSpec struct { Collection PlatformLogsCollectionSpec `json:"collection,omitempty"` } +// PlatformMetricsSpec defines the spec for the addon to collect, forward and store metrics +// from fleet managed clusters. +type PlatformMetricsSpec struct { + // Collection defines the spec for the addon to collect and forward metrics + // from fleet managed clusters. + // + // +optional + // +kubebuilder:validation:Optional + Collection PlatformMetricsCollectionSpec `json:"collection,omitempty"` +} + // PlatformCapabilitiesSpec defines the observability capabilities managed by the addon // for platform components. type PlatformCapabilitiesSpec struct { @@ -88,6 +109,13 @@ type PlatformCapabilitiesSpec struct { // +optional // +kubebuilder:validation:Optional Logs PlatformLogsSpec `json:"logs,omitempty"` + + // Metrics defines the configuration spec for collecting and storing metrics from + // platform components running on fleet managed clusters. + // + // +optional + // +kubebuilder:validation:Optional + Metrics PlatformMetricsSpec `json:"metrics,omitempty"` } // ClusterLogForwarderSpec defines the spec for the addon to collect and forward logs @@ -110,6 +138,16 @@ type UserWorkloadLogsCollectionSpec struct { ClusterLogForwarder ClusterLogForwarderSpec `json:"clusterLogForwarder,omitempty"` } +// UserWorkloadLogsSpec defines the spec for the addon to collect,forward and store logs +// from user workloads hosted on fleet managed clusters. +type UserWorkloadMetricsCollectionSpec struct { + // Enabled defines a flag to enable/disable the platform metrics collection. + // + // +optional + // +kubebuilder:validation:Optional + Enabled bool `json:"enabled,omitempty"` +} + // UserWorkloadLogsSpec defines the spec for the addon to collect,forward and store logs // from user workloads hosted on fleet managed clusters. type UserWorkloadLogsSpec struct { @@ -121,6 +159,17 @@ type UserWorkloadLogsSpec struct { Collection UserWorkloadLogsCollectionSpec `json:"collection,omitempty"` } +// UserWorkloadMetricsSpec defines the spec for the addon to collect, forward and store metrics +// from user workloads hosted on fleet managed clusters. +type UserWorkloadMetricsSpec struct { + // Collection defines the spec for the addon to collect and forward metrics + // from user workloads hosted on fleet managed clusters. + // + // +optional + // +kubebuilder:validation:Optional + Collection UserWorkloadMetricsCollectionSpec `json:"collection,omitempty"` +} + // OpenTelemetryCollectorSpec defines the spec for the addon to collect and forward observability signals // using the OpenTelemetryCollector custom resource. type OpenTelemetryCollectorSpec struct { @@ -176,6 +225,14 @@ type UserWorkloadCapabilitiesSpec struct { // +optional // +kubebuilder:validation:Optional Logs UserWorkloadLogsSpec `json:"logs,omitempty"` + + // Metrics defines the spec for the addon to collect, forward and store metrics + // from user workloads hosted on fleet managed clusters. + // + // +optional + // +kubebuilder:validation:Optional + Metrics UserWorkloadMetricsSpec `json:"metrics,omitempty"` + // Traces defines the spec for the addon to collect, forward and store traces // from user workloads hosted on fleet managed clusters. // diff --git a/operators/multiclusterobservability/bundle/manifests/multicluster-observability-operator.clusterserviceversion.yaml b/operators/multiclusterobservability/bundle/manifests/multicluster-observability-operator.clusterserviceversion.yaml index 7a3c5347c..42836354a 100644 --- a/operators/multiclusterobservability/bundle/manifests/multicluster-observability-operator.clusterserviceversion.yaml +++ b/operators/multiclusterobservability/bundle/manifests/multicluster-observability-operator.clusterserviceversion.yaml @@ -49,7 +49,7 @@ metadata: } ] capabilities: Basic Install - createdAt: "2024-09-23T12:07:01Z" + createdAt: "2024-11-06T16:52:14Z" operators.operatorframework.io/builder: operator-sdk-v1.34.2 operators.operatorframework.io/project_layout: go.kubebuilder.io/v3 name: multicluster-observability-operator.v0.1.0 diff --git a/operators/multiclusterobservability/bundle/manifests/observability.open-cluster-management.io_multiclusterobservabilities.yaml b/operators/multiclusterobservability/bundle/manifests/observability.open-cluster-management.io_multiclusterobservabilities.yaml index 3e0adaed7..7bc25232a 100644 --- a/operators/multiclusterobservability/bundle/manifests/observability.open-cluster-management.io_multiclusterobservabilities.yaml +++ b/operators/multiclusterobservability/bundle/manifests/observability.open-cluster-management.io_multiclusterobservabilities.yaml @@ -10036,6 +10036,22 @@ spec: type: boolean type: object type: object + metrics: + description: |- + Metrics defines the configuration spec for collecting and storing metrics from + platform components running on fleet managed clusters. + properties: + collection: + description: |- + Collection defines the spec for the addon to collect and forward metrics + from fleet managed clusters. + properties: + enabled: + description: Enabled defines a flag to enable/disable + the platform metrics collection. + type: boolean + type: object + type: object type: object userWorkloads: description: |- @@ -10066,6 +10082,22 @@ spec: type: object type: object type: object + metrics: + description: |- + Metrics defines the spec for the addon to collect, forward and store metrics + from user workloads hosted on fleet managed clusters. + properties: + collection: + description: |- + Collection defines the spec for the addon to collect and forward metrics + from user workloads hosted on fleet managed clusters. + properties: + enabled: + description: Enabled defines a flag to enable/disable + the platform metrics collection. + type: boolean + type: object + type: object traces: description: |- Traces defines the spec for the addon to collect, forward and store traces diff --git a/operators/multiclusterobservability/config/crd/bases/observability.open-cluster-management.io_multiclusterobservabilities.yaml b/operators/multiclusterobservability/config/crd/bases/observability.open-cluster-management.io_multiclusterobservabilities.yaml index 19b8365dc..9663dffdb 100644 --- a/operators/multiclusterobservability/config/crd/bases/observability.open-cluster-management.io_multiclusterobservabilities.yaml +++ b/operators/multiclusterobservability/config/crd/bases/observability.open-cluster-management.io_multiclusterobservabilities.yaml @@ -10020,6 +10020,22 @@ spec: type: boolean type: object type: object + metrics: + description: |- + Metrics defines the configuration spec for collecting and storing metrics from + platform components running on fleet managed clusters. + properties: + collection: + description: |- + Collection defines the spec for the addon to collect and forward metrics + from fleet managed clusters. + properties: + enabled: + description: Enabled defines a flag to enable/disable + the platform metrics collection. + type: boolean + type: object + type: object type: object userWorkloads: description: |- @@ -10050,6 +10066,22 @@ spec: type: object type: object type: object + metrics: + description: |- + Metrics defines the spec for the addon to collect, forward and store metrics + from user workloads hosted on fleet managed clusters. + properties: + collection: + description: |- + Collection defines the spec for the addon to collect and forward metrics + from user workloads hosted on fleet managed clusters. + properties: + enabled: + description: Enabled defines a flag to enable/disable + the platform metrics collection. + type: boolean + type: object + type: object traces: description: |- Traces defines the spec for the addon to collect, forward and store traces diff --git a/operators/multiclusterobservability/manifests/base/multicluster-observability-addon/cluster_management_addon.yaml b/operators/multiclusterobservability/manifests/base/multicluster-observability-addon/cluster_management_addon.yaml index 19b2dfce6..22ac71c0d 100644 --- a/operators/multiclusterobservability/manifests/base/multicluster-observability-addon/cluster_management_addon.yaml +++ b/operators/multiclusterobservability/manifests/base/multicluster-observability-addon/cluster_management_addon.yaml @@ -29,21 +29,16 @@ spec: # Describes the default Instrumentation type applied to all managed clusters. - group: opentelemetry.io resource: instrumentations + # Metrics forwarding configuration types. + - group: monitoring.coreos.com + resource: prometheusagents + - group: monitoring.coreos.com + resource: scrapeconfigs + - group: monitoring.coreos.com + resource: prometheusrules installStrategy: type: Placements placements: - name: global namespace: open-cluster-management-global-set - configs: - - group: observability.openshift.io - resource: clusterlogforwarders - name: instance - namespace: open-cluster-management-observability - - group: opentelemetry.io - resource: opentelemetrycollectors - name: instance - namespace: open-cluster-management-observability - - group: opentelemetry.io - resource: instrumentations - name: instance - namespace: open-cluster-management-observability + configs: [] diff --git a/operators/multiclusterobservability/manifests/base/multicluster-observability-addon/cluster_role.yaml b/operators/multiclusterobservability/manifests/base/multicluster-observability-addon/cluster_role.yaml index 7f8e3e900..183aad27d 100644 --- a/operators/multiclusterobservability/manifests/base/multicluster-observability-addon/cluster_role.yaml +++ b/operators/multiclusterobservability/manifests/base/multicluster-observability-addon/cluster_role.yaml @@ -68,3 +68,7 @@ - apiGroups: ["opentelemetry.io"] resources: ["opentelemetrycollectors", "instrumentations"] verbs: ["get", "list", "watch"] + # Role for addon to perform prometheus specific actions + - apiGroups: ["monitoring.coreos.com"] + resources: ["prometheusagents", "scrapeconfigs", "prometheusrules"] + verbs: ["get", "list", "watch", "create", "update", "delete"] diff --git a/operators/multiclusterobservability/pkg/config/config.go b/operators/multiclusterobservability/pkg/config/config.go index 5c8443544..c663b857d 100644 --- a/operators/multiclusterobservability/pkg/config/config.go +++ b/operators/multiclusterobservability/pkg/config/config.go @@ -229,6 +229,7 @@ const ( ClusterLogForwarderCRDName = "clusterlogforwarders.observability.openshift.io" OpenTelemetryCollectorCRDName = "opentelemetrycollectors.opentelemetry.io" InstrumentationCRDName = "instrumentations.opentelemetry.io" + PrometheusAgentCRDName = "prometheusagents.monitoring.coreos.com" ) var ( @@ -236,6 +237,7 @@ var ( ClusterLogForwarderCRDName: "v1", OpenTelemetryCollectorCRDName: "v1beta1", InstrumentationCRDName: "v1alpha1", + PrometheusAgentCRDName: "v1alpha1", } ) diff --git a/operators/multiclusterobservability/pkg/config/config_test.go b/operators/multiclusterobservability/pkg/config/config_test.go index 9952b5694..2efe5e25e 100644 --- a/operators/multiclusterobservability/pkg/config/config_test.go +++ b/operators/multiclusterobservability/pkg/config/config_test.go @@ -809,6 +809,7 @@ func TestGetMCOASupportedCRDNames(t *testing.T) { "clusterlogforwarders.observability.openshift.io", "opentelemetrycollectors.opentelemetry.io", "instrumentations.opentelemetry.io", + "prometheusagents.monitoring.coreos.com", } result := GetMCOASupportedCRDNames() diff --git a/operators/multiclusterobservability/pkg/rendering/renderer_mcoa.go b/operators/multiclusterobservability/pkg/rendering/renderer_mcoa.go index 165dac218..72c0449e1 100644 --- a/operators/multiclusterobservability/pkg/rendering/renderer_mcoa.go +++ b/operators/multiclusterobservability/pkg/rendering/renderer_mcoa.go @@ -5,8 +5,11 @@ package rendering import ( + "context" "fmt" + prometheusv1 "github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring/v1" + prometheusalpha1 "github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring/v1alpha1" appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -25,10 +28,13 @@ const ( cmaoKind = "ClusterManagementAddOn" // AODC CustomizedVariable Names - namePlatformLogsCollection = "platformLogsCollection" - nameUserWorkloadLogsCollection = "userWorkloadLogsCollection" - nameUserWorkloadTracesCollection = "userWorkloadTracesCollection" - nameUserWorkloadInstrumentation = "userWorkloadInstrumentation" + namePlatformLogsCollection = "platformLogsCollection" + namePlatformMetricsCollection = "platformMetricsCollection" + nameUserWorkloadLogsCollection = "userWorkloadLogsCollection" + nameUserWorkloadTracesCollection = "userWorkloadTracesCollection" + nameUserWorkloadInstrumentation = "userWorkloadInstrumentation" + nameUserWorkloadMetricsCollection = "userWorkloadMetricsCollection" + nameSignalsHubEndpoint = "signalsHubEndpoint" ) type MCOARendererOptions struct { @@ -164,6 +170,121 @@ func (r *MCORenderer) renderClusterManagementAddOn( } u.SetLabels(cLabels) + cma := &addonapiv1alpha1.ClusterManagementAddOn{} + if err := runtime.DefaultUnstructuredConverter.FromUnstructured(u.Object, cma); err != nil { + return nil, err + } + + if r.cr.Spec.Capabilities != nil { + if len(cma.Spec.InstallStrategy.Placements) != 1 { + return nil, fmt.Errorf("expected exactly one placement, got %d", len(cma.Spec.InstallStrategy.Placements)) + } + globalConfigs := []addonapiv1alpha1.AddOnConfig{} + if r.cr.Spec.Capabilities.Platform != nil && r.cr.Spec.Capabilities.Platform.Metrics.Collection.Enabled { + globalConfigs = append(globalConfigs, []addonapiv1alpha1.AddOnConfig{ + { + ConfigGroupResource: addonapiv1alpha1.ConfigGroupResource{ + Group: prometheusalpha1.SchemeGroupVersion.Group, + Resource: prometheusalpha1.PrometheusAgentName, + }, + ConfigReferent: addonapiv1alpha1.ConfigReferent{ + Name: "acm-platform-metrics-collector-default", + Namespace: mcoconfig.GetDefaultNamespace(), + }, + }, + { + ConfigGroupResource: addonapiv1alpha1.ConfigGroupResource{ + Group: prometheusalpha1.SchemeGroupVersion.Group, + Resource: prometheusalpha1.ScrapeConfigName, + }, + ConfigReferent: addonapiv1alpha1.ConfigReferent{ + Name: "platform-metrics-default", + Namespace: mcoconfig.GetDefaultNamespace(), + }, + }, + { + ConfigGroupResource: addonapiv1alpha1.ConfigGroupResource{ + Group: prometheusv1.SchemeGroupVersion.Group, + Resource: prometheusv1.PrometheusRuleName, + }, + ConfigReferent: addonapiv1alpha1.ConfigReferent{ + Name: "platform-rules-default", + Namespace: mcoconfig.GetDefaultNamespace(), + }, + }, + }...) + } + + if r.cr.Spec.Capabilities.UserWorkloads != nil && r.cr.Spec.Capabilities.UserWorkloads.Metrics.Collection.Enabled { + globalConfigs = append(globalConfigs, []addonapiv1alpha1.AddOnConfig{ + { + ConfigGroupResource: addonapiv1alpha1.ConfigGroupResource{ + Group: prometheusalpha1.SchemeGroupVersion.Group, + Resource: prometheusalpha1.PrometheusAgentName, + }, + ConfigReferent: addonapiv1alpha1.ConfigReferent{ + Name: "acm-user-workload-metrics-collector-default", + Namespace: mcoconfig.GetDefaultNamespace(), + }, + }, + }...) + } + + if r.cr.Spec.Capabilities.Platform != nil && r.cr.Spec.Capabilities.Platform.Logs.Collection.Enabled { + globalConfigs = append(globalConfigs, []addonapiv1alpha1.AddOnConfig{ + { + ConfigGroupResource: addonapiv1alpha1.ConfigGroupResource{ + Group: "observability.openshift.io", + Resource: "clusterlogforwarders", + }, + ConfigReferent: addonapiv1alpha1.ConfigReferent{ + Name: "instance", + Namespace: mcoconfig.GetDefaultNamespace(), + }, + }, + }...) + } + + if r.cr.Spec.Capabilities.UserWorkloads != nil { + if r.cr.Spec.Capabilities.UserWorkloads.Traces.Collection.Collector.Enabled { + globalConfigs = append(globalConfigs, []addonapiv1alpha1.AddOnConfig{ + { + ConfigGroupResource: addonapiv1alpha1.ConfigGroupResource{ + Group: "opentelemetry.io", + Resource: "opentelemetrycollectors", + }, + ConfigReferent: addonapiv1alpha1.ConfigReferent{ + Name: "instance", + Namespace: mcoconfig.GetDefaultNamespace(), + }, + }, + }...) + } + + if r.cr.Spec.Capabilities.UserWorkloads.Traces.Collection.Instrumentation.Enabled { + globalConfigs = append(globalConfigs, []addonapiv1alpha1.AddOnConfig{ + { + ConfigGroupResource: addonapiv1alpha1.ConfigGroupResource{ + Group: "opentelemetry.io", + Resource: "instrumentations", + }, + ConfigReferent: addonapiv1alpha1.ConfigReferent{ + Name: "instance", + Namespace: mcoconfig.GetDefaultNamespace(), + }, + }, + }...) + } + } + + cma.Spec.InstallStrategy.Placements[0].Configs = append(cma.Spec.InstallStrategy.Placements[0].Configs, globalConfigs...) + } + + u.Object, err = runtime.DefaultUnstructuredConverter.ToUnstructured(cma) + if err != nil { + return nil, err + } + return u, nil } @@ -195,6 +316,10 @@ func (r *MCORenderer) renderAddonDeploymentConfig( fqdn := mcoconfig.GetMCOASupportedCRDFQDN(mcoconfig.ClusterLogForwarderCRDName) appendCustomVar(aodc, namePlatformLogsCollection, fqdn) } + if cs.Platform.Metrics.Collection.Enabled { + fqdn := mcoconfig.GetMCOASupportedCRDFQDN(mcoconfig.PrometheusAgentCRDName) + appendCustomVar(aodc, namePlatformMetricsCollection, fqdn) + } } if cs.UserWorkloads != nil { @@ -202,6 +327,10 @@ func (r *MCORenderer) renderAddonDeploymentConfig( fqdn := mcoconfig.GetMCOASupportedCRDFQDN(mcoconfig.ClusterLogForwarderCRDName) appendCustomVar(aodc, nameUserWorkloadLogsCollection, fqdn) } + if cs.UserWorkloads.Metrics.Collection.Enabled { + fqdn := mcoconfig.GetMCOASupportedCRDFQDN(mcoconfig.PrometheusAgentCRDName) + appendCustomVar(aodc, nameUserWorkloadMetricsCollection, fqdn) + } if cs.UserWorkloads.Traces.Collection.Collector.Enabled { fqdn := mcoconfig.GetMCOASupportedCRDFQDN(mcoconfig.OpenTelemetryCollectorCRDName) appendCustomVar(aodc, nameUserWorkloadTracesCollection, fqdn) @@ -212,6 +341,14 @@ func (r *MCORenderer) renderAddonDeploymentConfig( } } + if (cs.Platform != nil && cs.Platform.Metrics.Collection.Enabled) || (cs.UserWorkloads != nil && cs.UserWorkloads.Metrics.Collection.Enabled) { + obsAPIURL, err := mcoconfig.GetObsAPIExternalURL(context.TODO(), r.kubeClient, namespace) + if err != nil { + return nil, fmt.Errorf("failed to get the Observatorium API URL: %w", err) + } + appendCustomVar(aodc, nameSignalsHubEndpoint, obsAPIURL.Host) + } + u.Object, err = runtime.DefaultUnstructuredConverter.ToUnstructured(aodc) if err != nil { return nil, err @@ -275,12 +412,13 @@ func MCOAEnabled(cr *obv1beta2.MultiClusterObservability) bool { } mcoaEnabled := false if cr.Spec.Capabilities.Platform != nil { - mcoaEnabled = mcoaEnabled || cr.Spec.Capabilities.Platform.Logs.Collection.Enabled + mcoaEnabled = mcoaEnabled || cr.Spec.Capabilities.Platform.Logs.Collection.Enabled || cr.Spec.Capabilities.Platform.Metrics.Collection.Enabled } if cr.Spec.Capabilities.UserWorkloads != nil { mcoaEnabled = mcoaEnabled || cr.Spec.Capabilities.UserWorkloads.Logs.Collection.ClusterLogForwarder.Enabled mcoaEnabled = mcoaEnabled || cr.Spec.Capabilities.UserWorkloads.Traces.Collection.Collector.Enabled mcoaEnabled = mcoaEnabled || cr.Spec.Capabilities.UserWorkloads.Traces.Collection.Instrumentation.Enabled + mcoaEnabled = mcoaEnabled || cr.Spec.Capabilities.UserWorkloads.Metrics.Collection.Enabled } return mcoaEnabled } diff --git a/operators/multiclusterobservability/pkg/rendering/renderer_mcoa_test.go b/operators/multiclusterobservability/pkg/rendering/renderer_mcoa_test.go index 720f314e3..795438f04 100644 --- a/operators/multiclusterobservability/pkg/rendering/renderer_mcoa_test.go +++ b/operators/multiclusterobservability/pkg/rendering/renderer_mcoa_test.go @@ -10,6 +10,7 @@ import ( "testing" mcov1beta2 "github.com/stolostron/multicluster-observability-operator/operators/multiclusterobservability/api/v1beta2" + "github.com/stolostron/multicluster-observability-operator/operators/multiclusterobservability/pkg/config" mcoconfig "github.com/stolostron/multicluster-observability-operator/operators/multiclusterobservability/pkg/config" "github.com/stolostron/multicluster-observability-operator/operators/multiclusterobservability/pkg/rendering/templates" templatesutil "github.com/stolostron/multicluster-observability-operator/operators/pkg/rendering/templates" @@ -20,7 +21,9 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/utils/ptr" + addonapiv1alpha1 "open-cluster-management.io/api/addon/v1alpha1" addonv1alpha1 "open-cluster-management.io/api/addon/v1alpha1" + "sigs.k8s.io/controller-runtime/pkg/client/fake" kustomizeres "sigs.k8s.io/kustomize/api/resource" ) @@ -133,6 +136,11 @@ func TestRenderAddonDeploymentConfig(t *testing.T) { Enabled: true, }, }, + Metrics: mcov1beta2.PlatformMetricsSpec{ + Collection: mcov1beta2.PlatformMetricsCollectionSpec{ + Enabled: true, + }, + }, }, UserWorkloads: &mcov1beta2.UserWorkloadCapabilitiesSpec{ Logs: mcov1beta2.UserWorkloadLogsSpec{ @@ -142,6 +150,11 @@ func TestRenderAddonDeploymentConfig(t *testing.T) { }, }, }, + Metrics: mcov1beta2.UserWorkloadMetricsSpec{ + Collection: mcov1beta2.UserWorkloadMetricsCollectionSpec{ + Enabled: true, + }, + }, Traces: mcov1beta2.UserWorkloadTracesSpec{ Collection: mcov1beta2.OpenTelemetryCollectionSpec{ Collector: mcov1beta2.OpenTelemetryCollectorSpec{ @@ -154,9 +167,21 @@ func TestRenderAddonDeploymentConfig(t *testing.T) { }, }, }, + AdvancedConfig: &mcov1beta2.AdvancedConfig{ + CustomObservabilityHubURL: "https://observability-hub", + }, }, } - renderer := &MCORenderer{cr: mco} + + config.SetMonitoringCRName("multicluster-observability") + scheme := runtime.NewScheme() + assert.NoError(t, mcov1beta2.AddToScheme(scheme)) + fakeClient := fake.NewClientBuilder(). + WithScheme(scheme). + WithObjects(mco). + Build() + + renderer := &MCORenderer{cr: mco, kubeClient: fakeClient} uobj, err := renderer.renderAddonDeploymentConfig(aodc, "test", map[string]string{"key": "value"}) assert.NoError(t, err) @@ -169,12 +194,214 @@ func TestRenderAddonDeploymentConfig(t *testing.T) { clfV1 := mcoconfig.GetMCOASupportedCRDFQDN(mcoconfig.ClusterLogForwarderCRDName) otelV1beta1 := mcoconfig.GetMCOASupportedCRDFQDN(mcoconfig.OpenTelemetryCollectorCRDName) instrV1alpha1 := mcoconfig.GetMCOASupportedCRDFQDN(mcoconfig.InstrumentationCRDName) + promV1alpha1 := mcoconfig.GetMCOASupportedCRDFQDN(mcoconfig.PrometheusAgentCRDName) - assert.Len(t, got.Spec.CustomizedVariables, 5) + assert.Len(t, got.Spec.CustomizedVariables, 8) assert.Contains(t, got.Spec.CustomizedVariables, addonv1alpha1.CustomizedVariable{Name: namePlatformLogsCollection, Value: clfV1}) assert.Contains(t, got.Spec.CustomizedVariables, addonv1alpha1.CustomizedVariable{Name: nameUserWorkloadLogsCollection, Value: clfV1}) assert.Contains(t, got.Spec.CustomizedVariables, addonv1alpha1.CustomizedVariable{Name: nameUserWorkloadTracesCollection, Value: otelV1beta1}) assert.Contains(t, got.Spec.CustomizedVariables, addonv1alpha1.CustomizedVariable{Name: nameUserWorkloadInstrumentation, Value: instrV1alpha1}) + assert.Contains(t, got.Spec.CustomizedVariables, addonv1alpha1.CustomizedVariable{Name: namePlatformMetricsCollection, Value: promV1alpha1}) + assert.Contains(t, got.Spec.CustomizedVariables, addonv1alpha1.CustomizedVariable{Name: nameUserWorkloadMetricsCollection, Value: promV1alpha1}) + assert.Contains(t, got.Spec.CustomizedVariables, addonv1alpha1.CustomizedVariable{Name: nameSignalsHubEndpoint, Value: "observability-hub"}) +} + +func TestMCORenderer_RenderClusterManagementAddOn(t *testing.T) { + tests := []struct { + name string + labels map[string]string + capabilities *mcov1beta2.CapabilitiesSpec + expectConfig func(*testing.T, *addonv1alpha1.ClusterManagementAddOn) + }{ + { + name: "add metrics configs when platform is enabled", + capabilities: &mcov1beta2.CapabilitiesSpec{ + Platform: &mcov1beta2.PlatformCapabilitiesSpec{ + Metrics: mcov1beta2.PlatformMetricsSpec{ + Collection: mcov1beta2.PlatformMetricsCollectionSpec{ + Enabled: true, + }, + }, + }, + }, + expectConfig: func(t *testing.T, cma *addonv1alpha1.ClusterManagementAddOn) { + assert.Len(t, cma.Spec.InstallStrategy.Placements[0].Configs, 3) + }, + }, + { + name: "add metrics configs when user workloads is enabled", + capabilities: &mcov1beta2.CapabilitiesSpec{ + UserWorkloads: &mcov1beta2.UserWorkloadCapabilitiesSpec{ + Metrics: mcov1beta2.UserWorkloadMetricsSpec{ + Collection: mcov1beta2.UserWorkloadMetricsCollectionSpec{ + Enabled: true, + }, + }, + }, + }, + expectConfig: func(t *testing.T, cma *addonv1alpha1.ClusterManagementAddOn) { + assert.Len(t, cma.Spec.InstallStrategy.Placements[0].Configs, 1) + }, + }, + { + name: "add metrics configs when both platform and user workloads are enabled", + capabilities: &mcov1beta2.CapabilitiesSpec{ + Platform: &mcov1beta2.PlatformCapabilitiesSpec{ + Metrics: mcov1beta2.PlatformMetricsSpec{ + Collection: mcov1beta2.PlatformMetricsCollectionSpec{ + Enabled: true, + }, + }, + }, + UserWorkloads: &mcov1beta2.UserWorkloadCapabilitiesSpec{ + Metrics: mcov1beta2.UserWorkloadMetricsSpec{ + Collection: mcov1beta2.UserWorkloadMetricsCollectionSpec{ + Enabled: true, + }, + }, + }, + }, + expectConfig: func(t *testing.T, cma *addonv1alpha1.ClusterManagementAddOn) { + assert.Len(t, cma.Spec.InstallStrategy.Placements[0].Configs, 4) + }, + }, + { + name: "add logs configs when platform is enabled", + capabilities: &mcov1beta2.CapabilitiesSpec{ + Platform: &mcov1beta2.PlatformCapabilitiesSpec{ + Logs: mcov1beta2.PlatformLogsSpec{ + Collection: mcov1beta2.PlatformLogsCollectionSpec{ + Enabled: true, + }, + }, + }, + }, + expectConfig: func(t *testing.T, cma *addonv1alpha1.ClusterManagementAddOn) { + assert.Len(t, cma.Spec.InstallStrategy.Placements[0].Configs, 1) + }, + }, + { + name: "add logs configs when user workloads is enabled", + capabilities: &mcov1beta2.CapabilitiesSpec{ + UserWorkloads: &mcov1beta2.UserWorkloadCapabilitiesSpec{ + Logs: mcov1beta2.UserWorkloadLogsSpec{ + Collection: mcov1beta2.UserWorkloadLogsCollectionSpec{ + ClusterLogForwarder: mcov1beta2.ClusterLogForwarderSpec{ + Enabled: true, + }, + }, + }, + }, + }, + expectConfig: func(t *testing.T, cma *addonv1alpha1.ClusterManagementAddOn) { + assert.Len(t, cma.Spec.InstallStrategy.Placements[0].Configs, 0) + }, + }, + { + name: "add logs configs when both platform and user workloads are enabled", + capabilities: &mcov1beta2.CapabilitiesSpec{ + Platform: &mcov1beta2.PlatformCapabilitiesSpec{ + Logs: mcov1beta2.PlatformLogsSpec{ + Collection: mcov1beta2.PlatformLogsCollectionSpec{ + Enabled: true, + }, + }, + }, + UserWorkloads: &mcov1beta2.UserWorkloadCapabilitiesSpec{ + Logs: mcov1beta2.UserWorkloadLogsSpec{ + Collection: mcov1beta2.UserWorkloadLogsCollectionSpec{ + ClusterLogForwarder: mcov1beta2.ClusterLogForwarderSpec{ + Enabled: true, + }, + }, + }, + }, + }, + expectConfig: func(t *testing.T, cma *addonv1alpha1.ClusterManagementAddOn) { + assert.Len(t, cma.Spec.InstallStrategy.Placements[0].Configs, 1) + }, + }, + { + name: "add traces configs when user workloads is enabled", + capabilities: &mcov1beta2.CapabilitiesSpec{ + UserWorkloads: &mcov1beta2.UserWorkloadCapabilitiesSpec{ + Traces: mcov1beta2.UserWorkloadTracesSpec{ + Collection: mcov1beta2.OpenTelemetryCollectionSpec{ + Collector: mcov1beta2.OpenTelemetryCollectorSpec{ + Enabled: true, + }, + Instrumentation: mcov1beta2.InstrumentationSpec{ + Enabled: true, + }, + }, + }, + }, + }, + expectConfig: func(t *testing.T, cma *addonv1alpha1.ClusterManagementAddOn) { + assert.Len(t, cma.Spec.InstallStrategy.Placements[0].Configs, 2) + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + r := &MCORenderer{ + cr: &mcov1beta2.MultiClusterObservability{ + Spec: mcov1beta2.MultiClusterObservabilitySpec{ + Capabilities: tt.capabilities, + }, + }, + } + + // Add deployment config to reflect real input + baseCMA := &addonv1alpha1.ClusterManagementAddOn{ + ObjectMeta: metav1.ObjectMeta{ + Labels: map[string]string{ + "k1": "v1", + }, + }, + Spec: addonv1alpha1.ClusterManagementAddOnSpec{ + InstallStrategy: addonv1alpha1.InstallStrategy{ + Placements: []addonv1alpha1.PlacementStrategy{ + { + Configs: []addonv1alpha1.AddOnConfig{}, + }, + }, + }, + }, + } + // to kustomize resource + baseCMAUnstructured, err := runtime.DefaultUnstructuredConverter.ToUnstructured(baseCMA) + assert.NoError(t, err) + kres := &kustomizeres.Resource{} + err = runtime.DefaultUnstructuredConverter.FromUnstructured(baseCMAUnstructured, kres) + assert.NoError(t, err) + + res, err := r.renderClusterManagementAddOn(kres, "ns", map[string]string{"k2": "v2"}) + assert.NoError(t, err) + assert.NotNil(t, res) + + // check labels + assert.Len(t, res.GetLabels(), 2) + + // check supportedConfigs + cma := &addonapiv1alpha1.ClusterManagementAddOn{} + err = runtime.DefaultUnstructuredConverter.FromUnstructured(res.Object, cma) + assert.NoError(t, err) + + tt.expectConfig(t, cma) + + // Check duplicated supportedConfigs + dups := make(map[string]struct{}) + for _, cfg := range cma.Spec.SupportedConfigs { + key := cfg.ConfigGroupResource.Group + "/" + cfg.ConfigGroupResource.Resource + if _, ok := dups[key]; ok { + t.Errorf("duplicated supportedConfigs %s", key) + } + dups[key] = struct{}{} + } + }) + } } func TestMCOAEnabled(t *testing.T) { @@ -228,6 +455,40 @@ func TestMCOAEnabled(t *testing.T) { }, expected: true, }, + { + name: "Platform metrics collection enabled", + cr: &mcov1beta2.MultiClusterObservability{ + Spec: mcov1beta2.MultiClusterObservabilitySpec{ + Capabilities: &mcov1beta2.CapabilitiesSpec{ + Platform: &mcov1beta2.PlatformCapabilitiesSpec{ + Metrics: mcov1beta2.PlatformMetricsSpec{ + Collection: mcov1beta2.PlatformMetricsCollectionSpec{ + Enabled: true, + }, + }, + }, + }, + }, + }, + expected: true, + }, + { + name: "User workloads metrics collection enabled", + cr: &mcov1beta2.MultiClusterObservability{ + Spec: mcov1beta2.MultiClusterObservabilitySpec{ + Capabilities: &mcov1beta2.CapabilitiesSpec{ + UserWorkloads: &mcov1beta2.UserWorkloadCapabilitiesSpec{ + Metrics: mcov1beta2.UserWorkloadMetricsSpec{ + Collection: mcov1beta2.UserWorkloadMetricsCollectionSpec{ + Enabled: true, + }, + }, + }, + }, + }, + }, + expected: true, + }, { name: "User workloads traces collection enabled", cr: &mcov1beta2.MultiClusterObservability{ @@ -261,6 +522,11 @@ func TestMCOAEnabled(t *testing.T) { Enabled: false, }, }, + Metrics: mcov1beta2.PlatformMetricsSpec{ + Collection: mcov1beta2.PlatformMetricsCollectionSpec{ + Enabled: false, + }, + }, }, UserWorkloads: &mcov1beta2.UserWorkloadCapabilitiesSpec{ Logs: mcov1beta2.UserWorkloadLogsSpec{ @@ -270,6 +536,11 @@ func TestMCOAEnabled(t *testing.T) { }, }, }, + Metrics: mcov1beta2.UserWorkloadMetricsSpec{ + Collection: mcov1beta2.UserWorkloadMetricsCollectionSpec{ + Enabled: false, + }, + }, Traces: mcov1beta2.UserWorkloadTracesSpec{ Collection: mcov1beta2.OpenTelemetryCollectionSpec{ Collector: mcov1beta2.OpenTelemetryCollectorSpec{