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 }