github.com/verrazzano/verrazzano@v1.7.1/tests/e2e/examples/helidonconfig/helidon_config_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 helidonconfig
     5  
     6  import (
     7  	"fmt"
     8  	"os"
     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  	imagePullWaitTimeout     = 40 * time.Minute
    29  	imagePullPollingInterval = 30 * time.Second
    30  
    31  	ingress        = "helidon-config-ingress-rule"
    32  	helidonService = "helidon-config-deployment"
    33  	targetsVersion = "1.4.0"
    34  )
    35  
    36  var (
    37  	t                  = framework.NewTestFramework("helidonconfig")
    38  	generatedNamespace = pkg.GenerateNamespace("helidon-config")
    39  	kubeConfig         = os.Getenv("KUBECONFIG")
    40  	host               = ""
    41  	metricsTest        pkg.MetricsTest
    42  )
    43  var isMinVersion140 bool
    44  
    45  var beforeSuite = t.BeforeSuiteFunc(func() {
    46  	if !skipDeploy {
    47  		start := time.Now()
    48  		Eventually(func() (*v1.Namespace, error) {
    49  			nsLabels := map[string]string{
    50  				"verrazzano-managed": "true",
    51  				"istio-injection":    istioInjection}
    52  			return pkg.CreateNamespace(namespace, nsLabels)
    53  		}, shortWaitTimeout, shortPollingInterval).ShouldNot(BeNil())
    54  
    55  		Eventually(func() error {
    56  			file, err := pkg.FindTestDataFile("examples/helidon-config/helidon-config-comp.yaml")
    57  			if err != nil {
    58  				return err
    59  			}
    60  			return resource.CreateOrUpdateResourceFromFileInGeneratedNamespace(file, namespace)
    61  		}, shortWaitTimeout, shortPollingInterval).ShouldNot(HaveOccurred())
    62  
    63  		Eventually(func() error {
    64  			file, err := pkg.FindTestDataFile("examples/helidon-config/helidon-config-app.yaml")
    65  			if err != nil {
    66  				return err
    67  			}
    68  			return resource.CreateOrUpdateResourceFromFileInGeneratedNamespace(file, namespace)
    69  		}, shortWaitTimeout, shortPollingInterval, "Failed to create helidon-config application resource").ShouldNot(HaveOccurred())
    70  		beforeSuitePassed = true
    71  		metrics.Emit(t.Metrics.With("deployment_elapsed_time", time.Since(start).Milliseconds()))
    72  
    73  		t.Logs.Info("Container image pull check")
    74  		Eventually(func() bool {
    75  			return pkg.ContainerImagePullWait(namespace, expectedPodsHelidonConfig)
    76  		}, imagePullWaitTimeout, imagePullPollingInterval).Should(BeTrue())
    77  	}
    78  	kubeconfigPath, err := k8sutil.GetKubeConfigLocation()
    79  	if err != nil {
    80  		Fail(fmt.Sprintf("Failed to get default kubeconfig path: %s", err.Error()))
    81  	}
    82  	isMinVersion140, err = pkg.IsVerrazzanoMinVersion("1.4.0", kubeconfigPath)
    83  	if err != nil {
    84  		Fail(err.Error())
    85  	}
    86  
    87  	// Verify helidon-config-deployment pod is running
    88  	// GIVEN OAM helidon-config app is deployed
    89  	// WHEN the component and appconfig are created
    90  	// THEN the expected pod must be running in the test namespace
    91  	t.Logs.Info("Helidon Config: check expected pods are running")
    92  	Eventually(func() bool {
    93  		result, err := pkg.PodsRunning(namespace, expectedPodsHelidonConfig)
    94  		if err != nil {
    95  			AbortSuite(fmt.Sprintf("One or more pods are not running in the namespace: %v, error: %v", namespace, err))
    96  		}
    97  		return result
    98  	}, longWaitTimeout, longPollingInterval).Should(BeTrue(), "Helidon Config Failed to Deploy: Pods are not ready")
    99  
   100  	t.Logs.Info("Helidon Config: check expected Services are running")
   101  	Eventually(func() bool {
   102  		result, err := pkg.DoesServiceExist(namespace, helidonService)
   103  		if err != nil {
   104  			AbortSuite(fmt.Sprintf("Helidon Service %s is not running in the namespace: %v, error: %v", helidonService, namespace, err))
   105  		}
   106  		return result
   107  	}, longWaitTimeout, longPollingInterval).Should(BeTrue(), "Helidon Config Failed to Deploy: Services are not ready")
   108  
   109  	t.Logs.Info("Helidon Config: check expected VirtualService is ready")
   110  	Eventually(func() bool {
   111  		result, err := pkg.DoesVirtualServiceExist(namespace, ingress)
   112  		if err != nil {
   113  			AbortSuite(fmt.Sprintf("Helidon VirtualService %s is not running in the namespace: %v, error: %v", ingress, namespace, err))
   114  		}
   115  		return result
   116  	}, shortWaitTimeout, longPollingInterval).Should(BeTrue(), "Helidon Config Failed to Deploy: VirtualService is not ready")
   117  
   118  	// Get the host from the Istio gateway resource.
   119  	start := time.Now()
   120  	t.Logs.Info("Helidon Config: check expected Gateway is ready")
   121  	Eventually(func() (string, error) {
   122  		host, err = k8sutil.GetHostnameFromGateway(namespace, "")
   123  		return host, err
   124  	}, shortWaitTimeout, shortPollingInterval).Should(Not(BeEmpty()), "Helidon Config: Gateway is not ready")
   125  	metrics.Emit(t.Metrics.With("get_host_name_elapsed_time", time.Since(start).Milliseconds()))
   126  
   127  	kubeconfig, err := k8sutil.GetKubeConfigLocation()
   128  	if err != nil {
   129  		AbortSuite(fmt.Sprintf("Failed to get the Kubeconfig location for the cluster: %v", err))
   130  	}
   131  	metricsTest, err = pkg.NewMetricsTest(kubeconfig, map[string]string{})
   132  	if err != nil {
   133  		AbortSuite(fmt.Sprintf("Failed to create the Metrics test object: %v", err))
   134  	}
   135  })
   136  
   137  var _ = BeforeSuite(beforeSuite)
   138  
   139  var failed = false
   140  var beforeSuitePassed = false
   141  
   142  var _ = t.AfterEach(func() {
   143  	failed = failed || CurrentSpecReport().Failed()
   144  })
   145  
   146  var afterSuite = t.AfterSuiteFunc(func() {
   147  	if failed || !beforeSuitePassed {
   148  		dump.ExecuteBugReport(namespace)
   149  	}
   150  	if !skipUndeploy {
   151  		start := time.Now()
   152  		// undeploy the application here
   153  		pkg.Log(pkg.Info, "Delete application")
   154  		Eventually(func() error {
   155  			file, err := pkg.FindTestDataFile("examples/helidon-config/helidon-config-app.yaml")
   156  			if err != nil {
   157  				return err
   158  			}
   159  			return resource.DeleteResourceFromFileInGeneratedNamespace(file, namespace)
   160  		}, shortWaitTimeout, shortPollingInterval).ShouldNot(HaveOccurred())
   161  
   162  		pkg.Log(pkg.Info, "Delete components")
   163  		Eventually(func() error {
   164  			file, err := pkg.FindTestDataFile("examples/helidon-config/helidon-config-comp.yaml")
   165  			if err != nil {
   166  				return err
   167  			}
   168  			return resource.DeleteResourceFromFileInGeneratedNamespace(file, namespace)
   169  		}, shortWaitTimeout, shortPollingInterval).ShouldNot(HaveOccurred())
   170  
   171  		pkg.Log(pkg.Info, "Wait for application pods to terminate")
   172  		Eventually(func() bool {
   173  			podsTerminated, _ := pkg.PodsNotRunning(namespace, expectedPodsHelidonConfig)
   174  			return podsTerminated
   175  		}, shortWaitTimeout, shortPollingInterval).Should(BeTrue())
   176  
   177  		pkg.Log(pkg.Info, "Delete namespace")
   178  		Eventually(func() error {
   179  			return pkg.DeleteNamespace(namespace)
   180  		}, shortWaitTimeout, shortPollingInterval).ShouldNot(HaveOccurred())
   181  
   182  		pkg.Log(pkg.Info, "Wait for Finalizer to be removed")
   183  		Eventually(func() bool {
   184  			return pkg.CheckNamespaceFinalizerRemoved(namespace)
   185  		}, shortWaitTimeout, shortPollingInterval).Should(BeTrue())
   186  
   187  		pkg.Log(pkg.Info, "Wait for namespace to be removed")
   188  		Eventually(func() bool {
   189  			_, err := pkg.GetNamespace(namespace)
   190  			return err != nil && errors.IsNotFound(err)
   191  		}, shortWaitTimeout, shortPollingInterval).Should(BeTrue())
   192  
   193  		metrics.Emit(t.Metrics.With("undeployment_elapsed_time", time.Since(start).Milliseconds()))
   194  	}
   195  })
   196  
   197  var _ = AfterSuite(afterSuite)
   198  
   199  var (
   200  	expectedPodsHelidonConfig = []string{"helidon-config-deployment"}
   201  	waitTimeout               = 10 * time.Minute
   202  	pollingInterval           = 30 * time.Second
   203  )
   204  
   205  var _ = t.Describe("Helidon Config OAM App test", Label("f:app-lcm.oam",
   206  	"f:app-lcm.helidon-workload"), func() {
   207  
   208  	var host = ""
   209  	var err error
   210  	// Get the host from the Istio gateway resource.
   211  	// GIVEN the Istio gateway for the helidon-config namespace
   212  	// WHEN GetHostnameFromGateway is called
   213  	// THEN return the host name found in the gateway.
   214  	t.BeforeEach(func() {
   215  		Eventually(func() (string, error) {
   216  			host, err = k8sutil.GetHostnameFromGateway(namespace, "")
   217  			return host, err
   218  		}, shortWaitTimeout, shortPollingInterval).Should(Not(BeEmpty()))
   219  	})
   220  
   221  	// Verify Helidon Config app is working
   222  	// GIVEN OAM helidon-config app is deployed
   223  	// WHEN the component and appconfig with ingress trait are created
   224  	// THEN the application endpoint must be accessible
   225  	t.Describe("Ingress.", Label("f:mesh.ingress"), func() {
   226  		t.It("Access /config App Url.", func() {
   227  			url := fmt.Sprintf("https://%s/config", host)
   228  			kubeconfigPath, err := k8sutil.GetKubeConfigLocation()
   229  			Expect(err).ShouldNot(HaveOccurred())
   230  			Eventually(func() (*pkg.HTTPResponse, error) {
   231  				return pkg.GetWebPageWithBasicAuth(url, host, "", "", kubeconfigPath)
   232  			}, longWaitTimeout, longPollingInterval).Should(And(pkg.HasStatus(200), pkg.BodyContains("HelloConfig World")))
   233  		})
   234  	})
   235  
   236  	// Verify Prometheus scraped targets
   237  	// GIVEN OAM helidon-config app is deployed
   238  	// WHEN the component and appconfig without metrics-trait(using default) are created
   239  	// THEN the application scrape targets must be healthy
   240  	t.Describe("Metrics.", Label("f:observability.monitoring.prom"), func() {
   241  		t.It("Verify all scrape targets are healthy for the application", func() {
   242  			Eventually(func() (bool, error) {
   243  				var componentNames = []string{"helidon-config-component"}
   244  				return pkg.ScrapeTargetsHealthy(pkg.GetScrapePools(namespace, "helidon-config-appconf", componentNames, isMinVersion140))
   245  			}, shortWaitTimeout, shortPollingInterval).Should(BeTrue())
   246  		})
   247  	})
   248  
   249  	t.Context("Logging.", Label("f:observability.logging.es"), func() {
   250  		var indexName string
   251  		Eventually(func() error {
   252  			indexName, err = pkg.GetOpenSearchAppIndex(namespace)
   253  			return err
   254  		}, shortWaitTimeout, shortPollingInterval).Should(BeNil(), "Expected to get OpenSearch App Index")
   255  
   256  		// GIVEN an application with logging enabled
   257  		// WHEN the Opensearch index is retrieved
   258  		// THEN verify that it is found
   259  		t.It("Verify Opensearch index exists", func() {
   260  			Eventually(func() bool {
   261  				return pkg.LogIndexFound(indexName)
   262  			}, longWaitTimeout, longPollingInterval).Should(BeTrue(), "Expected to find log index for helidon config")
   263  		})
   264  
   265  		// GIVEN an application with logging enabled
   266  		// WHEN the log records are retrieved from the Opensearch index
   267  		// THEN verify that at least one recent log record is found
   268  		t.It("Verify recent Opensearch log record exists", func() {
   269  			Eventually(func() bool {
   270  				return pkg.LogRecordFound(indexName, time.Now().Add(-24*time.Hour), map[string]string{
   271  					"kubernetes.labels.app_oam_dev\\/component": "helidon-config-component",
   272  					"kubernetes.labels.app_oam_dev\\/name":      "helidon-config-appconf",
   273  					"kubernetes.container_name":                 "helidon-config-container",
   274  				})
   275  			}, longWaitTimeout, longPollingInterval).Should(BeTrue(), "Expected to find a recent log record")
   276  		})
   277  	})
   278  })
   279  
   280  func helidonConfigPodsRunning() bool {
   281  	result, err := pkg.PodsRunning(namespace, expectedPodsHelidonConfig)
   282  	if err != nil {
   283  		AbortSuite(fmt.Sprintf("One or more pods are not running in the namespace: %v, error: %v", namespace, err))
   284  	}
   285  	return result
   286  }