github.com/verrazzano/verrazzano-monitoring-operator@v0.0.30/pkg/vmo/configmap.go (about) 1 // Copyright (C) 2020, 2022, Oracle and/or its affiliates. 2 // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. 3 4 package vmo 5 6 import ( 7 "bytes" 8 "context" 9 "html/template" 10 "strings" 11 12 vmcontrollerv1 "github.com/verrazzano/verrazzano-monitoring-operator/pkg/apis/vmcontroller/v1" 13 "github.com/verrazzano/verrazzano-monitoring-operator/pkg/config" 14 "github.com/verrazzano/verrazzano-monitoring-operator/pkg/metricsexporter" 15 16 "github.com/verrazzano/verrazzano-monitoring-operator/pkg/constants" 17 "github.com/verrazzano/verrazzano-monitoring-operator/pkg/resources" 18 "github.com/verrazzano/verrazzano-monitoring-operator/pkg/resources/configmaps" 19 corev1 "k8s.io/api/core/v1" 20 apierrors "k8s.io/apimachinery/pkg/api/errors" 21 k8serrors "k8s.io/apimachinery/pkg/api/errors" 22 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 23 "k8s.io/apimachinery/pkg/labels" 24 ) 25 26 const ( 27 prometheusOperatorPrometheusHost = "prometheus-operator-kube-p-prometheus.verrazzano-monitoring" 28 datasourceYAMLKey = "datasource.yaml" 29 ) 30 31 // CreateConfigmaps to create all required configmaps for VMI 32 func CreateConfigmaps(controller *Controller, vmo *vmcontrollerv1.VerrazzanoMonitoringInstance) error { 33 metric, metricErr := metricsexporter.GetCounterMetrics(metricsexporter.NamesConfigMap) 34 if metricErr != nil { 35 return metricErr 36 } 37 metric.Inc() 38 39 var configMaps []string 40 41 // Configmap for Grafana dashboard 42 dashboardTemplateMap := map[string]string{"vmo-dashboard-provider.yml": constants.DashboardProviderTmpl} 43 // Only create the CM if it doesnt exist. This will allow us to override the provider file e.g. Verrazzano 44 err := createConfigMapIfDoesntExist(controller, vmo, vmo.Spec.Grafana.DashboardsConfigMap, dashboardTemplateMap) 45 if err != nil { 46 return controller.log.ErrorfNewErr("Failed to create dashboard configmap %s: %v", vmo.Spec.Grafana.DashboardsConfigMap, err) 47 } 48 configMaps = append(configMaps, vmo.Spec.Grafana.DashboardsConfigMap) 49 50 //configmap for grafana datasources 51 replaceMap := map[string]string{constants.GrafanaTmplPrometheusURI: prometheusOperatorPrometheusHost, 52 constants.GrafanaTmplAlertManagerURI: resources.GetMetaName(vmo.Name, config.AlertManager.Name)} 53 dataSourceTemplate, err := asDashboardTemplate(constants.DataSourcesTmpl, replaceMap) 54 if err != nil { 55 return controller.log.ErrorfNewErr("Failed to create dashboard datasource template for VMI %s: %v", vmo.Name, err) 56 } 57 err = createUpdateDatasourcesConfigMap(controller, vmo, vmo.Spec.Grafana.DatasourcesConfigMap, map[string]string{datasourceYAMLKey: dataSourceTemplate}) 58 if err != nil { 59 return controller.log.ErrorfNewErr("Failed to create datasource configmap %s: %v", vmo.Spec.Grafana.DatasourcesConfigMap, err) 60 } 61 configMaps = append(configMaps, vmo.Spec.Grafana.DatasourcesConfigMap) 62 63 // Delete configmaps that shouldn't exist 64 controller.log.Debugf("Deleting unwanted ConfigMaps for VMI %s/%s", vmo.Namespace, vmo.Name) 65 selector := labels.SelectorFromSet(map[string]string{constants.VMOLabel: vmo.Name}) 66 configMapList, err := controller.configMapLister.ConfigMaps(vmo.Namespace).List(selector) 67 if err != nil { 68 return err 69 } 70 for _, configMap := range configMapList { 71 if !contains(configMaps, configMap.Name) { 72 controller.log.Debugf("Deleting config map %s", configMap.Name) 73 err := controller.kubeclientset.CoreV1().ConfigMaps(vmo.Namespace).Delete(context.TODO(), configMap.Name, metav1.DeleteOptions{}) 74 if err != nil { 75 return controller.log.ErrorfNewErr("Failed to delete configmap %s%s: %v", vmo.Namespace, configMap.Name, err) 76 } 77 } 78 } 79 timeMetric, timeErr := metricsexporter.GetTimestampMetrics(metricsexporter.NamesConfigMap) 80 if timeErr != nil { 81 return timeErr 82 } 83 timeMetric.SetLastTime() 84 return nil 85 } 86 87 // createUpdateDatasourcesConfigMap creates or updates the Grafana datasource configmap. If the configmap exists and the Prometheus URL still points 88 // to the legacy VMO-managed Prometheus, then replace the Prometheus URL with the new Prometheus Operator-managed Prometheus URL. 89 func createUpdateDatasourcesConfigMap(controller *Controller, vmo *vmcontrollerv1.VerrazzanoMonitoringInstance, configmapName string, data map[string]string) error { 90 91 existingConfig, err := getConfigMap(controller, vmo.Namespace, configmapName) 92 if err != nil { 93 return err 94 } 95 if existingConfig == nil { 96 configMap := configmaps.NewConfig(vmo, configmapName, data) 97 _, err := controller.kubeclientset.CoreV1().ConfigMaps(vmo.Namespace).Create(context.TODO(), configMap, metav1.CreateOptions{}) 98 if err != nil { 99 return err 100 } 101 return nil 102 } 103 104 // if the datasource still points to the legacy Prometheus instance, update it to point to the new Prometheus Operator-managed Prometheus 105 if ds, found := existingConfig.Data[datasourceYAMLKey]; found { 106 updatedDatasourceStr := strings.Replace(ds, resources.GetMetaName(vmo.Name, config.Prometheus.Name), prometheusOperatorPrometheusHost, 1) 107 if updatedDatasourceStr != ds { 108 controller.log.Infof("Replacing Prometheus URL in existing datasource configmap %s/%s", vmo.Namespace, configmapName) 109 110 existingConfig.Data[datasourceYAMLKey] = updatedDatasourceStr 111 _, err := controller.kubeclientset.CoreV1().ConfigMaps(vmo.Namespace).Update(context.TODO(), existingConfig, metav1.UpdateOptions{}) 112 if err != nil { 113 return err 114 } 115 } 116 } 117 return nil 118 } 119 120 // This function is being called for configmaps which don't modify with spec changes 121 func createConfigMapIfDoesntExist(controller *Controller, vmo *vmcontrollerv1.VerrazzanoMonitoringInstance, configmap string, data map[string]string) error { 122 configMap := configmaps.NewConfig(vmo, configmap, data) 123 _, err := controller.kubeclientset.CoreV1().ConfigMaps(vmo.Namespace).Create(context.TODO(), configMap, metav1.CreateOptions{}) 124 if err != nil && !apierrors.IsAlreadyExists(err) { 125 return err 126 } 127 return nil 128 } 129 130 // asDashboardTemplate replaces `namespace` placehoders in the tmplt with the namespace value 131 func asDashboardTemplate(tmplt string, replaceMap map[string]string) (string, error) { 132 t := template.Must(template.New("dashboard").Parse(tmplt)) 133 buf := &bytes.Buffer{} 134 if err := t.Execute(buf, replaceMap); err != nil { 135 return "", err 136 } 137 138 return buf.String(), nil 139 } 140 141 func getConfigMap(controller *Controller, namespace string, configmapName string) (*corev1.ConfigMap, error) { 142 configMap, err := controller.configMapLister.ConfigMaps(namespace).Get(configmapName) 143 if err != nil { 144 if k8serrors.IsNotFound(err) { 145 return nil, nil 146 } 147 return nil, err 148 } 149 return configMap, nil 150 }