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  })