github.com/verrazzano/verrazzano@v1.7.1/tests/e2e/jwt/helidon/helidon_example_test.go (about)

     1  // Copyright (c) 2020, 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 helidon
     5  
     6  import (
     7  	"fmt"
     8  	"github.com/hashicorp/go-retryablehttp"
     9  	dump "github.com/verrazzano/verrazzano/tests/e2e/pkg/test/clusterdump"
    10  	"io"
    11  	"net/http"
    12  	"strings"
    13  	"time"
    14  
    15  	. "github.com/onsi/ginkgo/v2"
    16  	. "github.com/onsi/gomega"
    17  	"github.com/verrazzano/verrazzano/pkg/k8sutil"
    18  	"github.com/verrazzano/verrazzano/tests/e2e/pkg"
    19  	"github.com/verrazzano/verrazzano/tests/e2e/pkg/test/framework"
    20  	"github.com/verrazzano/verrazzano/tests/e2e/pkg/test/framework/metrics"
    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  	skipVerifications        = "Skip Verifications"
    31  	helloHelidon             = "hello-helidon"
    32  	nodeExporterJobName      = "node-exporter"
    33  	jwtHelidonAppYaml        = "testdata/jwt/helidon/hello-helidon-app.yaml"
    34  )
    35  
    36  var isMinVersion140 bool
    37  var (
    38  	t                        = framework.NewTestFramework("helidon")
    39  	generatedNamespace       = pkg.GenerateNamespace(helloHelidon)
    40  	expectedPodsHelloHelidon = []string{"hello-helidon-deployment"}
    41  	metricsTest              pkg.MetricsTest
    42  )
    43  
    44  var beforeSuite = t.BeforeSuiteFunc(func() {
    45  	if !skipDeploy {
    46  		start := time.Now()
    47  		pkg.DeployHelloHelidonApplication(namespace, "", istioInjection, "", jwtHelidonAppYaml)
    48  		metrics.Emit(t.Metrics.With("deployment_elapsed_time", time.Since(start).Milliseconds()))
    49  	}
    50  
    51  	Eventually(func() bool {
    52  		return pkg.ContainerImagePullWait(namespace, expectedPodsHelloHelidon)
    53  	}, imagePullWaitTimeout, imagePullPollingInterval).Should(BeTrue())
    54  	// Verify hello-helidon-deployment pod is running
    55  	// GIVEN OAM hello-helidon app is deployed
    56  	// WHEN the component and appconfig are created
    57  	// THEN the expected pod must be running in the test namespace
    58  	if !skipVerify {
    59  		Eventually(helloHelidonPodsRunning, longWaitTimeout, longPollingInterval).Should(BeTrue())
    60  	}
    61  	kubeconfigPath, err := k8sutil.GetKubeConfigLocation()
    62  	if err != nil {
    63  		Fail(fmt.Sprintf("Failed to get default kubeconfig path: %s", err.Error()))
    64  	}
    65  	isMinVersion140, err = pkg.IsVerrazzanoMinVersion("1.4.0", kubeconfigPath)
    66  	if err != nil {
    67  		Fail(err.Error())
    68  	}
    69  
    70  	kubeconfig, err := k8sutil.GetKubeConfigLocation()
    71  	if err != nil {
    72  		AbortSuite(fmt.Sprintf("Failed to get the Kubeconfig location for the cluster: %v", err))
    73  	}
    74  	metricsTest, err = pkg.NewMetricsTest(kubeconfig, map[string]string{})
    75  	if err != nil {
    76  		AbortSuite(fmt.Sprintf("Failed to create the Metrics test object: %v", err))
    77  	}
    78  
    79  	beforeSuitePassed = true
    80  })
    81  
    82  var _ = BeforeSuite(beforeSuite)
    83  
    84  var failed = false
    85  var beforeSuitePassed = false
    86  
    87  var _ = t.AfterEach(func() {
    88  	failed = failed || CurrentSpecReport().Failed()
    89  })
    90  
    91  var afterSuite = t.AfterSuiteFunc(func() {
    92  	if failed || !beforeSuitePassed {
    93  		dump.ExecuteBugReport(namespace)
    94  	}
    95  	if !skipUndeploy {
    96  		start := time.Now()
    97  		pkg.UndeployHelloHelidonApplication(namespace, "", "")
    98  		metrics.Emit(t.Metrics.With("undeployment_elapsed_time", time.Since(start).Milliseconds()))
    99  	}
   100  })
   101  
   102  var _ = AfterSuite(afterSuite)
   103  
   104  var _ = t.Describe("Hello Helidon OAM App test", Label("f:app-lcm.oam",
   105  	"f:app-lcm.helidon-workload"), func() {
   106  	var host = ""
   107  	var err error
   108  	// Get the host from the Istio gateway resource.
   109  	// GIVEN the Istio gateway for the hello-helidon namespace
   110  	// WHEN GetHostnameFromGateway is called
   111  	// THEN return the host name found in the gateway.
   112  	t.BeforeEach(func() {
   113  		Eventually(func() (string, error) {
   114  			host, err = k8sutil.GetHostnameFromGateway(namespace, "")
   115  			return host, err
   116  		}, shortWaitTimeout, shortPollingInterval).Should(Not(BeEmpty()))
   117  	})
   118  
   119  	// Verify Hello Helidon app is working
   120  	// GIVEN OAM hello-helidon app is deployed
   121  	// WHEN the component and appconfig with ingress trait are created
   122  	// THEN the application endpoint must be accessible
   123  	t.Describe("for Ingress.", Label("f:mesh.ingress"), func() {
   124  
   125  		t.It("Access /greet App Url with valid token", func() {
   126  			if skipVerify {
   127  				Skip(skipVerifications)
   128  			}
   129  			kc, err := pkg.NewKeycloakAdminRESTClient()
   130  			Expect(err).To(BeNil())
   131  			password := pkg.GetRequiredEnvVarOrFail("REALM_USER_PASSWORD")
   132  			realmName := pkg.GetRequiredEnvVarOrFail("REALM_NAME")
   133  			// check for realm
   134  			_, err = kc.GetRealm(realmName)
   135  			Expect(err).To(BeNil())
   136  			var token string
   137  			token, err = kc.GetToken(realmName, "testuser", password, "appsclient", t.Logs)
   138  			Expect(err).To(BeNil())
   139  			t.Logs.Debugf("Obtained token: %v", token)
   140  			url := fmt.Sprintf("https://%s/greet", host)
   141  			Eventually(func() bool {
   142  				return appEndpointAccess(url, host, token, true)
   143  			}, longWaitTimeout, longPollingInterval).Should(BeTrue())
   144  		})
   145  
   146  		t.It("Access /greet App Url w/o token and get RBAC denial", func() {
   147  			if skipVerify {
   148  				Skip(skipVerifications)
   149  			}
   150  			url := fmt.Sprintf("https://%s/greet", host)
   151  			Eventually(func() bool {
   152  				return appEndpointAccess(url, host, "", false)
   153  			}, longWaitTimeout, longPollingInterval).Should(BeTrue())
   154  		})
   155  	})
   156  
   157  	// Verify Prometheus scraped targets
   158  	// GIVEN OAM hello-helidon app is deployed
   159  	// WHEN the component and appconfig without metrics-trait(using default) are created
   160  	// THEN the application scrape targets must be healthy
   161  	t.Describe("for Metrics.", Label("f:observability.monitoring.prom"), FlakeAttempts(5), func() {
   162  		t.It("Verify all scrape targets are healthy for the application", func() {
   163  			if skipVerify {
   164  				Skip(skipVerifications)
   165  			}
   166  			Eventually(func() (bool, error) {
   167  				var componentNames = []string{"hello-helidon-component"}
   168  				return pkg.ScrapeTargetsHealthy(pkg.GetScrapePools(namespace, helloHelidon, componentNames, isMinVersion140))
   169  			}, shortWaitTimeout, shortPollingInterval).Should(BeTrue())
   170  		})
   171  	})
   172  
   173  	t.Context("Logging.", Label("f:observability.logging.es"), FlakeAttempts(5), func() {
   174  		var indexName string
   175  		Eventually(func() error {
   176  			indexName, err = pkg.GetOpenSearchAppIndex(namespace)
   177  			return err
   178  		}, shortWaitTimeout, shortPollingInterval).Should(BeNil(), "Expected to get OpenSearch App Index")
   179  
   180  		// GIVEN an application with logging enabled
   181  		// WHEN the Opensearch index is retrieved
   182  		// THEN verify that it is found
   183  		t.It("Verify Opensearch index exists", func() {
   184  			if skipVerify {
   185  				Skip(skipVerifications)
   186  			}
   187  			Eventually(func() bool {
   188  				return pkg.LogIndexFound(indexName)
   189  			}, longWaitTimeout, longPollingInterval).Should(BeTrue(), "Expected to find log index for hello helidon")
   190  		})
   191  
   192  		// GIVEN an application with logging enabled
   193  		// WHEN the log records are retrieved from the Opensearch index
   194  		// THEN verify that at least one recent log record is found
   195  		t.It("Verify recent Opensearch log record exists", func() {
   196  			if skipVerify {
   197  				Skip(skipVerifications)
   198  			}
   199  			Eventually(func() bool {
   200  				return pkg.LogRecordFound(indexName, time.Now().Add(-24*time.Hour), map[string]string{
   201  					"kubernetes.labels.app_oam_dev\\/name": helloHelidon,
   202  					"kubernetes.container_name":            "hello-helidon-container",
   203  				})
   204  			}, longWaitTimeout, longPollingInterval).Should(BeTrue(), "Expected to find a recent log record")
   205  			Eventually(func() bool {
   206  				return pkg.LogRecordFound(indexName, time.Now().Add(-24*time.Hour), map[string]string{
   207  					"kubernetes.labels.app_oam_dev\\/component": "hello-helidon-component",
   208  					"kubernetes.labels.app_oam_dev\\/name":      helloHelidon,
   209  					"kubernetes.container_name":                 "hello-helidon-container",
   210  				})
   211  			}, longWaitTimeout, longPollingInterval).Should(BeTrue(), "Expected to find a recent log record")
   212  		})
   213  	})
   214  
   215  })
   216  
   217  func helloHelidonPodsRunning() bool {
   218  	result, err := pkg.PodsRunning(namespace, expectedPodsHelloHelidon)
   219  	if err != nil {
   220  		AbortSuite(fmt.Sprintf("One or more pods are not running in the namespace: %v, error: %v", namespace, err))
   221  	}
   222  	return result
   223  }
   224  
   225  func appEndpointAccess(url string, hostname string, token string, requestShouldSucceed bool) bool {
   226  	req, err := retryablehttp.NewRequest("GET", url, nil)
   227  	if err != nil {
   228  		t.Logs.Errorf("Unexpected error=%v", err)
   229  		return false
   230  	}
   231  
   232  	kubeconfigPath, err := k8sutil.GetKubeConfigLocation()
   233  	if err != nil {
   234  		t.Logs.Errorf("Unexpected error=%v", err)
   235  		return false
   236  	}
   237  
   238  	httpClient, err := pkg.GetVerrazzanoHTTPClient(kubeconfigPath)
   239  	if err != nil {
   240  		t.Logs.Errorf("Unexpected error=%v", err)
   241  		return false
   242  	}
   243  
   244  	if len(token) > 0 {
   245  		req.Header.Add("Authorization", fmt.Sprintf("Bearer %v", token))
   246  	}
   247  
   248  	req.Host = hostname
   249  	resp, err := httpClient.Do(req)
   250  	if err != nil {
   251  		t.Logs.Errorf("Unexpected error=%v", err)
   252  		return false
   253  	}
   254  	bodyRaw, err := io.ReadAll(resp.Body)
   255  	resp.Body.Close()
   256  	if err != nil {
   257  		t.Logs.Errorf("Unexpected error=%v", err)
   258  		return false
   259  	}
   260  	if requestShouldSucceed {
   261  		if resp.StatusCode != http.StatusOK {
   262  			t.Logs.Errorf("Unexpected status code=%v", resp.StatusCode)
   263  			return false
   264  		}
   265  		// HTTP Server headers should never be returned.
   266  		for headerName, headerValues := range resp.Header {
   267  			if strings.EqualFold(headerName, "Server") {
   268  				t.Logs.Errorf("Unexpected Server header=%v", headerValues)
   269  				return false
   270  			}
   271  		}
   272  		bodyStr := string(bodyRaw)
   273  		if !strings.Contains(bodyStr, "Hello World") {
   274  			t.Logs.Errorf("Unexpected response body=%v", bodyStr)
   275  			return false
   276  		}
   277  	} else {
   278  		if resp.StatusCode == http.StatusOK {
   279  			t.Logs.Errorf("Unexpected status code=%v", resp.StatusCode)
   280  			return false
   281  		}
   282  	}
   283  	return true
   284  }