github.com/verrazzano/verrazzano@v1.7.1/tests/e2e/verify-install/promstack/promstack_test.go (about)

     1  // Copyright (c) 2022, 2023, 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 promstack
     5  
     6  import (
     7  	"fmt"
     8  	"time"
     9  
    10  	"github.com/Jeffail/gabs/v2"
    11  	"github.com/verrazzano/verrazzano/pkg/vzcr"
    12  	vzbeta1 "github.com/verrazzano/verrazzano/platform-operator/apis/verrazzano/v1beta1"
    13  	"github.com/verrazzano/verrazzano/platform-operator/controllers/verrazzano/component/appoper"
    14  	"github.com/verrazzano/verrazzano/platform-operator/controllers/verrazzano/component/authproxy"
    15  	"github.com/verrazzano/verrazzano/platform-operator/controllers/verrazzano/component/clusteroperator"
    16  	"github.com/verrazzano/verrazzano/platform-operator/controllers/verrazzano/component/istio"
    17  	"github.com/verrazzano/verrazzano/platform-operator/controllers/verrazzano/component/nginx"
    18  	"github.com/verrazzano/verrazzano/platform-operator/controllers/verrazzano/component/opensearch"
    19  	"github.com/verrazzano/verrazzano/platform-operator/controllers/verrazzano/component/prometheus/adapter"
    20  	"github.com/verrazzano/verrazzano/platform-operator/controllers/verrazzano/component/prometheus/kubestatemetrics"
    21  	"github.com/verrazzano/verrazzano/platform-operator/controllers/verrazzano/component/prometheus/nodeexporter"
    22  	prometheusOperator "github.com/verrazzano/verrazzano/platform-operator/controllers/verrazzano/component/prometheus/operator"
    23  	"github.com/verrazzano/verrazzano/platform-operator/controllers/verrazzano/component/prometheus/pushgateway"
    24  	"github.com/verrazzano/verrazzano/platform-operator/controllers/verrazzano/component/thanos"
    25  	"github.com/verrazzano/verrazzano/platform-operator/controllers/verrazzano/component/vmo"
    26  
    27  	. "github.com/onsi/ginkgo/v2"
    28  	. "github.com/onsi/gomega"
    29  	"github.com/verrazzano/verrazzano/pkg/k8sutil"
    30  	"github.com/verrazzano/verrazzano/platform-operator/constants"
    31  	"github.com/verrazzano/verrazzano/tests/e2e/pkg"
    32  	"github.com/verrazzano/verrazzano/tests/e2e/pkg/test/framework"
    33  	corev1 "k8s.io/api/core/v1"
    34  	k8serrors "k8s.io/apimachinery/pkg/api/errors"
    35  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    36  )
    37  
    38  const (
    39  	waitTimeout                     = 15 * time.Minute
    40  	pollingInterval                 = 10 * time.Second
    41  	prometheusTLSSecret             = "prometheus-operator-kube-p-admission"
    42  	prometheusOperatorDeployment    = "prometheus-operator-kube-p-operator"
    43  	prometheusOperatorContainerName = "kube-prometheus-stack"
    44  	thanosSidecarContainerName      = "thanos-sidecar"
    45  	overrideConfigMapSecretName     = "test-overrides"
    46  	overrideKey                     = "override"
    47  	overrideValue                   = "true"
    48  )
    49  
    50  type enabledComponents struct {
    51  	podName       string
    52  	componentName string
    53  	target        string
    54  }
    55  
    56  var (
    57  	vz *vzbeta1.Verrazzano
    58  
    59  	promStackEnabledComponents = []enabledComponents{
    60  		{podName: "prometheus-adapter", componentName: adapter.ComponentName},
    61  		{podName: "prometheus-operator-kube-p-operator", componentName: prometheusOperator.ComponentName},
    62  		{podName: "kube-state-metrics", componentName: kubestatemetrics.ComponentName},
    63  		{podName: "prometheus-pushgateway", componentName: pushgateway.ComponentName},
    64  		{podName: "prometheus-node-exporter", componentName: nodeexporter.ComponentName},
    65  		{podName: "prometheus-prometheus-operator-kube-p-prometheus", componentName: prometheusOperator.ComponentName},
    66  	}
    67  	promOperatorCrds = []string{
    68  		"alertmanagerconfigs.monitoring.coreos.com",
    69  		"alertmanagers.monitoring.coreos.com",
    70  		"podmonitors.monitoring.coreos.com",
    71  		"probes.monitoring.coreos.com",
    72  		"prometheuses.monitoring.coreos.com",
    73  		"prometheusrules.monitoring.coreos.com",
    74  		"servicemonitors.monitoring.coreos.com",
    75  		"thanosrulers.monitoring.coreos.com",
    76  	}
    77  	imagePrefix              = pkg.GetImagePrefix()
    78  	expectedPromOperatorArgs = []string{
    79  		"--prometheus-default-base-image=" + imagePrefix + "/verrazzano/prometheus",
    80  		"--alertmanager-default-base-image=" + imagePrefix + "/verrazzano/alertmanager",
    81  	}
    82  	labelMatch      = map[string]string{overrideKey: overrideValue}
    83  	isMinVersion140 bool
    84  
    85  	scrapeTargets = []enabledComponents{
    86  		{target: "serviceMonitor/verrazzano-monitoring/prometheus-node-exporter", componentName: nodeexporter.ComponentName},
    87  		{target: "serviceMonitor/verrazzano-monitoring/prometheus-operator-kube-p-apiserver", componentName: prometheusOperator.ComponentName},
    88  		{target: "serviceMonitor/verrazzano-monitoring/prometheus-operator-kube-p-coredns", componentName: prometheusOperator.ComponentName},
    89  		{target: "serviceMonitor/verrazzano-monitoring/prometheus-operator-kube-p-kubelet", componentName: prometheusOperator.ComponentName},
    90  		{target: "serviceMonitor/verrazzano-monitoring/prometheus-operator-kube-p-operator", componentName: prometheusOperator.ComponentName},
    91  		//{target: "serviceMonitor/verrazzano-monitoring/prometheus-operator-kube-p-prometheus", componentName: prometheusOperator.ComponentName},
    92  		{target: "serviceMonitor/verrazzano-monitoring/authproxy", componentName: authproxy.ComponentName},
    93  		//{target: "serviceMonitor/verrazzano-monitoring/fluentd", componentName: fluentd.ComponentName},
    94  		{target: "serviceMonitor/verrazzano-monitoring/kube-state-metrics", componentName: kubestatemetrics.ComponentName},
    95  		{target: "serviceMonitor/verrazzano-monitoring/opensearch", componentName: opensearch.ComponentName},
    96  		{target: "serviceMonitor/verrazzano-monitoring/pilot", componentName: istio.ComponentName},
    97  		{target: "serviceMonitor/verrazzano-monitoring/verrazzano-application-operator", componentName: appoper.ComponentName},
    98  		{target: "serviceMonitor/verrazzano-monitoring/verrazzano-cluster-operator", componentName: clusteroperator.ComponentName},
    99  		{target: "serviceMonitor/verrazzano-monitoring/verrazzano-monitoring-operator", componentName: vmo.ComponentName},
   100  		{target: "serviceMonitor/verrazzano-monitoring/thanos-query-frontend", componentName: thanos.ComponentName},
   101  		{target: "serviceMonitor/verrazzano-monitoring/thanos-query", componentName: thanos.ComponentName},
   102  	}
   103  )
   104  
   105  var t = framework.NewTestFramework("promstack")
   106  
   107  func listEnabledComponents() []string {
   108  	var enabledPods []string
   109  	for _, component := range promStackEnabledComponents {
   110  		if vzcr.IsComponentStatusEnabled(vz, component.componentName) {
   111  			enabledPods = append(enabledPods, component.podName)
   112  		}
   113  	}
   114  	return enabledPods
   115  }
   116  
   117  func listDisabledComponents() []string {
   118  	var disabledPods []string
   119  	for _, component := range promStackEnabledComponents {
   120  		if !vzcr.IsComponentStatusEnabled(vz, component.componentName) {
   121  			disabledPods = append(disabledPods, component.podName)
   122  		}
   123  	}
   124  	return disabledPods
   125  }
   126  
   127  // areOverridesEnabled - return true if the override value prometheusOperator.podAnnotations.override
   128  // is present and set to "true"
   129  func areOverridesEnabled() bool {
   130  	kubeconfigPath := getKubeConfigOrAbort()
   131  	vz, err := pkg.GetVerrazzanoInstallResourceInClusterV1beta1(kubeconfigPath)
   132  	if err != nil {
   133  		AbortSuite(fmt.Sprintf("Failed to get vz resource in cluster: %s", err.Error()))
   134  		return false
   135  	}
   136  
   137  	promOper := vz.Spec.Components.PrometheusOperator
   138  	if promOper == nil || len(promOper.ValueOverrides) == 0 {
   139  		return false
   140  	}
   141  
   142  	// The overrides are enabled if the override value prometheusOperator.podAnnotations.override = "true"
   143  	for _, override := range promOper.ValueOverrides {
   144  		if override.Values != nil {
   145  			jsonString, err := gabs.ParseJSON(override.Values.Raw)
   146  			if err != nil {
   147  				return false
   148  			}
   149  			if container := jsonString.Path("prometheusOperator.podAnnotations.override"); container != nil {
   150  				if val, ok := container.Data().(string); ok {
   151  					return "true" == string(val)
   152  				}
   153  			}
   154  		}
   155  	}
   156  
   157  	return false
   158  }
   159  
   160  // isThanosSidecarEnabled returns true if the Helm override for enabling the Thanos sidecar is found
   161  func isThanosSidecarEnabled() (bool, error) {
   162  	kubeconfigPath := getKubeConfigOrAbort()
   163  	vz, err := pkg.GetVerrazzanoInstallResourceInClusterV1beta1(kubeconfigPath)
   164  	if err != nil {
   165  		t.Logs.Errorf("Failed to get installed Verrazzano resource in the cluster: %v", err)
   166  		return false, err
   167  	}
   168  
   169  	if vz.Spec.Components.PrometheusOperator == nil {
   170  		return false, nil
   171  	}
   172  	overrides := vz.Spec.Components.PrometheusOperator.ValueOverrides
   173  
   174  	for _, override := range overrides {
   175  		if override.Values == nil {
   176  			continue
   177  		}
   178  		vals, err := gabs.ParseJSON(override.Values.Raw)
   179  		if err != nil {
   180  			t.Logs.Errorf("Failed to parse the Values Override JSON: %v", err)
   181  			return false, err
   182  		}
   183  
   184  		integration, ok := vals.Path("prometheus.thanos.integration").Data().(string)
   185  		t.Logs.Debugf("Integration Override: %s", integration)
   186  		if ok && integration == "sidecar" {
   187  			return true, nil
   188  		}
   189  	}
   190  	t.Logs.Infof("Thanos Sidecar override not found in the Prometheus Operator overrides.")
   191  	return false, nil
   192  }
   193  
   194  // isThanosSidecarInstalledIfEnabled returns true if Thanos is disabled, or if Thanos is enabled and the sidecar container is found
   195  func isThanosSidecarInstalledIfEnabled() (bool, error) {
   196  	enabled, err := isThanosSidecarEnabled()
   197  	if err != nil {
   198  		return false, err
   199  	}
   200  	if !enabled {
   201  		return true, nil
   202  	}
   203  
   204  	promPod, err := pkg.GetPodsFromSelector(&metav1.LabelSelector{
   205  		MatchLabels: map[string]string{"app.kubernetes.io/name": "prometheus"},
   206  	}, constants.VerrazzanoMonitoringNamespace)
   207  	if err != nil {
   208  		t.Logs.Errorf("Failed to get the Prometheus pod from the cluster: %v", err)
   209  		return false, err
   210  	}
   211  
   212  	for _, pod := range promPod {
   213  		for _, container := range pod.Spec.Containers {
   214  			if container.Name == thanosSidecarContainerName {
   215  				return true, nil
   216  			}
   217  		}
   218  	}
   219  	return false, nil
   220  }
   221  
   222  // 'It' Wrapper to only run spec if the Prometheus Stack is supported on the current Verrazzano version
   223  func WhenPromStackInstalledIt(description string, f func()) {
   224  	kubeconfigPath := getKubeConfigOrAbort()
   225  	supported, err := pkg.IsVerrazzanoMinVersion("1.3.0", kubeconfigPath)
   226  	if err != nil {
   227  		t.It(description, func() {
   228  			Fail(fmt.Sprintf("Failed to check Verrazzano version 1.3.0: %s", err.Error()))
   229  		})
   230  	}
   231  	if supported {
   232  		t.It(description, f)
   233  	} else {
   234  		t.Logs.Infof("Skipping check '%v', the Prometheus stack is not supported", description)
   235  	}
   236  }
   237  
   238  func getKubeConfigOrAbort() string {
   239  	kubeconfigPath, err := k8sutil.GetKubeConfigLocation()
   240  	if err != nil {
   241  		AbortSuite(fmt.Sprintf("Failed to get default kubeconfig path: %s", err.Error()))
   242  	}
   243  	return kubeconfigPath
   244  }
   245  
   246  var beforeSuite = t.BeforeSuiteFunc(func() {
   247  	var err error
   248  	kubeconfigPath := getKubeConfigOrAbort()
   249  	isMinVersion140, err = pkg.IsVerrazzanoMinVersion("1.4.0", kubeconfigPath)
   250  	if err != nil {
   251  		Fail(err.Error())
   252  	}
   253  
   254  	vz, err = pkg.GetVerrazzanoInstallResourceInClusterV1beta1(kubeconfigPath)
   255  	if err != nil {
   256  		AbortSuite(fmt.Sprintf("Failed to get installed Verrazzano resource in the cluster: %v", err))
   257  	}
   258  })
   259  
   260  var _ = BeforeSuite(beforeSuite)
   261  
   262  var _ = t.Describe("Prometheus Stack", Label("f:platform-lcm.install"), func() {
   263  	t.Context("after successful installation", func() {
   264  		// GIVEN the Prometheus stack is installed
   265  		// WHEN we check to make sure the namespace exists
   266  		// THEN we successfully find the namespace
   267  		WhenPromStackInstalledIt("should have a verrazzano-monitoring namespace", func() {
   268  			Eventually(func() (bool, error) {
   269  				if len(listEnabledComponents()) == 0 {
   270  					return true, nil
   271  				}
   272  				return pkg.DoesNamespaceExist(constants.VerrazzanoMonitoringNamespace)
   273  			}, waitTimeout, pollingInterval).Should(BeTrue())
   274  		})
   275  
   276  		// GIVEN the Prometheus stack is installed
   277  		// WHEN we check to make sure the pods are running
   278  		// THEN we successfully find the running pods
   279  		WhenPromStackInstalledIt("should have running pods", func() {
   280  			promStackPodsRunning := func() (bool, error) {
   281  				enabledPods := listEnabledComponents()
   282  				result, err := pkg.PodsRunning(constants.VerrazzanoMonitoringNamespace, enabledPods)
   283  				if err != nil {
   284  					t.Logs.Errorf("Pods %v is not running in the namespace: %v, error: %v", enabledPods, constants.VerrazzanoMonitoringNamespace, err)
   285  				}
   286  				return result, err
   287  			}
   288  			Eventually(promStackPodsRunning, waitTimeout, pollingInterval).Should(BeTrue())
   289  		})
   290  
   291  		// GIVEN the Prometheus stack is installed
   292  		// WHEN we check to make sure any disabled pods are NOT running
   293  		// THEN we successfully find the running pods
   294  		WhenPromStackInstalledIt("Disabled pods not running", func() {
   295  			promStackPodsRunning := func() bool {
   296  				disabledPods := listDisabledComponents()
   297  				t.Logs.Debugf("Checking disabled component pods %v", disabledPods)
   298  				result, err := pkg.PodsNotRunning(constants.VerrazzanoMonitoringNamespace, disabledPods)
   299  				if err != nil {
   300  					AbortSuite(fmt.Sprintf("Unexpected error occurred checking for pods %v in namespace: %v, error: %v", disabledPods, constants.VerrazzanoMonitoringNamespace, err))
   301  				}
   302  				return result
   303  			}
   304  			Eventually(promStackPodsRunning, waitTimeout, pollingInterval).Should(BeTrue())
   305  		})
   306  
   307  		// GIVEN the Prometheus stack is installed
   308  		// WHEN we check to make sure the operator overrides get applied
   309  		// THEN we see that the correct pod labels and annotations exist
   310  		WhenPromStackInstalledIt("should have Prometheus Operator pod labeled and annotated", func() {
   311  			promStackPodsRunning := func() bool {
   312  				if vzcr.IsComponentStatusEnabled(vz, prometheusOperator.ComponentName) && areOverridesEnabled() {
   313  					_, err := pkg.GetConfigMap(overrideConfigMapSecretName, constants.DefaultNamespace)
   314  					if err == nil {
   315  						pods, err := pkg.GetPodsFromSelector(&metav1.LabelSelector{
   316  							MatchLabels: labelMatch,
   317  						}, constants.VerrazzanoMonitoringNamespace)
   318  						if err != nil {
   319  							AbortSuite(fmt.Sprintf("Label override not found for the Prometheus Operator pod in namespace %s: %v", constants.VerrazzanoMonitoringNamespace, err))
   320  						}
   321  						foundAnnotation := false
   322  						for _, pod := range pods {
   323  							if val, ok := pod.Annotations[overrideKey]; ok && val == overrideValue {
   324  								foundAnnotation = true
   325  							}
   326  						}
   327  						return len(pods) == 1 && foundAnnotation
   328  					} else if !k8serrors.IsNotFound(err) {
   329  						AbortSuite(fmt.Sprintf("Error retrieving the override ConfigMap: %v", err))
   330  					}
   331  				}
   332  				return true
   333  			}
   334  			Eventually(promStackPodsRunning, waitTimeout, pollingInterval).Should(BeTrue())
   335  		})
   336  
   337  		// GIVEN the Prometheus stack is installed
   338  		// WHEN we check to make sure the default images are from Verrazzano
   339  		// THEN we see that the arguments are correctly populated
   340  		WhenPromStackInstalledIt(fmt.Sprintf("should have the correct default image arguments: %s, %s", expectedPromOperatorArgs[0], expectedPromOperatorArgs[1]), func() {
   341  			promStackPodsRunning := func() (bool, error) {
   342  				if vzcr.IsComponentStatusEnabled(vz, prometheusOperator.ComponentName) {
   343  					return pkg.ContainerHasExpectedArgs(constants.VerrazzanoMonitoringNamespace, prometheusOperatorDeployment, prometheusOperatorContainerName, expectedPromOperatorArgs)
   344  				}
   345  				return true, nil
   346  			}
   347  			Eventually(promStackPodsRunning, waitTimeout, pollingInterval).Should(BeTrue())
   348  		})
   349  
   350  		WhenPromStackInstalledIt("should have the correct Prometheus Operator CRDs", func() {
   351  			verifyCRDList := func() (bool, error) {
   352  				if vzcr.IsComponentStatusEnabled(vz, prometheusOperator.ComponentName) {
   353  					for _, crd := range promOperatorCrds {
   354  						exists, err := pkg.DoesCRDExist(crd)
   355  						if err != nil || !exists {
   356  							return exists, err
   357  						}
   358  					}
   359  					return true, nil
   360  				}
   361  				return true, nil
   362  			}
   363  			Eventually(verifyCRDList, waitTimeout, pollingInterval).Should(BeTrue())
   364  		})
   365  
   366  		// GIVEN the Prometheus stack is installed
   367  		// WHEN the Prometheus Instance has Thanos enabled
   368  		// THEN we see that the Thanos sidecar exists
   369  		WhenPromStackInstalledIt("should have the Thanos sidecar if enabled", func() {
   370  			Eventually(func() (bool, error) {
   371  				return isThanosSidecarInstalledIfEnabled()
   372  			}, waitTimeout, pollingInterval).Should(BeTrue())
   373  		})
   374  
   375  		WhenPromStackInstalledIt("should have the TLS secret", func() {
   376  			Eventually(func() bool {
   377  				if vzcr.IsComponentStatusEnabled(vz, prometheusOperator.ComponentName) {
   378  					return pkg.SecretsCreated(constants.VerrazzanoMonitoringNamespace, prometheusTLSSecret)
   379  				}
   380  				return true
   381  			}, waitTimeout, pollingInterval).Should(BeTrue())
   382  		})
   383  
   384  		WhenPromStackInstalledIt("has affinity configured on prometheus pods", func() {
   385  			if isMinVersion140 {
   386  				var pods []corev1.Pod
   387  				Eventually(func() error {
   388  					var err error
   389  					selector := map[string]string{
   390  						"prometheus":             "prometheus-operator-kube-p-prometheus",
   391  						"app.kubernetes.io/name": "prometheus",
   392  					}
   393  					pods, err = pkg.GetPodsFromSelector(&metav1.LabelSelector{MatchLabels: selector}, constants.VerrazzanoMonitoringNamespace)
   394  					return err
   395  				}, waitTimeout, pollingInterval).ShouldNot(HaveOccurred())
   396  
   397  				// Check the affinity configuration. Verify only a pod anti-affinity definition exists.
   398  				for _, pod := range pods {
   399  					affinity := pod.Spec.Affinity
   400  					Expect(affinity).ToNot(BeNil())
   401  					Expect(affinity.PodAffinity).To(BeNil())
   402  					Expect(affinity.NodeAffinity).To(BeNil())
   403  					Expect(affinity.PodAntiAffinity).ToNot(BeNil())
   404  					Expect(len(affinity.PodAntiAffinity.PreferredDuringSchedulingIgnoredDuringExecution)).To(Equal(1))
   405  				}
   406  			} else {
   407  				t.Logs.Info("Skipping check, Verrazzano minimum version is not v1.4.0")
   408  			}
   409  		})
   410  
   411  		WhenPromStackInstalledIt("should have scrape targets healthy for installed components", func() {
   412  			if isMinVersion140 && !pkg.IsManagedClusterProfile() {
   413  				verifyScrapeTargets := func() (bool, error) {
   414  					var targets []string
   415  					for _, scrapeTarget := range scrapeTargets {
   416  						if vzcr.IsComponentStatusEnabled(vz, scrapeTarget.componentName) {
   417  							targets = append(targets, scrapeTarget.target)
   418  						}
   419  					}
   420  					if vzcr.IsComponentStatusEnabled(vz, prometheusOperator.ComponentName) {
   421  						if !vzcr.IsComponentStatusEnabled(vz, nginx.ComponentName) {
   422  							return pkg.ScrapeTargetsHealthyFromExec(targets)
   423  						}
   424  						return pkg.ScrapeTargetsHealthy(targets)
   425  					}
   426  					return true, nil
   427  				}
   428  				Eventually(verifyScrapeTargets, waitTimeout, pollingInterval).Should(BeTrue())
   429  			} else {
   430  				t.Logs.Info("Skipping check, Verrazzano minimum version is not v1.4.0 or is managed cluster")
   431  			}
   432  		})
   433  	})
   434  })