@@ -23,6 +23,7 @@ import (
2323 "reflect"
2424 "regexp"
2525
26+ corev1 "k8s.io/api/core/v1"
2627 discoveryv1 "k8s.io/api/discovery/v1"
2728 k8s_errors "k8s.io/apimachinery/pkg/api/errors"
2829 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@@ -52,6 +53,7 @@ import (
5253 infranetworkv1 "github.com/openstack-k8s-operators/infra-operator/apis/network/v1beta1"
5354 telemetryv1 "github.com/openstack-k8s-operators/telemetry-operator/api/v1beta1"
5455 ceilometer "github.com/openstack-k8s-operators/telemetry-operator/pkg/ceilometer"
56+ "github.com/openstack-k8s-operators/telemetry-operator/pkg/dashboards"
5557 metricstorage "github.com/openstack-k8s-operators/telemetry-operator/pkg/metricstorage"
5658 monv1 "github.com/rhobs/obo-prometheus-operator/pkg/apis/monitoring/v1"
5759 obov1 "github.com/rhobs/observability-operator/pkg/apis/monitoring/v1alpha1"
@@ -79,6 +81,7 @@ func (r *MetricStorageReconciler) GetLogger(ctx context.Context) logr.Logger {
7981//+kubebuilder:rbac:groups=monitoring.rhobs,resources=monitoringstacks,verbs=get;list;watch;create;update;patch;delete
8082//+kubebuilder:rbac:groups=monitoring.rhobs,resources=servicemonitors,verbs=get;list;watch;create;update;patch;delete
8183//+kubebuilder:rbac:groups=monitoring.rhobs,resources=scrapeconfigs,verbs=get;list;watch;create;update;patch;delete
84+ //+kubebuilder:rbac:groups=monitoring.rhobs,resources=prometheusrules,verbs=get;list;watch;create;update;patch;delete
8285//+kubebuilder:rbac:groups=network.openstack.org,resources=ipsets,verbs=get;list;watch
8386//+kubebuilder:rbac:groups=dataplane.openstack.org,resources=openstackdataplanenodesets,verbs=get;list;watch
8487//+kubebuilder:rbac:groups=dataplane.openstack.org,resources=openstackdataplaneservices,verbs=get;list;watch
@@ -148,6 +151,9 @@ func (r *MetricStorageReconciler) Reconcile(ctx context.Context, req ctrl.Reques
148151 condition .UnknownCondition (telemetryv1 .ServiceMonitorReadyCondition , condition .InitReason , telemetryv1 .ServiceMonitorReadyInitMessage ),
149152 condition .UnknownCondition (telemetryv1 .ScrapeConfigReadyCondition , condition .InitReason , telemetryv1 .ScrapeConfigReadyInitMessage ),
150153 condition .UnknownCondition (telemetryv1 .NodeSetReadyCondition , condition .InitReason , telemetryv1 .NodeSetReadyInitMessage ),
154+ condition .UnknownCondition (telemetryv1 .DashboardPrometheusRuleReadyCondition , condition .InitReason , telemetryv1 .DashboardPrometheusRuleReadyInitMessage ),
155+ condition .UnknownCondition (telemetryv1 .DashboardDatasourceReadyCondition , condition .InitReason , telemetryv1 .DashboardDatasourceReadyInitMessage ),
156+ condition .UnknownCondition (telemetryv1 .DashboardDefinitionReadyCondition , condition .InitReason , telemetryv1 .DashboardDefinitionReadyInitMessage ),
151157 )
152158
153159 instance .Status .Conditions .Init (& cl )
@@ -375,6 +381,121 @@ func (r *MetricStorageReconciler) reconcileNormal(
375381 }
376382 instance .Status .Conditions .MarkTrue (telemetryv1 .ScrapeConfigReadyCondition , condition .ReadyMessage )
377383
384+ if ! instance .Spec .MonitoringStack .DashboardsEnabled {
385+ instance .Status .Conditions .MarkTrue (telemetryv1 .DashboardPrometheusRuleReadyCondition , telemetryv1 .DashboardsNotEnabledMessage )
386+ instance .Status .Conditions .MarkTrue (telemetryv1 .DashboardDatasourceReadyCondition , telemetryv1 .DashboardsNotEnabledMessage )
387+ instance .Status .Conditions .MarkTrue (telemetryv1 .DashboardDefinitionReadyCondition , telemetryv1 .DashboardsNotEnabledMessage )
388+ } else {
389+ // Deploy PrometheusRule for dashboards
390+ err = r .ensureWatches (ctx , "prometheusrules.monitoring.rhobs" , & monv1.PrometheusRule {}, eventHandler )
391+ if err != nil {
392+ instance .Status .Conditions .MarkFalse (telemetryv1 .DashboardPrometheusRuleReadyCondition ,
393+ condition .Reason ("Can't own PrometheusRule resource" ),
394+ condition .SeverityError ,
395+ telemetryv1 .DashboardPrometheusRuleUnableToOwnMessage , err )
396+ Log .Info ("Can't own PrometheusRule resource" )
397+ return ctrl.Result {RequeueAfter : telemetryv1 .PauseBetweenWatchAttempts }, nil
398+ }
399+ prometheusRule := & monv1.PrometheusRule {
400+ ObjectMeta : metav1.ObjectMeta {
401+ Name : instance .Name ,
402+ Namespace : instance .Namespace ,
403+ },
404+ }
405+ op , err = controllerutil .CreateOrPatch (ctx , r .Client , prometheusRule , func () error {
406+ ruleLabels := map [string ]string {
407+ common .AppSelector : telemetryv1 .DefaultServiceName ,
408+ }
409+ desiredPrometheusRule := metricstorage .DashboardPrometheusRule (instance , serviceLabels , ruleLabels )
410+ desiredPrometheusRule .Spec .DeepCopyInto (& prometheusRule .Spec )
411+ prometheusRule .ObjectMeta .Labels = desiredPrometheusRule .ObjectMeta .Labels
412+ err = controllerutil .SetControllerReference (instance , prometheusRule , r .Scheme )
413+ return err
414+ })
415+ if err != nil {
416+ return ctrl.Result {}, err
417+ }
418+ if op != controllerutil .OperationResultNone {
419+ Log .Info (fmt .Sprintf ("Prometheus Rules %s successfully changed - operation: %s" , prometheusRule .Name , string (op )))
420+ }
421+ instance .Status .Conditions .MarkTrue (telemetryv1 .DashboardPrometheusRuleReadyCondition , condition .ReadyMessage )
422+
423+ // Deploy Configmap for Console UI Datasource
424+ datasourceName := instance .Namespace + "-" + instance .Name + "-datasource"
425+ datasourceCM := & corev1.ConfigMap {
426+ ObjectMeta : metav1.ObjectMeta {
427+ Name : datasourceName ,
428+ Namespace : "console-dashboards" ,
429+ },
430+ }
431+ dataSourceSuccess := false
432+ op , err = controllerutil .CreateOrPatch (ctx , r .Client , datasourceCM , func () error {
433+ datasourceCM .ObjectMeta .Labels = map [string ]string {
434+ "console.openshift.io/dashboard-datasource" : "true" ,
435+ }
436+ datasourceCM .Data = map [string ]string {
437+ // WARNING: The lines below MUST be indented with spaces instead of tabs
438+ "dashboard-datasource.yaml" : `
439+ kind: "Datasource"
440+ metadata:
441+ name: "` + datasourceName + `"
442+ spec:
443+ plugin:
444+ kind: "PrometheusDatasource"
445+ spec:
446+ direct_url: "http://prometheus-operated.` + instance .Namespace + ".svc.cluster.local:9090\" " ,
447+ }
448+ return nil
449+ })
450+ if err != nil {
451+ Log .Error (err , "Failed to update Console UI Datasource ConfigMap %s - operation: %s" , datasourceCM .Name , string (op ))
452+ instance .Status .Conditions .MarkFalse (telemetryv1 .DashboardDatasourceReadyCondition ,
453+ condition .Reason ("Can't create Console UI Datasource ConfigMap" ),
454+ condition .SeverityError ,
455+ telemetryv1 .DashboardDatasourceFailedMessage , err )
456+ } else {
457+ dataSourceSuccess = true
458+ instance .Status .Conditions .MarkTrue (telemetryv1 .DashboardDatasourceReadyCondition , condition .ReadyMessage )
459+ }
460+ if op != controllerutil .OperationResultNone {
461+ Log .Info (fmt .Sprintf ("Console UI Datasource ConfigMap %s successfully changed - operation: %s" , datasourceCM .Name , string (op )))
462+ }
463+
464+ // Deploy ConfigMaps for dashboards
465+ // NOTE: Dashboards installed without the custom datasource will default to the openshift-monitoring prometheus causing unexpected results
466+ if dataSourceSuccess {
467+ dashboardCMs := map [string ]* corev1.ConfigMap {
468+ "grafana-dashboard-openstack-cloud" : dashboards .OpenstackCloud (datasourceName ),
469+ "grafana-dashboard-openstack-node" : dashboards .OpenstackNode (datasourceName ),
470+ }
471+
472+ for dashboardName , desiredCM := range dashboardCMs {
473+ dashboardCM := & corev1.ConfigMap {
474+ ObjectMeta : metav1.ObjectMeta {
475+ Name : dashboardName ,
476+ Namespace : "openshift-config-managed" ,
477+ },
478+ }
479+ op , err = controllerutil .CreateOrPatch (ctx , r .Client , dashboardCM , func () error {
480+ dashboardCM .ObjectMeta .Labels = desiredCM .ObjectMeta .Labels
481+ dashboardCM .Data = desiredCM .Data
482+ return nil
483+ })
484+ if err != nil {
485+ Log .Error (err , "Failed to update Dashboard ConfigMap %s - operation: %s" , dashboardCM .Name , string (op ))
486+ instance .Status .Conditions .MarkFalse (telemetryv1 .DashboardDefinitionReadyCondition ,
487+ condition .Reason ("Can't create Console UI Dashboard ConfigMap" ),
488+ condition .SeverityError ,
489+ telemetryv1 .DashboardDefinitionFailedMessage , err )
490+ } else {
491+ instance .Status .Conditions .MarkTrue (telemetryv1 .DashboardDefinitionReadyCondition , condition .ReadyMessage )
492+ }
493+ if op != controllerutil .OperationResultNone {
494+ Log .Info (fmt .Sprintf ("Dashboard ConfigMap %s successfully changed - operation: %s" , dashboardCM .Name , string (op )))
495+ }
496+ }
497+ }
498+ }
378499 Log .Info ("Reconciled Service successfully" )
379500 return ctrl.Result {}, nil
380501}
@@ -547,6 +668,7 @@ func isValidDomain(domain string) bool {
547668func (r * MetricStorageReconciler ) SetupWithManager (mgr ctrl.Manager ) error {
548669 control , err := ctrl .NewControllerManagedBy (mgr ).
549670 For (& telemetryv1.MetricStorage {}).
671+ Owns (& monv1.PrometheusRule {}).
550672 Build (r )
551673 r .Controller = control
552674 return err
0 commit comments