github.com/verrazzano/verrazzano@v1.7.1/tests/e2e/multicluster/workloads/mccoherence/coherence_workload_mc_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 mccoherence
     5  
     6  import (
     7  	"fmt"
     8  	"net/http"
     9  	"os"
    10  	"strconv"
    11  	"time"
    12  
    13  	. "github.com/onsi/ginkgo/v2"
    14  	. "github.com/onsi/gomega"
    15  	"github.com/verrazzano/verrazzano/pkg/k8s/resource"
    16  	"github.com/verrazzano/verrazzano/pkg/k8sutil"
    17  	"github.com/verrazzano/verrazzano/tests/e2e/multicluster"
    18  	"github.com/verrazzano/verrazzano/tests/e2e/pkg"
    19  	dump "github.com/verrazzano/verrazzano/tests/e2e/pkg/test/clusterdump"
    20  	"github.com/verrazzano/verrazzano/tests/e2e/pkg/test/framework"
    21  	"github.com/verrazzano/verrazzano/tests/e2e/pkg/test/framework/metrics"
    22  )
    23  
    24  const (
    25  	// wait intervals
    26  	pollingInterval      = 5 * time.Second
    27  	waitTimeout          = 5 * time.Minute
    28  	shortWaitTimeout     = 10 * time.Minute
    29  	shortPollingInterval = 10 * time.Second
    30  	consistentlyDuration = 1 * time.Minute
    31  	longWaitTimeout      = 10 * time.Minute
    32  	longPollingInterval  = 20 * time.Second
    33  
    34  	// application specific constants
    35  	helloCoherence = "hello-coherence"
    36  	appNamespace   = "mc-hello-coherence"
    37  	appConfigName  = "hello-appconf"
    38  	projectName    = helloCoherence
    39  	componentName  = helloCoherence
    40  
    41  	cohClusterName   = "HelloCoherence"
    42  	podName          = "hello-coh-0"
    43  	containerName    = "coherence"
    44  	appName          = "hello-coh"
    45  	appEndPoint      = "catalogue"
    46  	expectedResponse = "A perfect example of a swivel chair trained calf"
    47  
    48  	// metrics
    49  	jvmUptime            = "base_jvm_uptime_seconds"
    50  	vendorRequestsCount  = "vendor_requests_count_total"
    51  	memUsageBytes        = "container_memory_usage_bytes"
    52  	clusterSize          = "vendor:coherence_cluster_size"
    53  	serviceMessagesLocal = "vendor:coherence_service_messages_local"
    54  
    55  	// various labels
    56  	k8sLabelOAMComp              = "kubernetes.labels.app_oam_dev\\/component"
    57  	k8sLabelOAMCompKeyword       = "kubernetes.labels.app_oam_dev\\/component.keyword"
    58  	k8sLabelOAMApp               = "kubernetes.labels.app_oam_dev\\/name"
    59  	k8sLabelContainerName        = "kubernetes.container_name"
    60  	k8sLabelContainerNameKeyword = "kubernetes.container_name.keyword"
    61  	k8sLabelCoherenceCluster     = "kubernetes.labels.coherenceCluster"
    62  	k8sPodName                   = "kubernetes.pod_name"
    63  	labelApp                     = "app"
    64  	labelNS                      = "namespace"
    65  	labelCluster                 = "cluster"
    66  	skipVerifications            = "Skip Verifications"
    67  	skipDeletions                = "Skip Deletions"
    68  
    69  	// application resources
    70  	appConfiguration     = "tests/testdata/test-applications/coherence/hello-coherence/hello-coherence-mc-app.yaml"
    71  	compConfiguration    = "tests/testdata/test-applications/coherence/hello-coherence/hello-coherence-comp.yaml"
    72  	projectConfiguration = "tests/testdata/test-applications/coherence/hello-coherence/verrazzano-project.yaml"
    73  )
    74  
    75  var (
    76  	appComp           = []string{helloCoherence}
    77  	appPod            = []string{"hello-coh-"}
    78  	clusterName       = os.Getenv("MANAGED_CLUSTER_NAME")
    79  	adminKubeconfig   = os.Getenv("ADMIN_KUBECONFIG")
    80  	managedKubeconfig = os.Getenv("MANAGED_KUBECONFIG")
    81  	metricsTest       pkg.MetricsTest
    82  	failed            = false
    83  	beforeSuitePassed = false
    84  )
    85  
    86  var t = framework.NewTestFramework("mccoherence")
    87  
    88  var _ = t.AfterEach(func() {
    89  	failed = failed || CurrentSpecReport().Failed()
    90  })
    91  
    92  var beforeSuite = t.BeforeSuiteFunc(func() {
    93  	if !skipDeploy {
    94  		start := time.Now()
    95  
    96  		// deploy the VerrazzanoProject
    97  		Eventually(func() error {
    98  			return multicluster.DeployVerrazzanoProject(projectConfiguration, adminKubeconfig)
    99  		}, waitTimeout, pollingInterval).ShouldNot(HaveOccurred())
   100  
   101  		// wait for the namespace to be created on the cluster before deploying app
   102  		Eventually(func() bool {
   103  			return multicluster.TestNamespaceExists(adminKubeconfig, appNamespace)
   104  		}, waitTimeout, pollingInterval).Should(BeTrue())
   105  
   106  		Eventually(func() error {
   107  			return multicluster.DeployCompResource(compConfiguration, appNamespace, adminKubeconfig)
   108  		}, waitTimeout, pollingInterval).ShouldNot(HaveOccurred())
   109  
   110  		Eventually(func() error {
   111  			return multicluster.DeployAppResource(appConfiguration, appNamespace, adminKubeconfig)
   112  		}, waitTimeout, pollingInterval).ShouldNot(HaveOccurred())
   113  		metrics.Emit(t.Metrics.With("deployment_elapsed_time", time.Since(start).Milliseconds()))
   114  	}
   115  
   116  	var err error
   117  	metricsTest, err = pkg.NewMetricsTest(adminKubeconfig, map[string]string{}, managedKubeconfig)
   118  	if err != nil {
   119  		AbortSuite(fmt.Sprintf("Failed to create the Metrics test object: %v", err))
   120  	}
   121  
   122  	beforeSuitePassed = true
   123  })
   124  
   125  var _ = BeforeSuite(beforeSuite)
   126  
   127  var afterSuite = t.AfterSuiteFunc(func() {
   128  	if failed || !beforeSuitePassed {
   129  		err := dump.ExecuteBugReport(appNamespace)
   130  		if err != nil {
   131  			return
   132  		}
   133  	}
   134  })
   135  
   136  var _ = AfterSuite(afterSuite)
   137  
   138  var _ = t.Describe("In Multi-cluster, verify Coherence application", Label("f:multicluster.mc-app-lcm"), func() {
   139  
   140  	t.Context("Admin Cluster", func() {
   141  		// GIVEN an admin cluster and at least one managed cluster
   142  		// WHEN the example application has been deployed to the admin cluster
   143  		// THEN expect that the multi-cluster resources have been created on the admin cluster
   144  		t.It("Has multi cluster resources", func() {
   145  			if skipVerify {
   146  				Skip(skipVerifications)
   147  			}
   148  			Eventually(func() bool {
   149  				return multicluster.VerifyMCResources(adminKubeconfig, true, false, appNamespace, appConfigName, appComp)
   150  			}, waitTimeout, pollingInterval).Should(BeTrue())
   151  		})
   152  
   153  		// GIVEN an admin cluster
   154  		// WHEN the multi-cluster example application has been created on admin cluster but not placed there
   155  		// THEN expect that the app is not deployed to the admin cluster consistently for some length of time
   156  		t.It("Does not have application placed", func() {
   157  			if skipVerify {
   158  				Skip(skipVerifications)
   159  			}
   160  			Consistently(func() bool {
   161  				result, err := multicluster.VerifyAppResourcesInCluster(adminKubeconfig, true, false, projectName, appNamespace, appPod)
   162  				if err != nil {
   163  					AbortSuite(fmt.Sprintf("Verification of application resources failed for admin cluster, error: %v", err))
   164  				}
   165  				return result
   166  			}, consistentlyDuration, pollingInterval).Should(BeTrue())
   167  		})
   168  	})
   169  
   170  	t.Context("Managed Cluster", func() {
   171  		// GIVEN an admin cluster and at least one managed cluster
   172  		// WHEN the example application has been deployed to the admin cluster
   173  		// THEN expect that the multi-cluster resources have been created on the managed cluster
   174  		t.It("Has multi cluster resources", func() {
   175  			if skipVerify {
   176  				Skip(skipVerifications)
   177  			}
   178  			Eventually(func() bool {
   179  				return multicluster.VerifyMCResources(managedKubeconfig, false, true, appNamespace, appConfigName, appComp)
   180  			}, waitTimeout, pollingInterval).Should(BeTrue())
   181  		})
   182  		// GIVEN an admin cluster and at least one managed cluster
   183  		// WHEN the multi-cluster example application has been created on admin cluster and placed in managed cluster
   184  		// THEN expect that the app is deployed to the managed cluster
   185  		t.It("Has application placed", func() {
   186  			if skipVerify {
   187  				Skip(skipVerifications)
   188  			}
   189  			Eventually(func() bool {
   190  				result, err := multicluster.VerifyAppResourcesInCluster(managedKubeconfig, false, true, projectName, appNamespace, appPod)
   191  				if err != nil {
   192  					AbortSuite(fmt.Sprintf("Verification of application resources failed for managed cluster, error: %v", err))
   193  				}
   194  				return result
   195  			}, longWaitTimeout, longPollingInterval).Should(BeTrue())
   196  		})
   197  	})
   198  
   199  	t.Context("Remaining Managed Clusters", func() {
   200  		clusterCountStr := os.Getenv("CLUSTER_COUNT")
   201  		if clusterCountStr == "" {
   202  			// skip tests
   203  			return
   204  		}
   205  		clusterCount, err := strconv.Atoi(clusterCountStr)
   206  		if err != nil {
   207  			// skip tests
   208  			return
   209  		}
   210  
   211  		kubeconfigDir := os.Getenv("KUBECONFIG_DIR")
   212  		for i := 3; i <= clusterCount; i++ {
   213  			kubeconfig := kubeconfigDir + "/" + fmt.Sprintf("%d", i) + "/kube_config"
   214  			t.It("Does not have multi cluster resources", func() {
   215  				if skipVerify {
   216  					Skip(skipVerifications)
   217  				}
   218  				Eventually(func() bool {
   219  					return multicluster.VerifyMCResources(kubeconfig, false, false, appNamespace, appConfigName, appComp)
   220  				}, waitTimeout, pollingInterval).Should(BeTrue())
   221  			})
   222  			t.It("Does not have application placed", func() {
   223  				if skipVerify {
   224  					Skip(skipVerifications)
   225  				}
   226  				Eventually(func() bool {
   227  					result, err := multicluster.VerifyAppResourcesInCluster(kubeconfig, false, false, projectName, appNamespace, appPod)
   228  					if err != nil {
   229  						AbortSuite(fmt.Sprintf("Verification of application resources failed for the cluster with kubeconfig: %v, error: %v", kubeconfig, err))
   230  					}
   231  					return result
   232  				}, waitTimeout, pollingInterval).Should(BeTrue())
   233  			})
   234  		}
   235  	})
   236  
   237  	t.Context("for Ingress", Label("f:mesh.ingress"), func() {
   238  		var host = ""
   239  		var err error
   240  		// Get the host from the Istio gateway resource.
   241  		// GIVEN the Istio gateway for the app namespace
   242  		// WHEN GetHostnameFromGateway is called
   243  		// THEN return the host name found in the gateway.
   244  		t.It("Get host from gateway.", func() {
   245  			if skipVerify {
   246  				Skip(skipVerifications)
   247  			}
   248  			Eventually(func() (string, error) {
   249  				host, err = k8sutil.GetHostnameFromGatewayInCluster(appNamespace, "", managedKubeconfig)
   250  				return host, err
   251  			}, waitTimeout, pollingInterval).Should(Not(BeEmpty()))
   252  		})
   253  
   254  		// Verify the application REST endpoint is working.
   255  		// GIVEN the Coherence app is deployed
   256  		// WHEN the UI is accessed
   257  		// THEN the expected returned page should contain an expected value.
   258  		t.It("Verify '/catalogue' endpoint is working.", func() {
   259  			if skipVerify {
   260  				Skip(skipVerifications)
   261  			}
   262  			Eventually(func() (*pkg.HTTPResponse, error) {
   263  				url := fmt.Sprintf("https://%s/%s", host, appEndPoint)
   264  				return pkg.GetWebPageInCluster(url, host, managedKubeconfig)
   265  			}, waitTimeout, pollingInterval).Should(And(pkg.HasStatus(http.StatusOK), pkg.BodyContains(expectedResponse)))
   266  		})
   267  	})
   268  
   269  	t.Context("for Logging", Label("f:observability.logging.es"), func() {
   270  		indexName, err := pkg.GetOpenSearchAppIndexWithKC(appNamespace, adminKubeconfig)
   271  		Expect(err).To(BeNil())
   272  		// GIVEN an admin cluster and at least one managed cluster
   273  		// WHEN the example application has been deployed to the admin cluster
   274  		// THEN expect the Elasticsearch index for the app exists on the admin cluster Elasticsearch
   275  		t.It("Verify Elasticsearch index exists on admin cluster", func() {
   276  			if skipVerify {
   277  				Skip(skipVerifications)
   278  			}
   279  			Eventually(func() bool {
   280  				return pkg.LogIndexFoundInCluster(indexName, adminKubeconfig)
   281  			}, longWaitTimeout, longPollingInterval).Should(BeTrue(), "Expected to find log index for Coherence application")
   282  		})
   283  
   284  		t.It("Verify recent log record exists", func() {
   285  			if skipVerify {
   286  				Skip(skipVerifications)
   287  			}
   288  			Eventually(func() bool {
   289  				return pkg.LogRecordFoundInCluster(indexName, time.Now().Add(-24*time.Hour), map[string]string{
   290  					k8sLabelOAMComp:       componentName,
   291  					k8sLabelOAMApp:        appConfigName,
   292  					k8sLabelContainerName: componentName,
   293  				}, adminKubeconfig)
   294  			}, shortWaitTimeout, shortPollingInterval).Should(BeTrue(), "Expected to find a recent log record")
   295  		})
   296  
   297  		t.It("Verify recent application log record", func() {
   298  			if skipVerify {
   299  				Skip(skipVerifications)
   300  			}
   301  			Eventually(func() bool {
   302  				return pkg.LogRecordFoundInCluster(indexName, time.Now().Add(-24*time.Hour), map[string]string{
   303  					k8sLabelCoherenceCluster:     cohClusterName,
   304  					k8sLabelOAMCompKeyword:       componentName,
   305  					k8sPodName:                   podName,
   306  					k8sLabelContainerNameKeyword: containerName,
   307  				}, adminKubeconfig)
   308  			}, shortWaitTimeout, shortPollingInterval).Should(BeTrue(), "Expected to find a recent log record")
   309  		})
   310  	})
   311  
   312  	t.Context("for Prometheus Metrics", Label("f:observability.monitoring.prom"), func() {
   313  		// Coherence metric fix available only from 1.3.0
   314  		if ok, _ := pkg.IsVerrazzanoMinVersion("1.3.0", adminKubeconfig); ok {
   315  			t.It("Retrieve application Prometheus scraped metrics", func() {
   316  				if skipVerify {
   317  					Skip(skipVerifications)
   318  				}
   319  				pkg.Concurrently(
   320  					func() {
   321  						clusterNameMetricsLabel, _ := pkg.GetClusterNameMetricLabel(adminKubeconfig)
   322  						Eventually(func() bool {
   323  							m := make(map[string]string)
   324  							m[labelApp] = appName
   325  							m[clusterNameMetricsLabel] = clusterName
   326  							return metricsTest.MetricsExist(jvmUptime, m)
   327  						}, longWaitTimeout, longPollingInterval).Should(BeTrue())
   328  					},
   329  					func() {
   330  						clusterNameMetricsLabel, _ := pkg.GetClusterNameMetricLabel(adminKubeconfig)
   331  						Eventually(func() bool {
   332  							m := make(map[string]string)
   333  							m[labelApp] = appName
   334  							m[clusterNameMetricsLabel] = clusterName
   335  							return metricsTest.MetricsExist(vendorRequestsCount, m)
   336  						}, longWaitTimeout, longPollingInterval).Should(BeTrue())
   337  					},
   338  					func() {
   339  						clusterNameMetricsLabel, _ := pkg.GetClusterNameMetricLabel(adminKubeconfig)
   340  						Eventually(func() bool {
   341  							m := make(map[string]string)
   342  							m[labelNS] = appNamespace
   343  							m[clusterNameMetricsLabel] = clusterName
   344  							return metricsTest.MetricsExist(memUsageBytes, m)
   345  						}, longWaitTimeout, longPollingInterval).Should(BeTrue())
   346  					},
   347  					func() {
   348  						clusterNameMetricsLabel, _ := pkg.GetClusterNameMetricLabel(adminKubeconfig)
   349  						Eventually(func() bool {
   350  							m := make(map[string]string)
   351  							m[labelCluster] = cohClusterName
   352  							m[clusterNameMetricsLabel] = clusterName
   353  							return metricsTest.MetricsExist(clusterSize, m)
   354  						}, longWaitTimeout, longPollingInterval).Should(BeTrue(), "Expected to find coherence metric")
   355  					},
   356  					func() {
   357  						clusterNameMetricsLabel, _ := pkg.GetClusterNameMetricLabel(adminKubeconfig)
   358  						Eventually(func() bool {
   359  							m := make(map[string]string)
   360  							m[labelCluster] = cohClusterName
   361  							m[clusterNameMetricsLabel] = clusterName
   362  							return metricsTest.MetricsExist(serviceMessagesLocal, m)
   363  						}, longWaitTimeout, longPollingInterval).Should(BeTrue(), "Expected to find coherence metric")
   364  					},
   365  				)
   366  			})
   367  		}
   368  	})
   369  
   370  	t.Context("Delete resources", func() {
   371  		t.It("on admin cluster", func() {
   372  			if skipUndeploy {
   373  				Skip(skipDeletions)
   374  			}
   375  			Eventually(func() error {
   376  				return cleanUp(adminKubeconfig)
   377  			}, waitTimeout, pollingInterval).ShouldNot(HaveOccurred())
   378  		})
   379  
   380  		t.It("Verify deletion on admin cluster", func() {
   381  			if skipUndeploy {
   382  				Skip(skipDeletions)
   383  			}
   384  			Eventually(func() bool {
   385  				return multicluster.VerifyDeleteOnAdminCluster(adminKubeconfig, false, appNamespace, projectName, appConfigName, appPod)
   386  			}, waitTimeout, pollingInterval).Should(BeTrue())
   387  		})
   388  
   389  		t.It("Verify automatic deletion on managed cluster", func() {
   390  			if skipUndeploy {
   391  				Skip(skipDeletions)
   392  			}
   393  			Eventually(func() bool {
   394  				return multicluster.VerifyDeleteOnManagedCluster(managedKubeconfig, appNamespace, projectName, appConfigName, appPod)
   395  			}, waitTimeout, pollingInterval).Should(BeTrue())
   396  		})
   397  
   398  		t.It("Delete test namespace on managed cluster", func() {
   399  			if skipUndeploy {
   400  				Skip(skipDeletions)
   401  			}
   402  			Eventually(func() error {
   403  				return pkg.DeleteNamespaceInCluster(appNamespace, managedKubeconfig)
   404  			}, waitTimeout, pollingInterval).ShouldNot(HaveOccurred())
   405  		})
   406  
   407  		t.It("Delete test namespace on admin cluster", func() {
   408  			if skipUndeploy {
   409  				Skip(skipDeletions)
   410  			}
   411  			Eventually(func() error {
   412  				return pkg.DeleteNamespaceInCluster(appNamespace, adminKubeconfig)
   413  			}, waitTimeout, pollingInterval).ShouldNot(HaveOccurred())
   414  		})
   415  	})
   416  })
   417  
   418  func cleanUp(kubeconfigPath string) error {
   419  	start := time.Now()
   420  	file, err := pkg.FindTestDataFile(appConfiguration)
   421  	if err != nil {
   422  		return err
   423  	}
   424  	if err := resource.DeleteResourceFromFileInClusterInGeneratedNamespace(file, kubeconfigPath, appNamespace); err != nil {
   425  		return fmt.Errorf("failed to delete application resource: %v", err)
   426  	}
   427  	file, err = pkg.FindTestDataFile(compConfiguration)
   428  	if err != nil {
   429  		return err
   430  	}
   431  	if err := resource.DeleteResourceFromFileInClusterInGeneratedNamespace(file, kubeconfigPath, appNamespace); err != nil {
   432  		return fmt.Errorf("failed to delete component resource: %v", err)
   433  	}
   434  
   435  	file, err = pkg.FindTestDataFile(projectConfiguration)
   436  	if err != nil {
   437  		return err
   438  	}
   439  	if err := resource.DeleteResourceFromFileInCluster(file, kubeconfigPath); err != nil {
   440  		return fmt.Errorf("failed to delete project resource: %v", err)
   441  	}
   442  	metrics.Emit(t.Metrics.With("undeployment_elapsed_time", time.Since(start).Milliseconds()))
   443  	return nil
   444  }