github.com/verrazzano/verrazzano@v1.7.1/tests/e2e/metrics/deploymetrics/deploymetrics_test.go (about) 1 // Copyright (c) 2021, 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 deploymetrics 5 6 import ( 7 "context" 8 "fmt" 9 dump "github.com/verrazzano/verrazzano/tests/e2e/pkg/test/clusterdump" 10 "os" 11 "time" 12 13 "github.com/verrazzano/verrazzano/pkg/k8s/resource" 14 15 . "github.com/onsi/ginkgo/v2" 16 . "github.com/onsi/gomega" 17 promoperapi "github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring/v1" 18 "github.com/verrazzano/verrazzano/pkg/k8sutil" 19 "github.com/verrazzano/verrazzano/tests/e2e/pkg" 20 "github.com/verrazzano/verrazzano/tests/e2e/pkg/test/framework" 21 "github.com/verrazzano/verrazzano/tests/e2e/pkg/test/framework/metrics" 22 v1 "k8s.io/api/core/v1" 23 "k8s.io/apimachinery/pkg/api/errors" 24 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 25 "k8s.io/apimachinery/pkg/runtime" 26 k8sclient "sigs.k8s.io/controller-runtime/pkg/client" 27 ) 28 29 const ( 30 deploymetricsCompYaml = "testdata/deploymetrics/deploymetrics-comp.yaml" 31 deploymetricsCompName = "deploymetrics-deployment" 32 deploymetricsAppYaml = "testdata/deploymetrics/deploymetrics-app.yaml" 33 deploymetricsAppName = "deploymetrics-appconf" 34 skipVerifications = "Skip Verifications" 35 errGenerateSvcMonJobFmt = "Failed to generate the Service Monitor job name: %v" 36 ) 37 38 var ( 39 expectedPodsDeploymetricsApp = []string{"deploymetrics-workload"} 40 generatedNamespace = pkg.GenerateNamespace("deploymetrics") 41 metricsTest pkg.MetricsTest 42 43 waitTimeout = 10 * time.Minute 44 pollingInterval = 30 * time.Second 45 shortPollingInterval = 10 * time.Second 46 shortWaitTimeout = 5 * time.Minute 47 longWaitTimeout = 15 * time.Minute 48 longPollingInterval = 30 * time.Second 49 imagePullWaitTimeout = 40 * time.Minute 50 imagePullPollingInterval = 30 * time.Second 51 52 clusterNameLabel string 53 54 t = framework.NewTestFramework("deploymetrics") 55 ) 56 57 var clusterDump = dump.NewClusterDumpWrapper(t, generatedNamespace) 58 var kubeconfig string 59 var beforeSuite = clusterDump.BeforeSuiteFunc(func() { 60 if !skipDeploy { 61 deployMetricsApplication() 62 } 63 var err error 64 65 kubeconfig, err = getKubeConfigPath() 66 if err != nil { 67 pkg.Log(pkg.Error, fmt.Sprintf("Failed to get the kubeconfig: %v", err)) 68 } 69 clusterNameLabel, err = pkg.GetClusterNameMetricLabel(kubeconfig) 70 if err != nil { 71 pkg.Log(pkg.Error, err.Error()) 72 Fail(err.Error()) 73 } 74 75 // Create a service in VZ versions 1.4.0 and greater, so that a servicemonitor will be generated 76 // by Verrazzano for Prometheus Operator 77 isVzMinVer14, _ := pkg.IsVerrazzanoMinVersion("1.4.0", kubeconfig) 78 if isVzMinVer14 { 79 Eventually(func() error { 80 promJobName, err := getPromJobName() 81 if err != nil { 82 pkg.Log(pkg.Error, fmt.Sprintf(errGenerateSvcMonJobFmt, err)) 83 } 84 serviceName := promJobName 85 err = createService(serviceName) 86 // if this is running post upgrade, we may have already run this test pre-upgrade and 87 // created the service. It is not an error if the service already exists. 88 if err != nil && !errors.IsAlreadyExists(err) { 89 pkg.Log(pkg.Error, fmt.Sprintf("Failed to create the Service for the Service Monitor: %v", err)) 90 return err 91 } 92 return nil 93 }, waitTimeout, pollingInterval).Should(BeNil(), "Expected to be able to create the metrics service") 94 } 95 96 metricsTest, err = pkg.NewMetricsTest(kubeconfig, map[string]string{}) 97 if err != nil { 98 AbortSuite(fmt.Sprintf("Failed to create the Metrics test object: %v", err)) 99 } 100 }) 101 var _ = clusterDump.AfterEach(func() {}) // Dump cluster if spec fails 102 var afterSuite = clusterDump.AfterSuiteFunc(func() { // Dump cluster if aftersuite fails 103 if !skipUndeploy { 104 undeployMetricsApplication() 105 } 106 }) 107 108 var _ = BeforeSuite(beforeSuite) 109 var _ = AfterSuite(afterSuite) 110 111 func deployMetricsApplication() { 112 t.Logs.Info("Deploy DeployMetrics Application") 113 114 t.Logs.Info("Create namespace") 115 start := time.Now() 116 Eventually(func() (*v1.Namespace, error) { 117 nsLabels := map[string]string{ 118 "verrazzano-managed": "true", 119 "istio-injection": istioInjection} 120 return pkg.CreateNamespace(namespace, nsLabels) 121 }, shortWaitTimeout, shortPollingInterval).ShouldNot(BeNil()) 122 123 t.Logs.Info("Create component resource") 124 Eventually(func() error { 125 file, err := pkg.FindTestDataFile(deploymetricsCompYaml) 126 if err != nil { 127 return err 128 } 129 return resource.CreateOrUpdateResourceFromFileInGeneratedNamespace(file, namespace) 130 }, shortWaitTimeout, shortPollingInterval).ShouldNot(HaveOccurred()) 131 132 t.Logs.Info("Create application resource") 133 Eventually(func() error { 134 file, err := pkg.FindTestDataFile(deploymetricsAppYaml) 135 if err != nil { 136 return err 137 } 138 return resource.CreateOrUpdateResourceFromFileInGeneratedNamespace(file, namespace) 139 }, shortWaitTimeout, shortPollingInterval).ShouldNot(HaveOccurred(), "Failed to create DeployMetrics application resource") 140 141 Eventually(func() bool { 142 return pkg.ContainerImagePullWait(namespace, expectedPodsDeploymetricsApp) 143 }, imagePullWaitTimeout, imagePullPollingInterval).Should(BeTrue()) 144 145 t.Logs.Info("Verify deploymetrics-workload pod is running") 146 Eventually(func() bool { 147 result, err := pkg.PodsRunning(namespace, expectedPodsDeploymetricsApp) 148 if err != nil { 149 AbortSuite(fmt.Sprintf("One or more pods are not running in the namespace: %v, error: %v", namespace, err)) 150 } 151 return result 152 }, waitTimeout, pollingInterval).Should(BeTrue()) 153 metrics.Emit(t.Metrics.With("deployment_elapsed_time", time.Since(start).Milliseconds())) 154 } 155 156 func undeployMetricsApplication() { 157 t.Logs.Info("Undeploy DeployMetrics Application") 158 159 t.Logs.Info("Delete application") 160 start := time.Now() 161 Eventually(func() error { 162 file, err := pkg.FindTestDataFile(deploymetricsCompYaml) 163 if err != nil { 164 return err 165 } 166 return resource.DeleteResourceFromFileInGeneratedNamespace(file, namespace) 167 }, shortWaitTimeout, shortPollingInterval).ShouldNot(HaveOccurred()) 168 169 t.Logs.Info("Delete components") 170 Eventually(func() error { 171 file, err := pkg.FindTestDataFile(deploymetricsAppYaml) 172 if err != nil { 173 return err 174 } 175 return resource.DeleteResourceFromFileInGeneratedNamespace(file, namespace) 176 }, shortWaitTimeout, shortPollingInterval).ShouldNot(HaveOccurred()) 177 178 t.Logs.Info("Wait for pods to terminate") 179 Eventually(func() bool { 180 podsNotRunning, _ := pkg.PodsNotRunning(namespace, expectedPodsDeploymetricsApp) 181 return podsNotRunning 182 }, shortWaitTimeout, shortPollingInterval).Should(BeTrue()) 183 184 isVzMinVer14, _ := pkg.IsVerrazzanoMinVersion("1.4.0", kubeconfig) 185 if isVzMinVer14 { 186 Eventually(func() bool { 187 serviceName, err := getPromJobName() 188 if err != nil { 189 pkg.Log(pkg.Error, fmt.Sprintf(errGenerateSvcMonJobFmt, err)) 190 return false 191 } 192 _, err = pkg.GetServiceMonitor(namespace, serviceName) 193 return err != nil 194 }, waitTimeout, pollingInterval).Should(BeTrue(), "Expected Service Monitor to not exist") 195 } 196 t.Logs.Info("Delete namespace") 197 Eventually(func() error { 198 return pkg.DeleteNamespace(namespace) 199 }, longWaitTimeout, longPollingInterval).ShouldNot(HaveOccurred()) 200 201 t.Logs.Info("Wait for Finalizer to be removed") 202 Eventually(func() bool { 203 return pkg.CheckNamespaceFinalizerRemoved(namespace) 204 }, shortWaitTimeout, shortPollingInterval).Should(BeTrue()) 205 206 t.Logs.Info("Waiting for namespace deletion") 207 Eventually(func() bool { 208 _, err := pkg.GetNamespace(namespace) 209 return err != nil && errors.IsNotFound(err) 210 }, longWaitTimeout, longPollingInterval).Should(BeTrue()) 211 metrics.Emit(t.Metrics.With("undeployment_elapsed_time", time.Since(start).Milliseconds())) 212 } 213 214 var _ = t.Describe("DeployMetrics Application test", Label("f:app-lcm.oam"), func() { 215 216 t.Context("for Prometheus Config.", Label("f:observability.monitoring.prom"), func() { 217 t.It("Verify that Prometheus Service Monitor exists", func() { 218 if skipVerify { 219 Skip(skipVerifications) 220 } 221 isVzMinVer14, _ := pkg.IsVerrazzanoMinVersion("1.4.0", kubeconfig) 222 if isVzMinVer14 { 223 serviceName, err := getPromJobName() 224 if err != nil { 225 pkg.Log(pkg.Error, fmt.Sprintf(errGenerateSvcMonJobFmt, err)) 226 } 227 Eventually(func() (*promoperapi.ServiceMonitor, error) { 228 monitor, err := pkg.GetServiceMonitor(namespace, serviceName) 229 if err != nil { 230 pkg.Log(pkg.Error, fmt.Sprintf("Failed to get the Service Monitor from the cluster: %v", err)) 231 } 232 return monitor, err 233 }, waitTimeout, pollingInterval).Should(Not(BeNil()), "Expected to find Service Monitor") 234 } 235 }) 236 }) 237 238 t.Context("Retrieve Prometheus scraped metrics for", Label("f:observability.monitoring.prom"), func() { 239 t.It("App Component", func() { 240 if skipVerify { 241 Skip(skipVerifications) 242 } 243 clusterName, err := getClusterNameForPromQuery() 244 if err != nil { 245 pkg.Log(pkg.Error, fmt.Sprintf("Failed getting the cluster name: %v", err)) 246 } 247 metricLabels := map[string]string{ 248 "app_oam_dev_name": "deploymetrics-appconf", 249 clusterNameLabel: clusterName, 250 } 251 Eventually(func() bool { 252 return metricsTest.MetricsExist("http_server_requests_seconds_count", metricLabels) 253 }, longWaitTimeout, longPollingInterval).Should(BeTrue(), "Expected to find Prometheus scraped metrics for App Component.") 254 }) 255 t.It("App Config", func() { 256 if skipVerify { 257 Skip(skipVerifications) 258 } 259 clusterName, err := getClusterNameForPromQuery() 260 if err != nil { 261 pkg.Log(pkg.Error, fmt.Sprintf("Failed getting the cluster name: %v", err)) 262 } 263 metricLabels := map[string]string{ 264 "app_oam_dev_component": "deploymetrics-deployment", 265 clusterNameLabel: clusterName, 266 } 267 Eventually(func() bool { 268 return metricsTest.MetricsExist("tomcat_sessions_created_sessions_total", metricLabels) 269 }, longWaitTimeout, longPollingInterval).Should(BeTrue(), "Expected to find Prometheus scraped metrics for App Config.") 270 }) 271 }) 272 273 }) 274 275 // Return the cluster name used for the Prometheus query 276 func getClusterNameForPromQuery() (string, error) { 277 if pkg.IsManagedClusterProfile() { 278 var clusterName = os.Getenv("CLUSTER_NAME") 279 pkg.Log(pkg.Info, "This is a managed cluster, returning cluster name - "+clusterName) 280 return clusterName, nil 281 } 282 kubeConfigPath, err := k8sutil.GetKubeConfigLocation() 283 if err != nil { 284 return kubeConfigPath, err 285 } 286 isMinVersion110, err := pkg.IsVerrazzanoMinVersion("1.1.0", kubeConfigPath) 287 if err != nil { 288 return "", err 289 } 290 if isMinVersion110 { 291 return "local", nil 292 } 293 return "", nil 294 } 295 296 func getDefaultKubeConfigPath() (string, error) { 297 kubeConfigPath, err := k8sutil.GetKubeConfigLocation() 298 if err != nil { 299 return kubeConfigPath, err 300 } 301 pkg.Log(pkg.Info, "Default kube config path - "+kubeConfigPath) 302 return kubeConfigPath, nil 303 } 304 305 func getKubeConfigPath() (string, error) { 306 adminKubeConfig, present := os.LookupEnv("ADMIN_KUBECONFIG") 307 if pkg.IsManagedClusterProfile() { 308 if !present { 309 return adminKubeConfig, fmt.Errorf("Environment variable ADMIN_KUBECONFIG is required to run the test") 310 } 311 } else { 312 return getDefaultKubeConfigPath() 313 } 314 return adminKubeConfig, nil 315 } 316 317 func getPromJobName() (string, error) { 318 kubeconfig, err := getKubeConfigPath() 319 if err != nil { 320 return kubeconfig, err 321 } 322 usesServiceMonitor, err := pkg.IsVerrazzanoMinVersion("1.4.0", kubeconfig) 323 if err != nil { 324 return kubeconfig, err 325 } 326 if usesServiceMonitor { 327 return pkg.GetAppServiceMonitorName(namespace, deploymetricsAppName, "deploymetrics-deployment"), nil 328 } 329 // For VZ versions prior to 1.4.0, the job name in prometheus scrape config was of the old format 330 // <app_name>_default_<app_namespace>_<app_component_name> 331 return fmt.Sprintf("%s_default_%s_%s", deploymetricsAppName, generatedNamespace, deploymetricsCompName), nil 332 } 333 334 // create Service creates a service for metrics collection 335 func createService(name string) error { 336 config, err := k8sutil.GetKubeConfig() 337 if err != nil { 338 return err 339 } 340 scheme := runtime.NewScheme() 341 _ = v1.AddToScheme(scheme) 342 cli, err := k8sclient.New(config, k8sclient.Options{Scheme: scheme}) 343 if err != nil { 344 return err 345 } 346 347 svc := v1.Service{ 348 ObjectMeta: metav1.ObjectMeta{ 349 Namespace: namespace, 350 Name: name, 351 }, 352 Spec: v1.ServiceSpec{ 353 Selector: map[string]string{ 354 "app": "deploymetrics", 355 }, 356 Ports: []v1.ServicePort{ 357 { 358 Name: "metrics", 359 Port: 8080, 360 Protocol: "TCP", 361 }, 362 }, 363 }, 364 } 365 return cli.Create(context.TODO(), &svc) 366 }