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  }