github.com/verrazzano/verrazzano@v1.7.1/tests/e2e/workloads/weblogic/weblogic_workload_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 weblogic 5 6 import ( 7 "fmt" 8 "net/http" 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 shortWaitTimeout = 10 * time.Minute 25 shortPollingInterval = 10 * time.Second 26 longPollingInterval = 20 * time.Second 27 imagePullWaitTimeout = 40 * time.Minute 28 imagePullPollingInterval = 30 * time.Second 29 30 appConfiguration = "tests/testdata/test-applications/weblogic/hello-weblogic/hello-wls-app.yaml" 31 compConfiguration = "tests/testdata/test-applications/weblogic/hello-weblogic/hello-wls-comp.yaml" 32 33 appURL = "hello/weblogic/greetings/message" 34 welcomeMessage = "Hello WebLogic" 35 36 wlsUser = "weblogic" 37 wlDomain = "hellodomain" 38 wlsAdminServer = "hellodomain-adminserver" 39 trait = "hello-domain-trait" 40 41 helloDomainRepoCreds = "hellodomain-repo-credentials" 42 helloDomainWeblogicCreds = "hellodomain-weblogic-credentials" 43 ) 44 45 var ( 46 t = framework.NewTestFramework("weblogicworkload") 47 generatedNamespace = pkg.GenerateNamespace("hello-wls") 48 expectedPods = []string{wlsAdminServer} 49 host = "" 50 metricsTest pkg.MetricsTest 51 ) 52 53 var beforeSuite = t.BeforeSuiteFunc(func() { 54 if !skipDeploy { 55 start := time.Now() 56 deployWebLogicApp(namespace) 57 metrics.Emit(t.Metrics.With("deployment_elapsed_time", time.Since(start).Milliseconds())) 58 59 t.Logs.Info("Container image pull check") 60 Eventually(func() bool { 61 return pkg.ContainerImagePullWait(namespace, expectedPods) 62 }, imagePullWaitTimeout, imagePullPollingInterval).Should(BeTrue()) 63 } 64 65 t.Logs.Info("WebLogic Application: check expected admin server pod is running") 66 Eventually(func() bool { 67 result, err := pkg.PodsRunning(namespace, expectedPods) 68 if err != nil { 69 AbortSuite(fmt.Sprintf("WebLogic admin server pod is not running in the namespace: %v, error: %v", namespace, err)) 70 } 71 return result 72 }, shortWaitTimeout, longPollingInterval).Should(BeTrue(), "Failed to deploy the WebLogic Application: Admin server pod is not ready") 73 74 t.Logs.Info("WebLogic Application: check expected VirtualService is ready") 75 Eventually(func() bool { 76 result, err := pkg.DoesVirtualServiceExist(namespace, trait) 77 if err != nil { 78 AbortSuite(fmt.Sprintf("WebLogic VirtualService %s is not running in the namespace: %v, error: %v", trait, namespace, err)) 79 } 80 return result 81 }, shortWaitTimeout, longPollingInterval).Should(BeTrue(), "Failed to deploy the WebLogic Application: VirtualService is not ready") 82 83 t.Logs.Info("WebLogic Application: check expected Secrets exist") 84 Eventually(func() bool { 85 result, err := pkg.DoesSecretExist(namespace, helloDomainWeblogicCreds) 86 if err != nil { 87 AbortSuite(fmt.Sprintf("WebLogic Secret %s does not exist in the namespace: %v, error: %v", helloDomainWeblogicCreds, namespace, err)) 88 } 89 return result 90 }, shortWaitTimeout, longPollingInterval).Should(BeTrue(), "Failed to deploy the WebLogic Application: Secret does not exist") 91 92 Eventually(func() bool { 93 result, err := pkg.DoesSecretExist(namespace, helloDomainRepoCreds) 94 if err != nil { 95 AbortSuite(fmt.Sprintf("WebLogic Secret %s does not exist in the namespace: %v, error: %v", helloDomainRepoCreds, namespace, err)) 96 } 97 return result 98 }, shortWaitTimeout, longPollingInterval).Should(BeTrue(), "Failed to deploy the WebLogic Application: Secret does not exist") 99 100 var err error 101 // Get the host from the Istio gateway resource. 102 start := time.Now() 103 t.Logs.Info("WebLogic Application: check expected Gateway is ready") 104 Eventually(func() (string, error) { 105 host, err = k8sutil.GetHostnameFromGateway(namespace, "") 106 return host, err 107 }, shortWaitTimeout, shortPollingInterval).Should(Not(BeEmpty()), "Failed to deploy the WebLogic Application: Gateway is not ready") 108 metrics.Emit(t.Metrics.With("get_host_name_elapsed_time", time.Since(start).Milliseconds())) 109 110 kubeconfigPath, err := k8sutil.GetKubeConfigLocation() 111 metricsTest, err = pkg.NewMetricsTest(kubeconfigPath, map[string]string{}) 112 if err != nil { 113 AbortSuite(fmt.Sprintf("Failed to create the Metrics test object: %v", err)) 114 } 115 116 beforeSuitePassed = true 117 }) 118 119 var _ = BeforeSuite(beforeSuite) 120 121 var failed = false 122 var beforeSuitePassed = false 123 var _ = t.AfterEach(func() { 124 failed = failed || CurrentSpecReport().Failed() 125 }) 126 127 var afterSuite = t.AfterSuiteFunc(func() { 128 if failed || !beforeSuitePassed { 129 dump.CaptureContainerLogs(namespace, wlsAdminServer, "weblogic-server", "/scratch/logs/hello-domain") 130 dump.ExecuteBugReport(namespace) 131 } 132 if !skipUndeploy { 133 undeployWebLogicApp() 134 } 135 }) 136 137 var _ = AfterSuite(afterSuite) 138 139 func deployWebLogicApp(namespace string) { 140 t.Logs.Info("Deploy WebLogic application") 141 wlsPass := pkg.GetRequiredEnvVarOrFail("WEBLOGIC_PSW") 142 regServ := pkg.GetRequiredEnvVarOrFail("OCR_REPO") 143 regUser := pkg.GetRequiredEnvVarOrFail("OCR_CREDS_USR") 144 regPass := pkg.GetRequiredEnvVarOrFail("OCR_CREDS_PSW") 145 146 t.Logs.Info("Create namespace") 147 Eventually(func() (*v1.Namespace, error) { 148 nsLabels := map[string]string{ 149 "verrazzano-managed": "true", 150 "istio-injection": istioInjection} 151 return pkg.CreateNamespace(namespace, nsLabels) 152 }, shortWaitTimeout, shortPollingInterval).ShouldNot(BeNil()) 153 154 t.Logs.Info("Create docker-registry secret to enable pulling image from the registry") 155 Eventually(func() (*v1.Secret, error) { 156 return pkg.CreateDockerSecret(namespace, helloDomainRepoCreds, regServ, regUser, regPass) 157 }, shortWaitTimeout, shortPollingInterval).ShouldNot(BeNil()) 158 159 t.Logs.Info("Create secret for the WebLogic domain") 160 Eventually(func() (*v1.Secret, error) { 161 return pkg.CreateCredentialsSecret(namespace, helloDomainWeblogicCreds, wlsUser, wlsPass, nil) 162 }, shortWaitTimeout, shortPollingInterval).ShouldNot(BeNil()) 163 164 // Note: creating the app config first to verify that default metrics traits are created properly if the app config exists before the components 165 t.Logs.Info("Create application resources") 166 Eventually(func() error { 167 file, err := pkg.FindTestDataFile(appConfiguration) 168 if err != nil { 169 return err 170 } 171 return resource.CreateOrUpdateResourceFromFileInGeneratedNamespace(file, namespace) 172 }, shortWaitTimeout, shortPollingInterval).ShouldNot(HaveOccurred()) 173 174 t.Logs.Info("Create component resources") 175 Eventually(func() error { 176 file, err := pkg.FindTestDataFile(compConfiguration) 177 if err != nil { 178 return err 179 } 180 return resource.CreateOrUpdateResourceFromFileInGeneratedNamespace(file, namespace) 181 }, shortWaitTimeout, shortPollingInterval, "Failed to create component resources for WebLogic application").ShouldNot(HaveOccurred()) 182 } 183 184 func undeployWebLogicApp() { 185 t.Logs.Info("Undeploy WebLogic application") 186 t.Logs.Info("Delete application") 187 start := time.Now() 188 Eventually(func() error { 189 file, err := pkg.FindTestDataFile(appConfiguration) 190 if err != nil { 191 return err 192 } 193 return resource.DeleteResourceFromFileInGeneratedNamespace(file, namespace) 194 }, shortWaitTimeout, shortPollingInterval).ShouldNot(HaveOccurred()) 195 196 t.Logs.Info("Delete component") 197 Eventually(func() error { 198 file, err := pkg.FindTestDataFile(compConfiguration) 199 if err != nil { 200 return err 201 } 202 return resource.DeleteResourceFromFileInGeneratedNamespace(file, namespace) 203 }, shortWaitTimeout, shortPollingInterval).ShouldNot(HaveOccurred()) 204 205 t.Logs.Info("Wait for pod to terminate") 206 Eventually(func() bool { 207 podsTerminated, _ := pkg.PodsNotRunning(namespace, expectedPods) 208 return podsTerminated 209 }, shortWaitTimeout, shortPollingInterval).Should(BeTrue()) 210 211 t.Logs.Info("Delete namespace") 212 Eventually(func() error { 213 return pkg.DeleteNamespace(namespace) 214 }, shortWaitTimeout, shortPollingInterval).ShouldNot(HaveOccurred()) 215 216 t.Logs.Info("Wait for namespace finalizer to be removed") 217 Eventually(func() bool { 218 return pkg.CheckNamespaceFinalizerRemoved(namespace) 219 }, shortWaitTimeout, shortPollingInterval).Should(BeTrue()) 220 221 t.Logs.Info("Wait for namespace deletion") 222 Eventually(func() bool { 223 _, err := pkg.GetNamespace(namespace) 224 return err != nil && errors.IsNotFound(err) 225 }, shortWaitTimeout, shortPollingInterval).Should(BeTrue()) 226 227 metrics.Emit(t.Metrics.With("undeployment_elapsed_time", time.Since(start).Milliseconds())) 228 } 229 230 var _ = t.Describe("Validate deployment of VerrazzanoWebLogicWorkload", Label("f:app-lcm.oam", "f:app-lcm.weblogic-workload"), func() { 231 232 t.Context("Ingress", Label("f:mesh.ingress"), FlakeAttempts(8), func() { 233 // Verify the application endpoint is working. 234 // GIVEN the sample WebLogic app is deployed 235 // WHEN the application endpoint is accessed 236 // THEN the expected returned page should contain an expected value. 237 t.It("Verify application endpoint is working", func() { 238 Eventually(func() (*pkg.HTTPResponse, error) { 239 url := fmt.Sprintf("https://%s/%s", host, appURL) 240 return pkg.GetWebPage(url, host) 241 }, shortWaitTimeout, shortPollingInterval).Should(And(pkg.HasStatus(http.StatusOK), pkg.BodyEquals(welcomeMessage))) 242 }) 243 }) 244 245 t.Context("Metrics", Label("f:observability.monitoring.prom"), FlakeAttempts(5), func() { 246 kubeconfigPath, err := k8sutil.GetKubeConfigLocation() 247 if err != nil { 248 Expect(err).To(BeNil(), fmt.Sprintf("Failed to get default kubeconfig path: %s", err.Error())) 249 } 250 ok, _ := pkg.IsVerrazzanoMinVersion("1.4.0", kubeconfigPath) 251 // Verify application Prometheus scraped targets 252 // GIVEN the sample WebLogic app is deployed 253 // WHEN the application configuration uses a default metrics trait 254 // THEN confirm that all the scrape targets are healthy 255 t.It("Verify all scrape targets are healthy for the application", func() { 256 Eventually(func() (bool, error) { 257 var componentNames = []string{"hello-domain"} 258 return pkg.ScrapeTargetsHealthy(pkg.GetScrapePools(namespace, "hello-appconf", componentNames, ok)) 259 }, shortWaitTimeout, shortPollingInterval).Should(BeTrue()) 260 }) 261 262 // Verify Istio Prometheus scraped metrics 263 // GIVEN the sample WebLogic app is deployed 264 // WHEN the application configuration is deployed 265 // THEN confirm that Istio metrics are being collected 266 if istioInjection == "enabled" { 267 t.It("Retrieve Istio Prometheus scraped metrics", func() { 268 pkg.Concurrently( 269 func() { 270 Eventually(func() bool { 271 return metricsTest.MetricsExist("istio_tcp_received_bytes_total", map[string]string{"destination_canonical_service": "hello-domain"}) 272 }, shortWaitTimeout, shortPollingInterval).Should(BeTrue()) 273 }, 274 func() { 275 Eventually(func() bool { 276 return metricsTest.MetricsExist("envoy_cluster_http2_pending_send_bytes", map[string]string{"pod_name": wlsAdminServer}) 277 }, shortWaitTimeout, longPollingInterval).Should(BeTrue()) 278 }, 279 ) 280 }) 281 } 282 }) 283 284 t.Context("WebLogic logging", Label("f:observability.logging.es"), func() { 285 var indexName string 286 var err error 287 Eventually(func() error { 288 indexName, err = pkg.GetOpenSearchAppIndex(namespace) 289 return err 290 }, shortWaitTimeout, shortPollingInterval).Should(BeNil(), "Expected to get OpenSearch App Index") 291 292 // GIVEN a WebLogic application with logging enabled 293 // WHEN the Opensearch index is retrieved 294 // THEN verify that it is found 295 t.It("Verify Opensearch index exists", func() { 296 Eventually(func() bool { 297 return pkg.LogIndexFound(indexName) 298 }, shortWaitTimeout, shortPollingInterval).Should(BeTrue(), "Expected to find log index "+indexName) 299 }) 300 pkg.Concurrently( 301 // GIVEN a WebLogic application with logging enabled 302 // WHEN the log records are retrieved from the Opensearch index 303 // THEN verify that a recent log record of hellodomain-adminserver stdout is found 304 func() { 305 t.It("Verify recent hellodomain-adminserver log record exists", func() { 306 Eventually(func() bool { 307 return pkg.LogRecordFound(indexName, time.Now().Add(-24*time.Hour), map[string]string{ 308 "kubernetes.labels.weblogic_domainUID": wlDomain, 309 "kubernetes.labels.weblogic_serverName": "AdminServer", 310 "kubernetes.pod_name": wlsAdminServer, 311 "kubernetes.container_name": "weblogic-server", 312 }) 313 }, shortWaitTimeout, shortPollingInterval).Should(BeTrue(), "Expected to find a recent log record") 314 }) 315 }, 316 317 // GIVEN a WebLogic application with logging enabled 318 // WHEN the log records are retrieved from the Opensearch index 319 // THEN verify that a recent log record of hellodomain-adminserver log file is found 320 func() { 321 t.It("Verify recent hellodomain-adminserver log record exists", func() { 322 Eventually(func() bool { 323 return pkg.LogRecordFound(indexName, time.Now().Add(-24*time.Hour), map[string]string{ 324 "kubernetes.labels.weblogic_domainUID": wlDomain, 325 "kubernetes.labels.weblogic_serverName": "AdminServer", 326 "kubernetes.pod_name": wlsAdminServer, 327 "kubernetes.container_name": "fluentd-stdout-sidecar", 328 }) 329 }, shortWaitTimeout, shortPollingInterval).Should(BeTrue(), "Expected to find a recent log record") 330 }) 331 }, 332 // GIVEN a WebLogic application with logging enabled 333 // WHEN the log records are retrieved from the Opensearch index 334 // THEN verify that a recent pattern-matched log record of hellodomain-adminserver stdout is found 335 func() { 336 t.It("Verify recent pattern-matched AdminServer log record exists", func() { 337 Eventually(func() bool { 338 return pkg.FindLog(indexName, 339 []pkg.Match{ 340 {Key: "kubernetes.container_name.keyword", Value: "fluentd-stdout-sidecar"}, 341 {Key: "subSystem.keyword", Value: "WorkManager"}, 342 {Key: "serverName.keyword", Value: wlsAdminServer}, 343 {Key: "serverName2.keyword", Value: "AdminServer"}, 344 {Key: "message", Value: "standby threads"}}, 345 []pkg.Match{}) 346 }, shortWaitTimeout, longPollingInterval).Should(BeTrue(), "Expected to find a recent log record") 347 }) 348 }, 349 // GIVEN a WebLogic application with logging enabled 350 // WHEN the log records are retrieved from the Opensearch index 351 // THEN verify that a recent pattern-matched log record of hellodomain-adminserver stdout is found 352 func() { 353 t.It("Verify recent pattern-matched AdminServer log record exists", func() { 354 Eventually(func() bool { 355 return pkg.FindLog(indexName, 356 []pkg.Match{ 357 {Key: "kubernetes.container_name.keyword", Value: "fluentd-stdout-sidecar"}, 358 {Key: "subSystem", Value: "WorkManager"}, 359 {Key: "serverName", Value: wlsAdminServer}, 360 {Key: "serverName2", Value: "AdminServer"}, 361 {Key: "message", Value: "Self-tuning"}}, 362 []pkg.Match{}) 363 }, shortWaitTimeout, longPollingInterval).Should(BeTrue(), "Expected to find a recent log record") 364 }) 365 }, 366 ) 367 }) 368 })