github.com/verrazzano/verrazzano@v1.7.1/tests/e2e/examples/helidonmetrics/helidon_metrics_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 helidonmetrics
     5  
     6  import (
     7  	"fmt"
     8  	"strings"
     9  	"time"
    10  
    11  	. "github.com/onsi/ginkgo/v2"
    12  	. "github.com/onsi/gomega"
    13  	"github.com/verrazzano/verrazzano/pkg/k8s/resource"
    14  	"github.com/verrazzano/verrazzano/pkg/k8sutil"
    15  	"github.com/verrazzano/verrazzano/tests/e2e/pkg"
    16  	dump "github.com/verrazzano/verrazzano/tests/e2e/pkg/test/clusterdump"
    17  	"github.com/verrazzano/verrazzano/tests/e2e/pkg/test/framework"
    18  	"github.com/verrazzano/verrazzano/tests/e2e/pkg/test/framework/metrics"
    19  	v1 "k8s.io/api/core/v1"
    20  	"k8s.io/apimachinery/pkg/api/errors"
    21  )
    22  
    23  const (
    24  	longWaitTimeout      = 20 * time.Minute
    25  	longPollingInterval  = 20 * time.Second
    26  	shortPollingInterval = 10 * time.Second
    27  	shortWaitTimeout     = 5 * time.Minute
    28  
    29  	ingress        = "hello-helidon-ingress-rule"
    30  	helidonService = "hello-helidon-deployment"
    31  )
    32  
    33  var (
    34  	t                  = framework.NewTestFramework("helidonmetrics")
    35  	generatedNamespace = pkg.GenerateNamespace("helidon-metrics")
    36  	host               = ""
    37  	kubeconfig         string
    38  	metricsTest        pkg.MetricsTest
    39  )
    40  
    41  var beforeSuite = t.BeforeSuiteFunc(func() {
    42  	if !skipDeploy {
    43  		start := time.Now()
    44  		Eventually(func() (*v1.Namespace, error) {
    45  			nsLabels := map[string]string{
    46  				"verrazzano-managed": "true",
    47  				"istio-injection":    istioInjection}
    48  			return pkg.CreateNamespace(namespace, nsLabels)
    49  		}, shortWaitTimeout, shortPollingInterval).ShouldNot(BeNil())
    50  
    51  		Eventually(func() error {
    52  			file, err := pkg.FindTestDataFile("examples/hello-helidon/hello-helidon-comp.yaml")
    53  			if err != nil {
    54  				return err
    55  			}
    56  			return resource.CreateOrUpdateResourceFromFileInGeneratedNamespace(file, namespace)
    57  		}, shortWaitTimeout, shortPollingInterval).ShouldNot(HaveOccurred())
    58  		Eventually(func() error {
    59  			file, err := pkg.FindTestDataFile("examples/hello-helidon/hello-helidon-app.yaml")
    60  			if err != nil {
    61  				return err
    62  			}
    63  			return resource.CreateOrUpdateResourceFromFileInGeneratedNamespace(file, namespace)
    64  		}, shortWaitTimeout, shortPollingInterval).ShouldNot(HaveOccurred())
    65  
    66  		beforeSuitePassed = true
    67  		metrics.Emit(t.Metrics.With("deployment_elapsed_time", time.Since(start).Milliseconds()))
    68  	}
    69  
    70  	t.Logs.Info("Helidon Example: check expected pods are running")
    71  	Eventually(func() bool {
    72  		result, err := pkg.PodsRunning(namespace, expectedPodsHelloHelidon)
    73  		if err != nil {
    74  			AbortSuite(fmt.Sprintf("One or more pods are not running in the namespace: %v, error: %v", namespace, err))
    75  		}
    76  		return result
    77  	}, longWaitTimeout, longPollingInterval).Should(BeTrue(), "Helidon Example Failed to Deploy: Pods are not ready")
    78  
    79  	t.Logs.Info("Helidon Example: check expected Services are running")
    80  	Eventually(func() bool {
    81  		result, err := pkg.DoesServiceExist(namespace, helidonService)
    82  		if err != nil {
    83  			AbortSuite(fmt.Sprintf("Helidon Service %s is not running in the namespace: %v, error: %v", helidonService, namespace, err))
    84  		}
    85  		return result
    86  	}, longWaitTimeout, longPollingInterval).Should(BeTrue(), "Helidon Example Failed to Deploy: Services are not ready")
    87  
    88  	t.Logs.Info("Helidon Example: check expected VirtualService is ready")
    89  	Eventually(func() bool {
    90  		result, err := pkg.DoesVirtualServiceExist(namespace, ingress)
    91  		if err != nil {
    92  			AbortSuite(fmt.Sprintf("Helidon VirtualService %s is not running in the namespace: %v, error: %v", ingress, namespace, err))
    93  		}
    94  		return result
    95  	}, shortWaitTimeout, longPollingInterval).Should(BeTrue(), "Helidon Example Failed to Deploy: VirtualService is not ready")
    96  
    97  	var err error
    98  	// Get the host from the Istio gateway resource.
    99  	start := time.Now()
   100  	t.Logs.Info("Helidon Example: check expected Gateway is ready")
   101  	Eventually(func() (string, error) {
   102  		host, err = k8sutil.GetHostnameFromGateway(namespace, "")
   103  		return host, err
   104  	}, shortWaitTimeout, shortPollingInterval).Should(Not(BeEmpty()), "Helidon Example: Gateway is not ready")
   105  
   106  	kubeconfig, err = k8sutil.GetKubeConfigLocation()
   107  	if err != nil {
   108  		AbortSuite(fmt.Sprintf("Failed to get the Kubeconfig location for the cluster: %v", err))
   109  	}
   110  	metricsTest, err = pkg.NewMetricsTest(kubeconfig, map[string]string{})
   111  	if err != nil {
   112  		AbortSuite(fmt.Sprintf("Failed to create the Metrics test object: %v", err))
   113  	}
   114  
   115  	metrics.Emit(t.Metrics.With("get_host_name_elapsed_time", time.Since(start).Milliseconds()))
   116  
   117  })
   118  
   119  var _ = BeforeSuite(beforeSuite)
   120  
   121  var failed = false
   122  var beforeSuitePassed = false
   123  
   124  var _ = t.AfterEach(func() {
   125  	failed = failed || CurrentSpecReport().Failed()
   126  })
   127  
   128  var afterSuite = t.AfterSuiteFunc(func() {
   129  	if failed || !beforeSuitePassed {
   130  		dump.ExecuteBugReport(namespace)
   131  	}
   132  	if !skipUndeploy {
   133  		start := time.Now()
   134  		// undeploy the application here
   135  		pkg.Log(pkg.Info, "Delete application")
   136  		Eventually(func() error {
   137  			file, err := pkg.FindTestDataFile("tests/e2e/examples/helidonmetrics/testdata/hello-helidon-app-metrics-disabled.yaml")
   138  			if err != nil {
   139  				return err
   140  			}
   141  			return resource.DeleteResourceFromFileInGeneratedNamespace(file, namespace)
   142  		}, shortWaitTimeout, shortPollingInterval).ShouldNot(HaveOccurred())
   143  
   144  		pkg.Log(pkg.Info, "Delete components")
   145  		Eventually(func() error {
   146  			file, err := pkg.FindTestDataFile("examples/hello-helidon/hello-helidon-comp.yaml")
   147  			if err != nil {
   148  				return err
   149  			}
   150  			return resource.DeleteResourceFromFileInGeneratedNamespace(file, namespace)
   151  		}, shortWaitTimeout, shortPollingInterval).ShouldNot(HaveOccurred())
   152  
   153  		pkg.Log(pkg.Info, "Wait for application pods to terminate")
   154  		Eventually(func() bool {
   155  			podsTerminated, _ := pkg.PodsNotRunning(namespace, expectedPodsHelloHelidon)
   156  			return podsTerminated
   157  		}, shortWaitTimeout, shortPollingInterval).Should(BeTrue())
   158  
   159  		pkg.Log(pkg.Info, "Delete namespace")
   160  		Eventually(func() error {
   161  			return pkg.DeleteNamespace(namespace)
   162  		}, shortWaitTimeout, shortPollingInterval).ShouldNot(HaveOccurred())
   163  
   164  		pkg.Log(pkg.Info, "Wait for Finalizer to be removed")
   165  		Eventually(func() bool {
   166  			return pkg.CheckNamespaceFinalizerRemoved(namespace)
   167  		}, shortWaitTimeout, shortPollingInterval).Should(BeTrue())
   168  
   169  		pkg.Log(pkg.Info, "Wait for namespace to be removed")
   170  		Eventually(func() bool {
   171  			_, err := pkg.GetNamespace(namespace)
   172  			return err != nil && errors.IsNotFound(err)
   173  		}, shortWaitTimeout, shortPollingInterval).Should(BeTrue())
   174  
   175  		metrics.Emit(t.Metrics.With("undeployment_elapsed_time", time.Since(start).Milliseconds()))
   176  	}
   177  })
   178  
   179  var _ = AfterSuite(afterSuite)
   180  
   181  var (
   182  	expectedPodsHelloHelidon = []string{"hello-helidon-deployment"}
   183  	waitTimeout              = 10 * time.Minute
   184  	pollingInterval          = 30 * time.Second
   185  )
   186  
   187  var _ = t.Describe("Hello Helidon OAM App test", Label("f:app-lcm.oam",
   188  	"f:app-lcm.helidon-workload"), func() {
   189  
   190  	var host = ""
   191  	var err error
   192  	// Get the host from the Istio gateway resource.
   193  	// GIVEN the Istio gateway for the hello-helidon namespace
   194  	// WHEN GetHostnameFromGateway is called
   195  	// THEN return the host name found in the gateway.
   196  	t.BeforeEach(func() {
   197  		Eventually(func() (string, error) {
   198  			host, err = k8sutil.GetHostnameFromGateway(namespace, "")
   199  			return host, err
   200  		}, shortWaitTimeout, shortPollingInterval).Should(Not(BeEmpty()))
   201  	})
   202  
   203  	// Verify Prometheus scrape targets
   204  	// GIVEN OAM hello-helidon app is deployed
   205  	// WHEN the appconfig is updated with metrics-trait.enabled=false
   206  	// THEN the application metrics target must be removed
   207  	t.Describe("for Metrics.", Label("f:observability.monitoring.prom"), FlakeAttempts(5), func() {
   208  		t.It("MetricsTrait can be disabled", func() {
   209  			pkg.Concurrently(
   210  				func() {
   211  					pkg.Log(pkg.Info, "Checking for scrape target existence")
   212  					Eventually(scrapeTargetExists, shortWaitTimeout, longPollingInterval).Should(BeTrue())
   213  				},
   214  				func() {
   215  					pkg.Log(pkg.Info, "Checking for service monitor existence")
   216  					Eventually(serviceMonitorExists, shortWaitTimeout, longPollingInterval).Should(BeTrue())
   217  				},
   218  			)
   219  			pkg.Log(pkg.Info, "Disabling metrics trait")
   220  			Eventually(func() error {
   221  				file, err := pkg.FindTestDataFile("tests/e2e/examples/helidonmetrics/testdata/hello-helidon-app-metrics-disabled.yaml")
   222  				if err != nil {
   223  					return err
   224  				}
   225  				return resource.CreateOrUpdateResourceFromFileInGeneratedNamespace(file, namespace)
   226  			}, shortWaitTimeout, shortPollingInterval, "Failed to disable metrics").ShouldNot(HaveOccurred())
   227  			pkg.Concurrently(
   228  				func() {
   229  					pkg.Log(pkg.Info, "Checking for scrape target to no longer exist")
   230  					Eventually(scrapeTargetExists, shortWaitTimeout, longPollingInterval).Should(BeFalse())
   231  				},
   232  				func() {
   233  					pkg.Log(pkg.Info, "Checking for service monitor to no longer exist")
   234  					Eventually(serviceMonitorExists, shortWaitTimeout, longPollingInterval).Should(BeFalse())
   235  				},
   236  			)
   237  		})
   238  	})
   239  })
   240  
   241  func serviceMonitorExists() bool {
   242  	smName := pkg.GetAppServiceMonitorName(namespace, "hello-helidon", "hello-helidon-component")
   243  	sm, err := pkg.GetServiceMonitor(namespace, smName)
   244  	if err != nil {
   245  		pkg.Log(pkg.Error, fmt.Sprintf("Failed to get the Service Monitor from the cluster: %v", err))
   246  		return false
   247  	}
   248  	return sm != nil
   249  }
   250  
   251  func scrapeTargetExists() bool {
   252  	promSource, err := pkg.NewPrometheusSource(kubeconfig)
   253  	if err != nil {
   254  		pkg.Log(pkg.Error, fmt.Sprintf("Failed to produce a Prometheus source: %v", err))
   255  		return false
   256  	}
   257  	targets, err := promSource.GetTargets()
   258  	if err != nil {
   259  		pkg.Log(pkg.Error, fmt.Sprintf("Failed to get Prometheus targets from the cluster: %v", err))
   260  		return false
   261  	}
   262  
   263  	for _, t := range targets {
   264  		m := t.(map[string]interface{})
   265  		scrapePool := m["scrapePool"].(string)
   266  		if strings.Contains(scrapePool, namespace) {
   267  			return true
   268  		}
   269  	}
   270  	return false
   271  }