From b5f51e3e511b2f9bcb447402e03f6c253493914f Mon Sep 17 00:00:00 2001 From: Andreas Gerstmayr Date: Tue, 13 Feb 2024 13:17:28 +0100 Subject: [PATCH] Support observability in TempoMonolithic CR (#793) Support creating ServiceMonitors, PrometheusRules and Grafana Data Sources in TempoMonolithic CR Signed-off-by: Andreas Gerstmayr --- .chloggen/monolithic_observability.yaml | 16 + apis/tempo/v1alpha1/tempomonolithic_types.go | 78 ++++ apis/tempo/v1alpha1/zz_generated.deepcopy.go | 125 +++++ .../tempo-operator.clusterserviceversion.yaml | 43 +- .../tempo.grafana.com_tempomonolithics.yaml | 92 ++++ .../tempo-operator.clusterserviceversion.yaml | 43 +- .../tempo.grafana.com_tempomonolithics.yaml | 92 ++++ .../tempo.grafana.com_tempomonolithics.yaml | 92 ++++ .../tempo-operator.clusterserviceversion.yaml | 41 ++ .../tempo-operator.clusterserviceversion.yaml | 41 ++ .../tempo/tempomonolithic_controller.go | 9 + controllers/tempo/tempostack_controller.go | 9 +- docs/operator/api.md | 436 ++++++++++++++++++ .../tempo.grafana.com_tempomonolithics.yaml | 17 + internal/manifests/grafana/datasource.go | 52 ++- internal/manifests/grafana/datasource_test.go | 24 +- internal/manifests/manifests.go | 6 +- internal/manifests/monolithic/build.go | 22 + .../monolithic/grafana_datasource.go | 22 + .../monolithic/grafana_datasource_test.go | 59 +++ .../manifests/monolithic/prometheusrule.go | 13 + .../monolithic/prometheusrule_test.go | 31 ++ .../manifests/monolithic/servicemonitor.go | 15 + .../monolithic/servicemonitor_test.go | 61 +++ .../servicemonitor/servicemonitor.go | 32 +- 25 files changed, 1422 insertions(+), 49 deletions(-) create mode 100644 .chloggen/monolithic_observability.yaml create mode 100644 internal/manifests/monolithic/grafana_datasource.go create mode 100644 internal/manifests/monolithic/grafana_datasource_test.go create mode 100644 internal/manifests/monolithic/prometheusrule.go create mode 100644 internal/manifests/monolithic/prometheusrule_test.go create mode 100644 internal/manifests/monolithic/servicemonitor.go create mode 100644 internal/manifests/monolithic/servicemonitor_test.go diff --git a/.chloggen/monolithic_observability.yaml b/.chloggen/monolithic_observability.yaml new file mode 100644 index 000000000..0faea128f --- /dev/null +++ b/.chloggen/monolithic_observability.yaml @@ -0,0 +1,16 @@ +# One of 'breaking', 'deprecation', 'new_component', 'enhancement', 'bug_fix' +change_type: enhancement + +# The name of the component, or a single word describing the area of concern, (e.g. operator, github action) +component: operator + +# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`). +note: Support creating ServiceMonitors, PrometheusRules and Grafana Data Sources in TempoMonolithic CR + +# One or more tracking issues related to the change +issues: [793] + +# (Optional) One or more lines of additional information to render under the primary note. +# These lines will be padded with 2 spaces and then inserted directly into the document. +# Use pipe (|) for multiline entries. +subtext: diff --git a/apis/tempo/v1alpha1/tempomonolithic_types.go b/apis/tempo/v1alpha1/tempomonolithic_types.go index dc0cd34bf..1995e7d43 100644 --- a/apis/tempo/v1alpha1/tempomonolithic_types.go +++ b/apis/tempo/v1alpha1/tempomonolithic_types.go @@ -26,6 +26,12 @@ type TempoMonolithicSpec struct { // +operator-sdk:csv:customresourcedefinitions:type=spec,displayName="Jaeger UI" JaegerUI *MonolithicJaegerUISpec `json:"jaegerui,omitempty"` + // Observability defines the observability configuration of the Tempo deployment. + // + // +kubebuilder:validation:Optional + // +operator-sdk:csv:customresourcedefinitions:type=spec,displayName="Observability" + Observability *MonolithicObservabilitySpec `json:"observability,omitempty"` + // ManagementState defines whether this instance is managed by the operator or self-managed. // Default: Managed. // @@ -277,6 +283,78 @@ type MonolithicJaegerUIRouteSpec struct { Termination TLSRouteTerminationType `json:"termination,omitempty"` } +// MonolithicObservabilitySpec defines the observability configuration of the Tempo deployment. +type MonolithicObservabilitySpec struct { + // Metrics defines the metric configuration of the Tempo deployment. + // + // +kubebuilder:validation:Optional + // +operator-sdk:csv:customresourcedefinitions:type=spec,displayName="Metrics" + Metrics *MonolithicObservabilityMetricsSpec `json:"metrics,omitempty"` + + // Grafana defines the Grafana configuration of the Tempo deployment. + // + // +kubebuilder:validation:Optional + // +operator-sdk:csv:customresourcedefinitions:type=spec,displayName="Grafana" + Grafana *MonolithicObservabilityGrafanaSpec `json:"grafana,omitempty"` +} + +// MonolithicObservabilityMetricsSpec defines the metrics settings of the Tempo deployment. +type MonolithicObservabilityMetricsSpec struct { + // ServiceMonitors defines the ServiceMonitor configuration. + // + // +kubebuilder:validation:Optional + // +operator-sdk:csv:customresourcedefinitions:type=spec,displayName="Service Monitors" + ServiceMonitors *MonolithicObservabilityMetricsServiceMonitorsSpec `json:"serviceMonitors,omitempty"` + + // ServiceMonitors defines the PrometheusRule configuration. + // + // +kubebuilder:validation:Optional + // +operator-sdk:csv:customresourcedefinitions:type=spec,displayName="Prometheus Rules" + PrometheusRules *MonolithicObservabilityMetricsPrometheusRulesSpec `json:"prometheusRules,omitempty"` +} + +// MonolithicObservabilityMetricsServiceMonitorsSpec defines the ServiceMonitor settings. +type MonolithicObservabilityMetricsServiceMonitorsSpec struct { + // Enabled defines if ServiceMonitor objects should be created for this Tempo deployment. + // + // +kubebuilder:validation:Required + // +operator-sdk:csv:customresourcedefinitions:type=spec,displayName="Enabled",xDescriptors="urn:alm:descriptor:com.tectonic.ui:booleanSwitch" + Enabled bool `json:"enabled"` +} + +// MonolithicObservabilityMetricsPrometheusRulesSpec defines the PrometheusRules settings. +type MonolithicObservabilityMetricsPrometheusRulesSpec struct { + // Enabled defines if PrometheusRule objects should be created for this Tempo deployment. + // + // +kubebuilder:validation:Required + // +operator-sdk:csv:customresourcedefinitions:type=spec,displayName="Enabled",xDescriptors="urn:alm:descriptor:com.tectonic.ui:booleanSwitch" + Enabled bool `json:"enabled"` +} + +// MonolithicObservabilityGrafanaSpec defines the Grafana configuration of the Tempo deployment. +type MonolithicObservabilityGrafanaSpec struct { + // DataSource defines the Grafana data source configuration. + // + // +kubebuilder:validation:Optional + // +operator-sdk:csv:customresourcedefinitions:type=spec,displayName="Grafana data source" + DataSource *MonolithicObservabilityGrafanaDataSourceSpec `json:"dataSource,omitempty"` +} + +// MonolithicObservabilityGrafanaDataSourceSpec defines the Grafana data source configuration of the Tempo deployment. +type MonolithicObservabilityGrafanaDataSourceSpec struct { + // Enabled defines if a Grafana data source should be created for this Tempo deployment. + // + // +kubebuilder:validation:Required + // +operator-sdk:csv:customresourcedefinitions:type=spec,displayName="Enabled",xDescriptors="urn:alm:descriptor:com.tectonic.ui:booleanSwitch" + Enabled bool `json:"enabled"` + + // InstanceSelector defines the Grafana instance where the data source should be created. + // + // +kubebuilder:validation:Optional + // +operator-sdk:csv:customresourcedefinitions:type=spec,displayName="Grafana Instance Selector" + InstanceSelector *metav1.LabelSelector `json:"instanceSelector,omitempty"` +} + // MonolithicComponentStatus defines the status of each component. type MonolithicComponentStatus struct { // Tempo is a map of the pod status of the Tempo pods. diff --git a/apis/tempo/v1alpha1/zz_generated.deepcopy.go b/apis/tempo/v1alpha1/zz_generated.deepcopy.go index 57b8392b1..2ac21f90f 100644 --- a/apis/tempo/v1alpha1/zz_generated.deepcopy.go +++ b/apis/tempo/v1alpha1/zz_generated.deepcopy.go @@ -560,6 +560,126 @@ func (in *MonolithicJaegerUISpec) DeepCopy() *MonolithicJaegerUISpec { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *MonolithicObservabilityGrafanaDataSourceSpec) DeepCopyInto(out *MonolithicObservabilityGrafanaDataSourceSpec) { + *out = *in + if in.InstanceSelector != nil { + in, out := &in.InstanceSelector, &out.InstanceSelector + *out = new(metav1.LabelSelector) + (*in).DeepCopyInto(*out) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MonolithicObservabilityGrafanaDataSourceSpec. +func (in *MonolithicObservabilityGrafanaDataSourceSpec) DeepCopy() *MonolithicObservabilityGrafanaDataSourceSpec { + if in == nil { + return nil + } + out := new(MonolithicObservabilityGrafanaDataSourceSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *MonolithicObservabilityGrafanaSpec) DeepCopyInto(out *MonolithicObservabilityGrafanaSpec) { + *out = *in + if in.DataSource != nil { + in, out := &in.DataSource, &out.DataSource + *out = new(MonolithicObservabilityGrafanaDataSourceSpec) + (*in).DeepCopyInto(*out) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MonolithicObservabilityGrafanaSpec. +func (in *MonolithicObservabilityGrafanaSpec) DeepCopy() *MonolithicObservabilityGrafanaSpec { + if in == nil { + return nil + } + out := new(MonolithicObservabilityGrafanaSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *MonolithicObservabilityMetricsPrometheusRulesSpec) DeepCopyInto(out *MonolithicObservabilityMetricsPrometheusRulesSpec) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MonolithicObservabilityMetricsPrometheusRulesSpec. +func (in *MonolithicObservabilityMetricsPrometheusRulesSpec) DeepCopy() *MonolithicObservabilityMetricsPrometheusRulesSpec { + if in == nil { + return nil + } + out := new(MonolithicObservabilityMetricsPrometheusRulesSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *MonolithicObservabilityMetricsServiceMonitorsSpec) DeepCopyInto(out *MonolithicObservabilityMetricsServiceMonitorsSpec) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MonolithicObservabilityMetricsServiceMonitorsSpec. +func (in *MonolithicObservabilityMetricsServiceMonitorsSpec) DeepCopy() *MonolithicObservabilityMetricsServiceMonitorsSpec { + if in == nil { + return nil + } + out := new(MonolithicObservabilityMetricsServiceMonitorsSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *MonolithicObservabilityMetricsSpec) DeepCopyInto(out *MonolithicObservabilityMetricsSpec) { + *out = *in + if in.ServiceMonitors != nil { + in, out := &in.ServiceMonitors, &out.ServiceMonitors + *out = new(MonolithicObservabilityMetricsServiceMonitorsSpec) + **out = **in + } + if in.PrometheusRules != nil { + in, out := &in.PrometheusRules, &out.PrometheusRules + *out = new(MonolithicObservabilityMetricsPrometheusRulesSpec) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MonolithicObservabilityMetricsSpec. +func (in *MonolithicObservabilityMetricsSpec) DeepCopy() *MonolithicObservabilityMetricsSpec { + if in == nil { + return nil + } + out := new(MonolithicObservabilityMetricsSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *MonolithicObservabilitySpec) DeepCopyInto(out *MonolithicObservabilitySpec) { + *out = *in + if in.Metrics != nil { + in, out := &in.Metrics, &out.Metrics + *out = new(MonolithicObservabilityMetricsSpec) + (*in).DeepCopyInto(*out) + } + if in.Grafana != nil { + in, out := &in.Grafana, &out.Grafana + *out = new(MonolithicObservabilityGrafanaSpec) + (*in).DeepCopyInto(*out) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MonolithicObservabilitySpec. +func (in *MonolithicObservabilitySpec) DeepCopy() *MonolithicObservabilitySpec { + if in == nil { + return nil + } + out := new(MonolithicObservabilitySpec) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *MonolithicStorageSpec) DeepCopyInto(out *MonolithicStorageSpec) { *out = *in @@ -1119,6 +1239,11 @@ func (in *TempoMonolithicSpec) DeepCopyInto(out *TempoMonolithicSpec) { *out = new(MonolithicJaegerUISpec) (*in).DeepCopyInto(*out) } + if in.Observability != nil { + in, out := &in.Observability, &out.Observability + *out = new(MonolithicObservabilitySpec) + (*in).DeepCopyInto(*out) + } if in.Resources != nil { in, out := &in.Resources, &out.Resources *out = new(v1.ResourceRequirements) diff --git a/bundle/community/manifests/tempo-operator.clusterserviceversion.yaml b/bundle/community/manifests/tempo-operator.clusterserviceversion.yaml index 36ff494bc..7117dcefa 100644 --- a/bundle/community/manifests/tempo-operator.clusterserviceversion.yaml +++ b/bundle/community/manifests/tempo-operator.clusterserviceversion.yaml @@ -74,7 +74,7 @@ metadata: capabilities: Deep Insights categories: Logging & Tracing,Monitoring containerImage: ghcr.io/grafana/tempo-operator/tempo-operator:v0.8.0 - createdAt: "2024-02-12T14:43:26Z" + createdAt: "2024-02-12T19:30:29Z" description: Create and manage deployments of Tempo, a high-scale distributed tracing backend. operatorframework.io/cluster-monitoring: "true" @@ -243,6 +243,47 @@ spec: the operator or self-managed. Default: Managed.' displayName: Management State path: management + - description: Observability defines the observability configuration of the + Tempo deployment. + displayName: Observability + path: observability + - description: Grafana defines the Grafana configuration of the Tempo deployment. + displayName: Grafana + path: observability.grafana + - description: DataSource defines the Grafana data source configuration. + displayName: Grafana data source + path: observability.grafana.dataSource + - description: Enabled defines if a Grafana data source should be created for + this Tempo deployment. + displayName: Enabled + path: observability.grafana.dataSource.enabled + x-descriptors: + - urn:alm:descriptor:com.tectonic.ui:booleanSwitch + - description: InstanceSelector defines the Grafana instance where the data + source should be created. + displayName: Grafana Instance Selector + path: observability.grafana.dataSource.instanceSelector + - description: Metrics defines the metric configuration of the Tempo deployment. + displayName: Metrics + path: observability.metrics + - description: ServiceMonitors defines the PrometheusRule configuration. + displayName: Prometheus Rules + path: observability.metrics.prometheusRules + - description: Enabled defines if PrometheusRule objects should be created for + this Tempo deployment. + displayName: Enabled + path: observability.metrics.prometheusRules.enabled + x-descriptors: + - urn:alm:descriptor:com.tectonic.ui:booleanSwitch + - description: ServiceMonitors defines the ServiceMonitor configuration. + displayName: Service Monitors + path: observability.metrics.serviceMonitors + - description: Enabled defines if ServiceMonitor objects should be created for + this Tempo deployment. + displayName: Enabled + path: observability.metrics.serviceMonitors.enabled + x-descriptors: + - urn:alm:descriptor:com.tectonic.ui:booleanSwitch - description: Resources defines the compute resource requirements of the Tempo container. displayName: Resources diff --git a/bundle/community/manifests/tempo.grafana.com_tempomonolithics.yaml b/bundle/community/manifests/tempo.grafana.com_tempomonolithics.yaml index 8d928473e..d523df11d 100644 --- a/bundle/community/manifests/tempo.grafana.com_tempomonolithics.yaml +++ b/bundle/community/manifests/tempo.grafana.com_tempomonolithics.yaml @@ -250,6 +250,98 @@ spec: - Managed - Unmanaged type: string + observability: + description: Observability defines the observability configuration + of the Tempo deployment. + properties: + grafana: + description: Grafana defines the Grafana configuration of the + Tempo deployment. + properties: + dataSource: + description: DataSource defines the Grafana data source configuration. + properties: + enabled: + description: Enabled defines if a Grafana data source + should be created for this Tempo deployment. + type: boolean + instanceSelector: + description: InstanceSelector defines the Grafana instance + where the data source should be created. + properties: + matchExpressions: + description: matchExpressions is a list of label selector + requirements. The requirements are ANDed. + items: + description: A label selector requirement is a selector + that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector + applies to. + type: string + operator: + description: operator represents a key's relationship + to a set of values. Valid operators are In, + NotIn, Exists and DoesNotExist. + type: string + values: + description: values is an array of string values. + If the operator is In or NotIn, the values + array must be non-empty. If the operator is + Exists or DoesNotExist, the values array must + be empty. This array is replaced during a + strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} pairs. + A single {key,value} in the matchLabels map is equivalent + to an element of matchExpressions, whose key field + is "key", the operator is "In", and the values array + contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + required: + - enabled + type: object + type: object + metrics: + description: Metrics defines the metric configuration of the Tempo + deployment. + properties: + prometheusRules: + description: ServiceMonitors defines the PrometheusRule configuration. + properties: + enabled: + description: Enabled defines if PrometheusRule objects + should be created for this Tempo deployment. + type: boolean + required: + - enabled + type: object + serviceMonitors: + description: ServiceMonitors defines the ServiceMonitor configuration. + properties: + enabled: + description: Enabled defines if ServiceMonitor objects + should be created for this Tempo deployment. + type: boolean + required: + - enabled + type: object + type: object + type: object resources: description: Resources defines the compute resource requirements of the Tempo container. diff --git a/bundle/openshift/manifests/tempo-operator.clusterserviceversion.yaml b/bundle/openshift/manifests/tempo-operator.clusterserviceversion.yaml index c7f20a62e..d8a096e61 100644 --- a/bundle/openshift/manifests/tempo-operator.clusterserviceversion.yaml +++ b/bundle/openshift/manifests/tempo-operator.clusterserviceversion.yaml @@ -74,7 +74,7 @@ metadata: capabilities: Deep Insights categories: Logging & Tracing,Monitoring containerImage: ghcr.io/grafana/tempo-operator/tempo-operator:v0.8.0 - createdAt: "2024-02-12T14:43:24Z" + createdAt: "2024-02-12T19:30:27Z" description: Create and manage deployments of Tempo, a high-scale distributed tracing backend. operatorframework.io/cluster-monitoring: "true" @@ -243,6 +243,47 @@ spec: the operator or self-managed. Default: Managed.' displayName: Management State path: management + - description: Observability defines the observability configuration of the + Tempo deployment. + displayName: Observability + path: observability + - description: Grafana defines the Grafana configuration of the Tempo deployment. + displayName: Grafana + path: observability.grafana + - description: DataSource defines the Grafana data source configuration. + displayName: Grafana data source + path: observability.grafana.dataSource + - description: Enabled defines if a Grafana data source should be created for + this Tempo deployment. + displayName: Enabled + path: observability.grafana.dataSource.enabled + x-descriptors: + - urn:alm:descriptor:com.tectonic.ui:booleanSwitch + - description: InstanceSelector defines the Grafana instance where the data + source should be created. + displayName: Grafana Instance Selector + path: observability.grafana.dataSource.instanceSelector + - description: Metrics defines the metric configuration of the Tempo deployment. + displayName: Metrics + path: observability.metrics + - description: ServiceMonitors defines the PrometheusRule configuration. + displayName: Prometheus Rules + path: observability.metrics.prometheusRules + - description: Enabled defines if PrometheusRule objects should be created for + this Tempo deployment. + displayName: Enabled + path: observability.metrics.prometheusRules.enabled + x-descriptors: + - urn:alm:descriptor:com.tectonic.ui:booleanSwitch + - description: ServiceMonitors defines the ServiceMonitor configuration. + displayName: Service Monitors + path: observability.metrics.serviceMonitors + - description: Enabled defines if ServiceMonitor objects should be created for + this Tempo deployment. + displayName: Enabled + path: observability.metrics.serviceMonitors.enabled + x-descriptors: + - urn:alm:descriptor:com.tectonic.ui:booleanSwitch - description: Resources defines the compute resource requirements of the Tempo container. displayName: Resources diff --git a/bundle/openshift/manifests/tempo.grafana.com_tempomonolithics.yaml b/bundle/openshift/manifests/tempo.grafana.com_tempomonolithics.yaml index 8d928473e..d523df11d 100644 --- a/bundle/openshift/manifests/tempo.grafana.com_tempomonolithics.yaml +++ b/bundle/openshift/manifests/tempo.grafana.com_tempomonolithics.yaml @@ -250,6 +250,98 @@ spec: - Managed - Unmanaged type: string + observability: + description: Observability defines the observability configuration + of the Tempo deployment. + properties: + grafana: + description: Grafana defines the Grafana configuration of the + Tempo deployment. + properties: + dataSource: + description: DataSource defines the Grafana data source configuration. + properties: + enabled: + description: Enabled defines if a Grafana data source + should be created for this Tempo deployment. + type: boolean + instanceSelector: + description: InstanceSelector defines the Grafana instance + where the data source should be created. + properties: + matchExpressions: + description: matchExpressions is a list of label selector + requirements. The requirements are ANDed. + items: + description: A label selector requirement is a selector + that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector + applies to. + type: string + operator: + description: operator represents a key's relationship + to a set of values. Valid operators are In, + NotIn, Exists and DoesNotExist. + type: string + values: + description: values is an array of string values. + If the operator is In or NotIn, the values + array must be non-empty. If the operator is + Exists or DoesNotExist, the values array must + be empty. This array is replaced during a + strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} pairs. + A single {key,value} in the matchLabels map is equivalent + to an element of matchExpressions, whose key field + is "key", the operator is "In", and the values array + contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + required: + - enabled + type: object + type: object + metrics: + description: Metrics defines the metric configuration of the Tempo + deployment. + properties: + prometheusRules: + description: ServiceMonitors defines the PrometheusRule configuration. + properties: + enabled: + description: Enabled defines if PrometheusRule objects + should be created for this Tempo deployment. + type: boolean + required: + - enabled + type: object + serviceMonitors: + description: ServiceMonitors defines the ServiceMonitor configuration. + properties: + enabled: + description: Enabled defines if ServiceMonitor objects + should be created for this Tempo deployment. + type: boolean + required: + - enabled + type: object + type: object + type: object resources: description: Resources defines the compute resource requirements of the Tempo container. diff --git a/config/crd/bases/tempo.grafana.com_tempomonolithics.yaml b/config/crd/bases/tempo.grafana.com_tempomonolithics.yaml index 000fc6b94..c8bbb2be2 100644 --- a/config/crd/bases/tempo.grafana.com_tempomonolithics.yaml +++ b/config/crd/bases/tempo.grafana.com_tempomonolithics.yaml @@ -247,6 +247,98 @@ spec: - Managed - Unmanaged type: string + observability: + description: Observability defines the observability configuration + of the Tempo deployment. + properties: + grafana: + description: Grafana defines the Grafana configuration of the + Tempo deployment. + properties: + dataSource: + description: DataSource defines the Grafana data source configuration. + properties: + enabled: + description: Enabled defines if a Grafana data source + should be created for this Tempo deployment. + type: boolean + instanceSelector: + description: InstanceSelector defines the Grafana instance + where the data source should be created. + properties: + matchExpressions: + description: matchExpressions is a list of label selector + requirements. The requirements are ANDed. + items: + description: A label selector requirement is a selector + that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector + applies to. + type: string + operator: + description: operator represents a key's relationship + to a set of values. Valid operators are In, + NotIn, Exists and DoesNotExist. + type: string + values: + description: values is an array of string values. + If the operator is In or NotIn, the values + array must be non-empty. If the operator is + Exists or DoesNotExist, the values array must + be empty. This array is replaced during a + strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} pairs. + A single {key,value} in the matchLabels map is equivalent + to an element of matchExpressions, whose key field + is "key", the operator is "In", and the values array + contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + required: + - enabled + type: object + type: object + metrics: + description: Metrics defines the metric configuration of the Tempo + deployment. + properties: + prometheusRules: + description: ServiceMonitors defines the PrometheusRule configuration. + properties: + enabled: + description: Enabled defines if PrometheusRule objects + should be created for this Tempo deployment. + type: boolean + required: + - enabled + type: object + serviceMonitors: + description: ServiceMonitors defines the ServiceMonitor configuration. + properties: + enabled: + description: Enabled defines if ServiceMonitor objects + should be created for this Tempo deployment. + type: boolean + required: + - enabled + type: object + type: object + type: object resources: description: Resources defines the compute resource requirements of the Tempo container. diff --git a/config/manifests/community/bases/tempo-operator.clusterserviceversion.yaml b/config/manifests/community/bases/tempo-operator.clusterserviceversion.yaml index 8e97c84cb..5ae4e7e53 100644 --- a/config/manifests/community/bases/tempo-operator.clusterserviceversion.yaml +++ b/config/manifests/community/bases/tempo-operator.clusterserviceversion.yaml @@ -172,6 +172,47 @@ spec: the operator or self-managed. Default: Managed.' displayName: Management State path: management + - description: Observability defines the observability configuration of the + Tempo deployment. + displayName: Observability + path: observability + - description: Grafana defines the Grafana configuration of the Tempo deployment. + displayName: Grafana + path: observability.grafana + - description: DataSource defines the Grafana data source configuration. + displayName: Grafana data source + path: observability.grafana.dataSource + - description: Enabled defines if a Grafana data source should be created for + this Tempo deployment. + displayName: Enabled + path: observability.grafana.dataSource.enabled + x-descriptors: + - urn:alm:descriptor:com.tectonic.ui:booleanSwitch + - description: InstanceSelector defines the Grafana instance where the data + source should be created. + displayName: Grafana Instance Selector + path: observability.grafana.dataSource.instanceSelector + - description: Metrics defines the metric configuration of the Tempo deployment. + displayName: Metrics + path: observability.metrics + - description: ServiceMonitors defines the PrometheusRule configuration. + displayName: Prometheus Rules + path: observability.metrics.prometheusRules + - description: Enabled defines if PrometheusRule objects should be created for + this Tempo deployment. + displayName: Enabled + path: observability.metrics.prometheusRules.enabled + x-descriptors: + - urn:alm:descriptor:com.tectonic.ui:booleanSwitch + - description: ServiceMonitors defines the ServiceMonitor configuration. + displayName: Service Monitors + path: observability.metrics.serviceMonitors + - description: Enabled defines if ServiceMonitor objects should be created for + this Tempo deployment. + displayName: Enabled + path: observability.metrics.serviceMonitors.enabled + x-descriptors: + - urn:alm:descriptor:com.tectonic.ui:booleanSwitch - description: Resources defines the compute resource requirements of the Tempo container. displayName: Resources diff --git a/config/manifests/openshift/bases/tempo-operator.clusterserviceversion.yaml b/config/manifests/openshift/bases/tempo-operator.clusterserviceversion.yaml index 81843db10..f36263082 100644 --- a/config/manifests/openshift/bases/tempo-operator.clusterserviceversion.yaml +++ b/config/manifests/openshift/bases/tempo-operator.clusterserviceversion.yaml @@ -172,6 +172,47 @@ spec: the operator or self-managed. Default: Managed.' displayName: Management State path: management + - description: Observability defines the observability configuration of the + Tempo deployment. + displayName: Observability + path: observability + - description: Grafana defines the Grafana configuration of the Tempo deployment. + displayName: Grafana + path: observability.grafana + - description: DataSource defines the Grafana data source configuration. + displayName: Grafana data source + path: observability.grafana.dataSource + - description: Enabled defines if a Grafana data source should be created for + this Tempo deployment. + displayName: Enabled + path: observability.grafana.dataSource.enabled + x-descriptors: + - urn:alm:descriptor:com.tectonic.ui:booleanSwitch + - description: InstanceSelector defines the Grafana instance where the data + source should be created. + displayName: Grafana Instance Selector + path: observability.grafana.dataSource.instanceSelector + - description: Metrics defines the metric configuration of the Tempo deployment. + displayName: Metrics + path: observability.metrics + - description: ServiceMonitors defines the PrometheusRule configuration. + displayName: Prometheus Rules + path: observability.metrics.prometheusRules + - description: Enabled defines if PrometheusRule objects should be created for + this Tempo deployment. + displayName: Enabled + path: observability.metrics.prometheusRules.enabled + x-descriptors: + - urn:alm:descriptor:com.tectonic.ui:booleanSwitch + - description: ServiceMonitors defines the ServiceMonitor configuration. + displayName: Service Monitors + path: observability.metrics.serviceMonitors + - description: Enabled defines if ServiceMonitor objects should be created for + this Tempo deployment. + displayName: Enabled + path: observability.metrics.serviceMonitors.enabled + x-descriptors: + - urn:alm:descriptor:com.tectonic.ui:booleanSwitch - description: Resources defines the compute resource requirements of the Tempo container. displayName: Resources diff --git a/controllers/tempo/tempomonolithic_controller.go b/controllers/tempo/tempomonolithic_controller.go index 0983e5370..f9b365108 100644 --- a/controllers/tempo/tempomonolithic_controller.go +++ b/controllers/tempo/tempomonolithic_controller.go @@ -179,5 +179,14 @@ func (r *TempoMonolithicReconciler) SetupWithManager(mgr ctrl.Manager) error { builder = builder.Owns(&routev1.Route{}) } + if r.CtrlConfig.Gates.PrometheusOperator { + builder = builder.Owns(&monitoringv1.ServiceMonitor{}) + builder = builder.Owns(&monitoringv1.PrometheusRule{}) + } + + if r.CtrlConfig.Gates.GrafanaOperator { + builder = builder.Owns(&grafanav1.GrafanaDatasource{}) + } + return builder.Complete(r) } diff --git a/controllers/tempo/tempostack_controller.go b/controllers/tempo/tempostack_controller.go index fa8eb714f..e1bcd4d03 100644 --- a/controllers/tempo/tempostack_controller.go +++ b/controllers/tempo/tempostack_controller.go @@ -6,6 +6,7 @@ import ( "fmt" "github.com/go-logr/logr" + grafanav1 "github.com/grafana/grafana-operator/v5/api/v1beta1" routev1 "github.com/openshift/api/route/v1" monitoringv1 "github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring/v1" appsv1 "k8s.io/api/apps/v1" @@ -206,13 +207,17 @@ func (r *TempoStackReconciler) SetupWithManager(mgr ctrl.Manager) error { builder.WithPredicates(predicate.ResourceVersionChangedPredicate{}), ) + if r.CtrlConfig.Gates.OpenShift.OpenShiftRoute { + builder = builder.Owns(&routev1.Route{}) + } + if r.CtrlConfig.Gates.PrometheusOperator { builder = builder.Owns(&monitoringv1.ServiceMonitor{}) builder = builder.Owns(&monitoringv1.PrometheusRule{}) } - if r.CtrlConfig.Gates.OpenShift.OpenShiftRoute { - builder = builder.Owns(&routev1.Route{}) + if r.CtrlConfig.Gates.GrafanaOperator { + builder = builder.Owns(&grafanav1.GrafanaDatasource{}) } return builder.Complete(r) diff --git a/docs/operator/api.md b/docs/operator/api.md index f441fecd3..01ebcbf00 100644 --- a/docs/operator/api.md +++ b/docs/operator/api.md @@ -2290,6 +2290,417 @@ Kubernetes core/v1.ResourceRequirements +## MonolithicObservabilityGrafanaDataSourceSpec { #tempo-grafana-com-v1alpha1-MonolithicObservabilityGrafanaDataSourceSpec } + +

+ +(Appears on:MonolithicObservabilityGrafanaSpec) + +

+ +
+ +

MonolithicObservabilityGrafanaDataSourceSpec defines the Grafana data source configuration of the Tempo deployment.

+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FieldDescription
+ +enabled
+ + + +bool + + + +
+ +

Enabled defines if a Grafana data source should be created for this Tempo deployment.

+ +
+ +instanceSelector
+ + + + + +Kubernetes meta/v1.LabelSelector + + + + + +
+ +

InstanceSelector defines the Grafana instance where the data source should be created.

+ +
+ +## MonolithicObservabilityGrafanaSpec { #tempo-grafana-com-v1alpha1-MonolithicObservabilityGrafanaSpec } + +

+ +(Appears on:MonolithicObservabilitySpec) + +

+ +
+ +

MonolithicObservabilityGrafanaSpec defines the Grafana configuration of the Tempo deployment.

+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + +
FieldDescription
+ +dataSource
+ + + + + +MonolithicObservabilityGrafanaDataSourceSpec + + + + + +
+ +

DataSource defines the Grafana data source configuration.

+ +
+ +## MonolithicObservabilityMetricsPrometheusRulesSpec { #tempo-grafana-com-v1alpha1-MonolithicObservabilityMetricsPrometheusRulesSpec } + +

+ +(Appears on:MonolithicObservabilityMetricsSpec) + +

+ +
+ +

MonolithicObservabilityMetricsPrometheusRulesSpec defines the PrometheusRules settings.

+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + +
FieldDescription
+ +enabled
+ + + +bool + + + +
+ +

Enabled defines if PrometheusRule objects should be created for this Tempo deployment.

+ +
+ +## MonolithicObservabilityMetricsServiceMonitorsSpec { #tempo-grafana-com-v1alpha1-MonolithicObservabilityMetricsServiceMonitorsSpec } + +

+ +(Appears on:MonolithicObservabilityMetricsSpec) + +

+ +
+ +

MonolithicObservabilityMetricsServiceMonitorsSpec defines the ServiceMonitor settings.

+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + +
FieldDescription
+ +enabled
+ + + +bool + + + +
+ +

Enabled defines if ServiceMonitor objects should be created for this Tempo deployment.

+ +
+ +## MonolithicObservabilityMetricsSpec { #tempo-grafana-com-v1alpha1-MonolithicObservabilityMetricsSpec } + +

+ +(Appears on:MonolithicObservabilitySpec) + +

+ +
+ +

MonolithicObservabilityMetricsSpec defines the metrics settings of the Tempo deployment.

+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FieldDescription
+ +serviceMonitors
+ + + + + +MonolithicObservabilityMetricsServiceMonitorsSpec + + + + + +
+ +

ServiceMonitors defines the ServiceMonitor configuration.

+ +
+ +prometheusRules
+ + + + + +MonolithicObservabilityMetricsPrometheusRulesSpec + + + + + +
+ +

ServiceMonitors defines the PrometheusRule configuration.

+ +
+ +## MonolithicObservabilitySpec { #tempo-grafana-com-v1alpha1-MonolithicObservabilitySpec } + +

+ +(Appears on:TempoMonolithicSpec) + +

+ +
+ +

MonolithicObservabilitySpec defines the observability configuration of the Tempo deployment.

+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FieldDescription
+ +metrics
+ + + + + +MonolithicObservabilityMetricsSpec + + + + + +
+ +

Metrics defines the metric configuration of the Tempo deployment.

+ +
+ +grafana
+ + + + + +MonolithicObservabilityGrafanaSpec + + + + + +
+ +

Grafana defines the Grafana configuration of the Tempo deployment.

+ +
+ ## MonolithicStorageSpec { #tempo-grafana-com-v1alpha1-MonolithicStorageSpec }

@@ -4858,6 +5269,31 @@ MonolithicJaegerUISpec +observability
+ + + + + +MonolithicObservabilitySpec + + + + + + + + + +

Observability defines the observability configuration of the Tempo deployment.

+ + + + + + + + management
diff --git a/docs/spec/tempo.grafana.com_tempomonolithics.yaml b/docs/spec/tempo.grafana.com_tempomonolithics.yaml index 8b4ab206d..83fe1945c 100644 --- a/docs/spec/tempo.grafana.com_tempomonolithics.yaml +++ b/docs/spec/tempo.grafana.com_tempomonolithics.yaml @@ -45,6 +45,23 @@ spec: # TempoMonolithicSpec defines the desir host: "" # Host defines the hostname of the Route object. termination: "edge" # Termination specifies the termination type. Default: edge. management: "" # ManagementState defines whether this instance is managed by the operator or self-managed. Default: Managed. + observability: # Observability defines the observability configuration of the Tempo deployment. + grafana: # Grafana defines the Grafana configuration of the Tempo deployment. + dataSource: # DataSource defines the Grafana data source configuration. + enabled: false # Enabled defines if a Grafana data source should be created for this Tempo deployment. + instanceSelector: # InstanceSelector defines the Grafana instance where the data source should be created. + matchExpressions: # matchExpressions is a list of label selector requirements. The requirements are ANDed. + - key: "" # key is the label key that the selector applies to. + operator: "" # operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist. + values: # values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. This array is replaced during a strategic merge patch. + - "" + matchLabels: # matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels map is equivalent to an element of matchExpressions, whose key field is "key", the operator is "In", and the values array contains only "value". The requirements are ANDed. + "key": "" + metrics: # Metrics defines the metric configuration of the Tempo deployment. + prometheusRules: # ServiceMonitors defines the PrometheusRule configuration. + enabled: false # Enabled defines if PrometheusRule objects should be created for this Tempo deployment. + serviceMonitors: # ServiceMonitors defines the ServiceMonitor configuration. + enabled: false # Enabled defines if ServiceMonitor objects should be created for this Tempo deployment. resources: # Resources defines the compute resource requirements of the Tempo container. claims: # Claims lists the names of resources, defined in spec.resourceClaims, that are used by this container. This is an alpha field and requires enabling the DynamicResourceAllocation feature gate. This field is immutable. It can only be set for containers. - name: "" # Name must match the name of one entry in pod.spec.resourceClaims of the Pod where this field is used. It makes that resource available inside a container. diff --git a/internal/manifests/grafana/datasource.go b/internal/manifests/grafana/datasource.go index 83e7c1165..4ed439509 100644 --- a/internal/manifests/grafana/datasource.go +++ b/internal/manifests/grafana/datasource.go @@ -1,40 +1,58 @@ package grafana import ( - "encoding/json" "fmt" grafanav1 "github.com/grafana/grafana-operator/v5/api/v1beta1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/labels" "github.com/grafana/tempo-operator/internal/manifests/manifestutils" "github.com/grafana/tempo-operator/internal/manifests/naming" ) -// BuildGrafanaDatasource creates a Datasource for Grafana Tempo. -func BuildGrafanaDatasource(params manifestutils.Params) (*grafanav1.GrafanaDatasource, error) { - var tlsSkipVerify = true - var component = manifestutils.QueryFrontendComponentName +// BuildGrafanaDatasource creates a data source for Grafana Tempo. +func BuildGrafanaDatasource(params manifestutils.Params) *grafanav1.GrafanaDatasource { + tempo := params.Tempo + labels := manifestutils.CommonLabels(tempo.Name) + var url string - if params.Tempo.Spec.Template.Gateway.Enabled { - component = manifestutils.GatewayComponentName + if tempo.Spec.Template.Gateway.Enabled { + url = fmt.Sprintf("http://%s:%d", naming.ServiceFqdn(tempo.Namespace, tempo.Name, manifestutils.GatewayComponentName), manifestutils.PortHTTPServer) + } else { + url = fmt.Sprintf("http://%s:%d", naming.ServiceFqdn(tempo.Namespace, tempo.Name, manifestutils.QueryFrontendComponentName), manifestutils.PortHTTPServer) } + return NewGrafanaDatasource(tempo.Namespace, tempo.Name, labels, url, tempo.Spec.Observability.Grafana.InstanceSelector) +} + +// NewGrafanaDatasource creates a data source for Grafana Tempo. +func NewGrafanaDatasource( + namespace string, + name string, + labels labels.Set, + url string, + instanceSelector metav1.LabelSelector, +) *grafanav1.GrafanaDatasource { return &grafanav1.GrafanaDatasource{ + TypeMeta: metav1.TypeMeta{ + APIVersion: grafanav1.GroupVersion.String(), + Kind: "GrafanaDatasource", + }, ObjectMeta: metav1.ObjectMeta{ - Name: params.Tempo.Name, - Namespace: params.Tempo.Namespace, - Labels: manifestutils.CommonLabels(params.Tempo.Name), + Namespace: namespace, + Name: name, + Labels: labels, }, Spec: grafanav1.GrafanaDatasourceSpec{ Datasource: &grafanav1.GrafanaDatasourceInternal{ - Access: "proxy", - Name: params.Tempo.Name, - Type: "tempo", - URL: fmt.Sprintf("http://%s:%d", naming.ServiceFqdn(params.Tempo.Namespace, params.Tempo.Name, component), manifestutils.PortHTTPServer), - JSONData: json.RawMessage(fmt.Sprintf(`{"tlsSkipVerify": %t}`, tlsSkipVerify)), + Name: name, + Type: "tempo", + Access: "proxy", + URL: url, }, - InstanceSelector: ¶ms.Tempo.Spec.Observability.Grafana.InstanceSelector, + // InstanceSelector is a required field in the spec + InstanceSelector: &instanceSelector, }, - }, nil + } } diff --git a/internal/manifests/grafana/datasource_test.go b/internal/manifests/grafana/datasource_test.go index 9dfe70a02..a4a72ee53 100644 --- a/internal/manifests/grafana/datasource_test.go +++ b/internal/manifests/grafana/datasource_test.go @@ -1,12 +1,9 @@ package grafana import ( - "encoding/json" - "fmt" "testing" grafanav1 "github.com/grafana/grafana-operator/v5/api/v1beta1" - "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -15,7 +12,7 @@ import ( ) func TestBuildGrafanaDatasource(t *testing.T) { - datasource, err := BuildGrafanaDatasource(manifestutils.Params{Tempo: v1alpha1.TempoStack{ + datasource := BuildGrafanaDatasource(manifestutils.Params{Tempo: v1alpha1.TempoStack{ ObjectMeta: metav1.ObjectMeta{ Name: "test", Namespace: "tempo", @@ -24,10 +21,12 @@ func TestBuildGrafanaDatasource(t *testing.T) { }}) labels := manifestutils.CommonLabels("test") - require.NoError(t, err) - - assert.NotNil(t, datasource) - assert.Equal(t, &grafanav1.GrafanaDatasource{ + require.NotNil(t, datasource) + require.Equal(t, &grafanav1.GrafanaDatasource{ + TypeMeta: metav1.TypeMeta{ + APIVersion: "grafana.integreatly.org/v1beta1", + Kind: "GrafanaDatasource", + }, ObjectMeta: metav1.ObjectMeta{ Name: "test", Namespace: "tempo", @@ -35,11 +34,10 @@ func TestBuildGrafanaDatasource(t *testing.T) { }, Spec: grafanav1.GrafanaDatasourceSpec{ Datasource: &grafanav1.GrafanaDatasourceInternal{ - Access: "proxy", - Name: "test", - Type: "tempo", - URL: "http://tempo-test-query-frontend.tempo.svc.cluster.local:3200", - JSONData: json.RawMessage(fmt.Sprintf(`{"tlsSkipVerify": %t}`, true)), + Access: "proxy", + Name: "test", + Type: "tempo", + URL: "http://tempo-test-query-frontend.tempo.svc.cluster.local:3200", }, InstanceSelector: &metav1.LabelSelector{}, }, diff --git a/internal/manifests/manifests.go b/internal/manifests/manifests.go index 87c74f78b..27c804131 100644 --- a/internal/manifests/manifests.go +++ b/internal/manifests/manifests.go @@ -84,11 +84,7 @@ func BuildAll(params manifestutils.Params) ([]client.Object, error) { } if params.Tempo.Spec.Observability.Grafana.CreateDatasource { - grafanaDatasource, err := grafana.BuildGrafanaDatasource(params) - if err != nil { - return nil, err - } - manifests = append(manifests, grafanaDatasource) + manifests = append(manifests, grafana.BuildGrafanaDatasource(params)) } return manifests, nil diff --git a/internal/manifests/monolithic/build.go b/internal/manifests/monolithic/build.go index 8d3fd0f79..016ab45ee 100644 --- a/internal/manifests/monolithic/build.go +++ b/internal/manifests/monolithic/build.go @@ -6,6 +6,7 @@ import ( // BuildAll generates all manifests. func BuildAll(opts Options) ([]client.Object, error) { + tempo := opts.Tempo manifests := []client.Object{} configMap, configChecksum, err := BuildConfigMap(opts) @@ -30,5 +31,26 @@ func BuildAll(opts Options) ([]client.Object, error) { } manifests = append(manifests, ingresses...) + if tempo.Spec.Observability != nil { + if tempo.Spec.Observability.Metrics != nil { + if tempo.Spec.Observability.Metrics.ServiceMonitors != nil && tempo.Spec.Observability.Metrics.ServiceMonitors.Enabled { + manifests = append(manifests, BuildServiceMonitor(opts)) + } + + if tempo.Spec.Observability.Metrics.PrometheusRules != nil && tempo.Spec.Observability.Metrics.PrometheusRules.Enabled { + prometheusRules, err := BuildPrometheusRules(opts) + if err != nil { + return nil, err + } + manifests = append(manifests, prometheusRules...) + } + } + + if tempo.Spec.Observability.Grafana != nil && + tempo.Spec.Observability.Grafana.DataSource != nil && tempo.Spec.Observability.Grafana.DataSource.Enabled { + manifests = append(manifests, BuildGrafanaDatasource(opts)) + } + } + return manifests, nil } diff --git a/internal/manifests/monolithic/grafana_datasource.go b/internal/manifests/monolithic/grafana_datasource.go new file mode 100644 index 000000000..60a38fd0f --- /dev/null +++ b/internal/manifests/monolithic/grafana_datasource.go @@ -0,0 +1,22 @@ +package monolithic + +import ( + "fmt" + + grafanav1 "github.com/grafana/grafana-operator/v5/api/v1beta1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/utils/ptr" + + "github.com/grafana/tempo-operator/internal/manifests/grafana" + "github.com/grafana/tempo-operator/internal/manifests/manifestutils" + "github.com/grafana/tempo-operator/internal/manifests/naming" +) + +// BuildGrafanaDatasource create a Grafana data source. +func BuildGrafanaDatasource(opts Options) *grafanav1.GrafanaDatasource { + tempo := opts.Tempo + labels := ComponentLabels(manifestutils.TempoMonolithComponentName, tempo.Name) + url := fmt.Sprintf("http://%s:%d", naming.ServiceFqdn(tempo.Namespace, tempo.Name, manifestutils.TempoMonolithComponentName), manifestutils.PortHTTPServer) + instanceSelector := ptr.Deref(tempo.Spec.Observability.Grafana.DataSource.InstanceSelector, metav1.LabelSelector{}) + return grafana.NewGrafanaDatasource(tempo.Namespace, tempo.Name, labels, url, instanceSelector) +} diff --git a/internal/manifests/monolithic/grafana_datasource_test.go b/internal/manifests/monolithic/grafana_datasource_test.go new file mode 100644 index 000000000..d863cc4a7 --- /dev/null +++ b/internal/manifests/monolithic/grafana_datasource_test.go @@ -0,0 +1,59 @@ +package monolithic + +import ( + "testing" + + grafanav1 "github.com/grafana/grafana-operator/v5/api/v1beta1" + "github.com/stretchr/testify/require" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + "github.com/grafana/tempo-operator/apis/tempo/v1alpha1" +) + +func TestBuildGrafanaDatasource(t *testing.T) { + opts := Options{ + Tempo: v1alpha1.TempoMonolithic{ + ObjectMeta: metav1.ObjectMeta{ + Name: "sample", + Namespace: "default", + }, + Spec: v1alpha1.TempoMonolithicSpec{ + Observability: &v1alpha1.MonolithicObservabilitySpec{ + Grafana: &v1alpha1.MonolithicObservabilityGrafanaSpec{ + DataSource: &v1alpha1.MonolithicObservabilityGrafanaDataSourceSpec{ + Enabled: true, + InstanceSelector: &metav1.LabelSelector{ + MatchLabels: map[string]string{"key": "value"}, + }, + }, + }, + }, + }, + }, + } + datasource := BuildGrafanaDatasource(opts) + + labels := ComponentLabels("tempo", "sample") + require.Equal(t, &grafanav1.GrafanaDatasource{ + TypeMeta: metav1.TypeMeta{ + APIVersion: "grafana.integreatly.org/v1beta1", + Kind: "GrafanaDatasource", + }, + ObjectMeta: metav1.ObjectMeta{ + Namespace: "default", + Name: "sample", + Labels: labels, + }, + Spec: grafanav1.GrafanaDatasourceSpec{ + Datasource: &grafanav1.GrafanaDatasourceInternal{ + Name: "sample", + Type: "tempo", + Access: "proxy", + URL: "http://tempo-sample.default.svc.cluster.local:3200", + }, + InstanceSelector: &metav1.LabelSelector{ + MatchLabels: map[string]string{"key": "value"}, + }, + }, + }, datasource) +} diff --git a/internal/manifests/monolithic/prometheusrule.go b/internal/manifests/monolithic/prometheusrule.go new file mode 100644 index 000000000..700ed77d8 --- /dev/null +++ b/internal/manifests/monolithic/prometheusrule.go @@ -0,0 +1,13 @@ +package monolithic + +import ( + "sigs.k8s.io/controller-runtime/pkg/client" + + "github.com/grafana/tempo-operator/internal/manifests/alerts" +) + +// BuildPrometheusRules creates PrometheusRule objects. +func BuildPrometheusRules(opts Options) ([]client.Object, error) { + tempo := opts.Tempo + return alerts.BuildPrometheusRule(tempo.Name, tempo.Namespace) +} diff --git a/internal/manifests/monolithic/prometheusrule_test.go b/internal/manifests/monolithic/prometheusrule_test.go new file mode 100644 index 000000000..1b6ce382b --- /dev/null +++ b/internal/manifests/monolithic/prometheusrule_test.go @@ -0,0 +1,31 @@ +package monolithic + +import ( + "testing" + + monitoringv1 "github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring/v1" + "github.com/stretchr/testify/require" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + "github.com/grafana/tempo-operator/apis/tempo/v1alpha1" +) + +func TestBuildPrometheusRules(t *testing.T) { + opts := Options{ + Tempo: v1alpha1.TempoMonolithic{ + ObjectMeta: metav1.ObjectMeta{ + Name: "sample", + Namespace: "default", + }, + }, + } + objects, err := BuildPrometheusRules(opts) + require.NoError(t, err) + + rules := objects[0].(*monitoringv1.PrometheusRule) + require.Equal(t, "sample-prometheus-rule", rules.Name) + require.Len(t, rules.Spec.Groups, 2) + require.Len(t, rules.Spec.Groups[0].Rules, 14) // alert rules + require.Len(t, rules.Spec.Groups[1].Rules, 6) // recording rules + require.Equal(t, "cluster_namespace_job_route:tempo_request_duration_seconds:99quantile{cluster=\"sample\", namespace=\"default\", route!~\"metrics|/frontend.Frontend/Process|debug_pprof\"} > 3\n", rules.Spec.Groups[0].Rules[0].Expr.StrVal) +} diff --git a/internal/manifests/monolithic/servicemonitor.go b/internal/manifests/monolithic/servicemonitor.go new file mode 100644 index 000000000..e4f70ac3b --- /dev/null +++ b/internal/manifests/monolithic/servicemonitor.go @@ -0,0 +1,15 @@ +package monolithic + +import ( + monitoringv1 "github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring/v1" + + "github.com/grafana/tempo-operator/internal/manifests/manifestutils" + "github.com/grafana/tempo-operator/internal/manifests/servicemonitor" +) + +// BuildServiceMonitor create a ServiceMonitor. +func BuildServiceMonitor(opts Options) *monitoringv1.ServiceMonitor { + tempo := opts.Tempo + labels := ComponentLabels(manifestutils.TempoMonolithComponentName, tempo.Name) + return servicemonitor.NewServiceMonitor(tempo.Namespace, tempo.Name, labels, false, manifestutils.TempoMonolithComponentName, manifestutils.HttpPortName) +} diff --git a/internal/manifests/monolithic/servicemonitor_test.go b/internal/manifests/monolithic/servicemonitor_test.go new file mode 100644 index 000000000..e5399b1ca --- /dev/null +++ b/internal/manifests/monolithic/servicemonitor_test.go @@ -0,0 +1,61 @@ +package monolithic + +import ( + "testing" + + monitoringv1 "github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring/v1" + "github.com/stretchr/testify/require" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + "github.com/grafana/tempo-operator/apis/tempo/v1alpha1" +) + +func TestBuildServiceMonitor(t *testing.T) { + opts := Options{ + Tempo: v1alpha1.TempoMonolithic{ + ObjectMeta: metav1.ObjectMeta{ + Name: "sample", + Namespace: "default", + }, + Spec: v1alpha1.TempoMonolithicSpec{}, + }, + } + sm := BuildServiceMonitor(opts) + + labels := ComponentLabels("tempo", "sample") + require.Equal(t, &monitoringv1.ServiceMonitor{ + TypeMeta: metav1.TypeMeta{ + APIVersion: "monitoring.coreos.com/v1", + Kind: "ServiceMonitor", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "tempo-sample", + Namespace: "default", + Labels: labels, + }, + Spec: monitoringv1.ServiceMonitorSpec{ + Endpoints: []monitoringv1.Endpoint{{ + Scheme: "http", + Port: "http", + Path: "/metrics", + RelabelConfigs: []*monitoringv1.RelabelConfig{ + { + SourceLabels: []monitoringv1.LabelName{"__meta_kubernetes_service_label_app_kubernetes_io_instance"}, + TargetLabel: "cluster", + }, + { + SourceLabels: []monitoringv1.LabelName{"__meta_kubernetes_namespace", "__meta_kubernetes_service_label_app_kubernetes_io_component"}, + Separator: "/", + TargetLabel: "job", + }, + }, + }}, + NamespaceSelector: monitoringv1.NamespaceSelector{ + MatchNames: []string{"default"}, + }, + Selector: metav1.LabelSelector{ + MatchLabels: labels, + }, + }, + }, sm) +} diff --git a/internal/manifests/servicemonitor/servicemonitor.go b/internal/manifests/servicemonitor/servicemonitor.go index aec322806..f76cea68b 100644 --- a/internal/manifests/servicemonitor/servicemonitor.go +++ b/internal/manifests/servicemonitor/servicemonitor.go @@ -4,6 +4,7 @@ import ( monitoringv1 "github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring/v1" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/labels" "sigs.k8s.io/controller-runtime/pkg/client" "github.com/grafana/tempo-operator/internal/certrotation" @@ -33,21 +34,32 @@ func BuildServiceMonitors(params manifestutils.Params) []client.Object { } func buildServiceMonitor(params manifestutils.Params, component string, port string) *monitoringv1.ServiceMonitor { - tempo := params.Tempo - labels := manifestutils.ComponentLabels(component, tempo.Name) + labels := manifestutils.ComponentLabels(component, params.Tempo.Name) + return NewServiceMonitor(params.Tempo.Namespace, params.Tempo.Name, labels, params.CtrlConfig.Gates.HTTPEncryption, component, port) +} + +// NewServiceMonitor creates a ServiceMonitor. +func NewServiceMonitor( + namespace string, + name string, + labels labels.Set, + tls bool, + component string, + port string, +) *monitoringv1.ServiceMonitor { scheme := "http" var tlsConfig *monitoringv1.TLSConfig - if params.CtrlConfig.Gates.HTTPEncryption { + if tls { scheme = "https" - serverName := naming.ServiceFqdn(tempo.Namespace, tempo.Name, component) + serverName := naming.ServiceFqdn(namespace, name, component) tlsConfig = &monitoringv1.TLSConfig{ SafeTLSConfig: monitoringv1.SafeTLSConfig{ CA: monitoringv1.SecretOrConfigMap{ ConfigMap: &corev1.ConfigMapKeySelector{ LocalObjectReference: corev1.LocalObjectReference{ - Name: naming.SigningCABundleName(tempo.Name), + Name: naming.SigningCABundleName(name), }, Key: certrotation.CAFile, }, @@ -55,14 +67,14 @@ func buildServiceMonitor(params manifestutils.Params, component string, port str Cert: monitoringv1.SecretOrConfigMap{ Secret: &corev1.SecretKeySelector{ LocalObjectReference: corev1.LocalObjectReference{ - Name: naming.TLSSecretName(component, tempo.Name), + Name: naming.TLSSecretName(component, name), }, Key: corev1.TLSCertKey, }, }, KeySecret: &corev1.SecretKeySelector{ LocalObjectReference: corev1.LocalObjectReference{ - Name: naming.TLSSecretName(component, tempo.Name), + Name: naming.TLSSecretName(component, name), }, Key: corev1.TLSPrivateKeyKey, }, @@ -78,8 +90,8 @@ func buildServiceMonitor(params manifestutils.Params, component string, port str Kind: monitoringv1.ServiceMonitorsKind, }, ObjectMeta: metav1.ObjectMeta{ - Namespace: tempo.Namespace, - Name: naming.Name(component, tempo.Name), + Namespace: namespace, + Name: naming.Name(component, name), Labels: labels, }, Spec: monitoringv1.ServiceMonitorSpec{ @@ -103,7 +115,7 @@ func buildServiceMonitor(params manifestutils.Params, component string, port str }, }}, NamespaceSelector: monitoringv1.NamespaceSelector{ - MatchNames: []string{tempo.Namespace}, + MatchNames: []string{namespace}, }, Selector: metav1.LabelSelector{ MatchLabels: labels,