github.com/verrazzano/verrazzano@v1.7.1/tests/e2e/examples/socks/sock_shop_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 socks 5 6 import ( 7 b64 "encoding/base64" 8 "encoding/json" 9 "fmt" 10 "net/http" 11 "os" 12 "strconv" 13 "time" 14 15 . "github.com/onsi/ginkgo/v2" 16 . "github.com/onsi/gomega" 17 "github.com/verrazzano/verrazzano/pkg/k8s/resource" 18 "github.com/verrazzano/verrazzano/pkg/k8sutil" 19 "github.com/verrazzano/verrazzano/tests/e2e/pkg" 20 dump "github.com/verrazzano/verrazzano/tests/e2e/pkg/test/clusterdump" 21 "github.com/verrazzano/verrazzano/tests/e2e/pkg/test/framework" 22 "github.com/verrazzano/verrazzano/tests/e2e/pkg/test/framework/metrics" 23 v1 "k8s.io/api/core/v1" 24 "k8s.io/apimachinery/pkg/api/errors" 25 ) 26 27 const ( 28 shortWaitTimeout = 7 * time.Minute 29 shortPollingInterval = 10 * time.Second 30 waitTimeout = 10 * time.Minute 31 longWaitTimeout = 20 * time.Minute 32 pollingInterval = 30 * time.Second 33 imagePullWaitTimeout = 40 * time.Minute 34 imagePullPollingInterval = 30 * time.Second 35 36 sockshopAppName = "sockshop-appconf" 37 ingress = "orders-ingress-rule" 38 cartsService = "carts" 39 cartsCreds = "carts-coh" 40 operatorCreds = "coherence-operator-config" 41 42 sampleSpringMetric = "http_server_requests_seconds_count" 43 sampleMicronautMetric = "process_start_time_seconds" 44 oamComponent = "app_oam_dev_component" 45 ) 46 47 var sockShop SockShop 48 var username, password string 49 var isMinVersion140 bool 50 51 var ( 52 t = framework.NewTestFramework("socks") 53 generatedNamespace = pkg.GenerateNamespace("sockshop") 54 clusterDump = dump.NewClusterDumpWrapper(t, generatedNamespace) 55 host = "" 56 metricsTest pkg.MetricsTest 57 ) 58 59 // creates the sockshop namespace and applies the components and application yaml 60 var beforeSuite = clusterDump.BeforeSuiteFunc(func() { 61 username = "username" + strconv.FormatInt(time.Now().Unix(), 10) 62 password = b64.StdEncoding.EncodeToString([]byte(time.Now().String())) 63 sockShop = NewSockShop(username, password, pkg.Ingress()) 64 65 variant := getVariant() 66 t.Logs.Infof("*** Socks shop test is running against variant: %s\n", variant) 67 68 if !skipDeploy { 69 start := time.Now() 70 // deploy the application here 71 Eventually(func() (*v1.Namespace, error) { 72 return pkg.CreateNamespace(namespace, map[string]string{"verrazzano-managed": "true"}) 73 }, shortWaitTimeout, shortPollingInterval).ShouldNot(BeNil()) 74 75 Eventually(func() error { 76 file, err := pkg.FindTestDataFile("examples/sock-shop/" + variant + "/sock-shop-comp.yaml") 77 if err != nil { 78 return err 79 } 80 return resource.CreateOrUpdateResourceFromFileInGeneratedNamespace(file, namespace) 81 }, shortWaitTimeout, shortPollingInterval).ShouldNot(HaveOccurred()) 82 83 Eventually(func() error { 84 file, err := pkg.FindTestDataFile("examples/sock-shop/" + variant + "/sock-shop-app.yaml") 85 if err != nil { 86 return err 87 } 88 return resource.CreateOrUpdateResourceFromFileInGeneratedNamespace(file, namespace) 89 }, shortWaitTimeout, shortPollingInterval, "Failed to create Sock Shop application resource").ShouldNot(HaveOccurred()) 90 metrics.Emit(t.Metrics.With("deployment_elapsed_time", time.Since(start).Milliseconds())) 91 } 92 93 t.Logs.Info("Container image pull check") 94 Eventually(func() bool { 95 return pkg.ContainerImagePullWait(namespace, expectedPods) 96 }, imagePullWaitTimeout, imagePullPollingInterval).Should(BeTrue()) 97 98 t.Logs.Info("Sock Shop Application: check expected pods are running") 99 Eventually(func() bool { 100 result, err := pkg.PodsRunning(namespace, expectedPods) 101 if err != nil { 102 AbortSuite(fmt.Sprintf("One or more pods are not running in the namespace: %v, error: %v", namespace, err)) 103 } 104 return result 105 }, longWaitTimeout, pollingInterval).Should(BeTrue(), "Sock Shop Application Failed to Deploy: Pods are not ready") 106 107 t.Logs.Info("Sock Shop Application: check expected Service is running") 108 Eventually(func() bool { 109 result, err := pkg.DoesServiceExist(namespace, cartsService) 110 if err != nil { 111 AbortSuite(fmt.Sprintf("App Service %s is not running in the namespace: %v, error: %v", cartsService, namespace, err)) 112 } 113 return result 114 }, longWaitTimeout, pollingInterval).Should(BeTrue(), "Sock Shop Application Failed to Deploy: Service is not ready") 115 116 t.Logs.Info("Sock Shop Application: check expected VirtualService is ready") 117 Eventually(func() bool { 118 result, err := pkg.DoesVirtualServiceExist(namespace, ingress) 119 if err != nil { 120 AbortSuite(fmt.Sprintf("App VirtualService %s is not running in the namespace: %v, error: %v", ingress, namespace, err)) 121 } 122 return result 123 }, shortWaitTimeout, pollingInterval).Should(BeTrue(), "Sock Shop Application Failed to Deploy: VirtualService is not ready") 124 125 t.Logs.Info("Sock Shop Application: check expected Secrets exist") 126 Eventually(func() bool { 127 result, err := pkg.DoesSecretExist(namespace, cartsCreds) 128 if err != nil { 129 AbortSuite(fmt.Sprintf("App Secret %s does not exist in the namespace: %v, error: %v", cartsCreds, namespace, err)) 130 } 131 return result 132 }, shortWaitTimeout, pollingInterval).Should(BeTrue(), "Sock Shop Application Failed to Deploy: Secret does not exist") 133 134 Eventually(func() bool { 135 result, err := pkg.DoesSecretExist(namespace, operatorCreds) 136 if err != nil { 137 AbortSuite(fmt.Sprintf("App Secret %s does not exist in the namespace: %v, error: %v", operatorCreds, namespace, err)) 138 } 139 return result 140 }, shortWaitTimeout, pollingInterval).Should(BeTrue(), "Sock Shop Application Failed to Deploy: Secret does not exist") 141 142 var err error 143 // Get the host from the Istio gateway resource. 144 start := time.Now() 145 t.Logs.Info("Sock Shop Application: check expected Gateway is ready") 146 Eventually(func() (string, error) { 147 host, err = k8sutil.GetHostnameFromGateway(namespace, "") 148 return host, err 149 }, shortWaitTimeout, shortPollingInterval).Should(Not(BeEmpty()), "Sock Shop Application Failed to Deploy: Gateway is not ready") 150 151 kubeconfig, err := k8sutil.GetKubeConfigLocation() 152 if err != nil { 153 AbortSuite(fmt.Sprintf("Failed to get the Kubeconfig location for the cluster: %v", err)) 154 } 155 metricsTest, err = pkg.NewMetricsTest(kubeconfig, map[string]string{}) 156 if err != nil { 157 AbortSuite(fmt.Sprintf("Failed to create the Metrics test object: %v", err)) 158 } 159 160 metrics.Emit(t.Metrics.With("get_host_name_elapsed_time", time.Since(start).Milliseconds())) 161 162 // checks that all pods are up and running 163 Eventually(sockshopPodsRunning, longWaitTimeout, pollingInterval).Should(BeTrue()) 164 165 kubeconfigPath, err := k8sutil.GetKubeConfigLocation() 166 if err != nil { 167 Fail(fmt.Sprintf("Failed to get default kubeconfig path: %s", err.Error())) 168 } 169 isMinVersion140, err = pkg.IsVerrazzanoMinVersion("1.4.0", kubeconfigPath) 170 if err != nil { 171 Fail(err.Error()) 172 } 173 }) 174 175 var _ = BeforeSuite(beforeSuite) 176 177 // the list of expected pods 178 var expectedPods = []string{ 179 "carts-coh-0", 180 "catalog-coh-0", 181 "orders-coh-0", 182 "payment-coh-0", 183 "shipping-coh-0", 184 "users-coh-0"} 185 186 // user registration template 187 const registerTemp = `{ 188 "username":"%v", 189 "password":"%v", 190 "email":"foo@oracle.com", 191 "firstName":"foo", 192 "lastName":"coo" 193 }` 194 195 var _ = t.AfterEach(func() {}) 196 197 var _ = t.Describe("Sock Shop test", Label("f:app-lcm.oam", 198 "f:app-lcm.helidon-workload", 199 "f:app-lcm.spring-workload", 200 "f:app-lcm.coherence-workload"), func() { 201 202 var hostname = "" 203 var err error 204 t.BeforeEach(func() { 205 Eventually(func() (string, error) { 206 hostname, err = k8sutil.GetHostnameFromGateway(namespace, "") 207 return hostname, err 208 }, waitTimeout, shortPollingInterval).Should(Not(BeEmpty())) 209 }) 210 211 sockShop.SetHostHeader(hostname) 212 213 t.It("SockShop application configuration exists", func() { 214 Eventually(func() bool { 215 appConfig, err := pkg.GetAppConfig(namespace, sockshopAppName) 216 if err != nil { 217 return false 218 } 219 return appConfig != nil 220 }, waitTimeout, pollingInterval).Should(BeTrue(), "Failed to get the application configuration for Sockshop") 221 }) 222 223 t.It("SockShop can be accessed and user can be registered", func() { 224 Eventually(func() (bool, error) { 225 return sockShop.RegisterUser(fmt.Sprintf(registerTemp, username, password), hostname) 226 }, longWaitTimeout, pollingInterval).Should(BeTrue(), "Failed to register SockShop User") 227 }) 228 229 t.It("SockShop can log in with default user", func() { 230 kubeconfigPath, err := k8sutil.GetKubeConfigLocation() 231 Expect(err).ShouldNot(HaveOccurred()) 232 Eventually(func() (*pkg.HTTPResponse, error) { 233 url := fmt.Sprintf("https://%v/login", hostname) 234 return pkg.GetWebPageWithBasicAuth(url, hostname, username, password, kubeconfigPath) 235 }, longWaitTimeout, pollingInterval).Should(pkg.HasStatus(http.StatusOK)) 236 237 }) 238 239 t.It("SockShop can add item to cart", func() { 240 // get the catalog 241 var response *pkg.HTTPResponse 242 Eventually(func() (*pkg.HTTPResponse, error) { 243 var err error 244 response, err = sockShop.GetCatalogItems(hostname) 245 return response, err 246 }, shortWaitTimeout, shortPollingInterval).Should(And(pkg.HasStatus(200), pkg.BodyContains("/catalogue/"))) 247 248 var catalogItems []CatalogItem 249 json.Unmarshal(response.Body, &catalogItems) 250 Expect(catalogItems).ShouldNot(BeEmpty(), "Catalog should not be empty") 251 252 // add items to the cart from the catalog 253 Eventually(func() (*pkg.HTTPResponse, error) { 254 return sockShop.AddToCart(catalogItems[0], hostname) 255 }, shortWaitTimeout, shortPollingInterval).Should(pkg.HasStatus(201)) 256 257 Eventually(func() (*pkg.HTTPResponse, error) { 258 return sockShop.AddToCart(catalogItems[0], hostname) 259 }, shortWaitTimeout, shortPollingInterval).Should(pkg.HasStatus(201)) 260 261 Eventually(func() (*pkg.HTTPResponse, error) { 262 return sockShop.AddToCart(catalogItems[0], hostname) 263 }, shortWaitTimeout, shortPollingInterval).Should(pkg.HasStatus(201)) 264 265 Eventually(func() (*pkg.HTTPResponse, error) { 266 return sockShop.AddToCart(catalogItems[1], hostname) 267 }, shortWaitTimeout, shortPollingInterval).Should(pkg.HasStatus(201)) 268 269 Eventually(func() (*pkg.HTTPResponse, error) { 270 return sockShop.AddToCart(catalogItems[2], hostname) 271 }, shortWaitTimeout, shortPollingInterval).Should(pkg.HasStatus(201)) 272 273 Eventually(func() (*pkg.HTTPResponse, error) { 274 return sockShop.AddToCart(catalogItems[2], hostname) 275 }, shortWaitTimeout, shortPollingInterval).Should(pkg.HasStatus(201)) 276 277 // get the cart 278 Eventually(func() (*pkg.HTTPResponse, error) { 279 var err error 280 response, err = sockShop.GetCartItems(hostname) 281 return response, err 282 }, shortWaitTimeout, shortPollingInterval).Should(pkg.HasStatus(200)) 283 284 var cartItems []CartItem 285 json.Unmarshal(response.Body, &cartItems) 286 Expect(cartItems).ShouldNot(BeEmpty(), "Cart should not be empty") 287 288 // make sure the right items and quantities are in the cart 289 sockShop.CheckCart(cartItems, catalogItems[0], 3) 290 sockShop.CheckCart(cartItems, catalogItems[1], 1) 291 sockShop.CheckCart(cartItems, catalogItems[2], 2) 292 }) 293 294 t.It("SockShop can delete all cart items", func() { 295 var response *pkg.HTTPResponse 296 // get the cart 297 Eventually(func() (*pkg.HTTPResponse, error) { 298 var err error 299 response, err = sockShop.GetCartItems(hostname) 300 return response, err 301 }, shortWaitTimeout, shortPollingInterval).Should(pkg.HasStatus(200)) 302 303 var cartItems []CartItem 304 json.Unmarshal(response.Body, &cartItems) 305 Expect(cartItems).ShouldNot(BeEmpty(), "Cart should not be empty") 306 307 // delete each item 308 for _, item := range cartItems { 309 Eventually(func() (*pkg.HTTPResponse, error) { 310 return sockShop.DeleteCartItem(item, hostname) 311 }, shortWaitTimeout, shortPollingInterval).Should(pkg.HasStatus(202)) 312 } 313 314 // get the cart again - this time the cart should be empty 315 Eventually(func() (*pkg.HTTPResponse, error) { 316 var err error 317 response, err = sockShop.GetCartItems(hostname) 318 return response, err 319 }, shortWaitTimeout, shortPollingInterval).Should(pkg.HasStatus(200)) 320 321 json.Unmarshal(response.Body, &cartItems) 322 Expect(cartItems).Should(BeEmpty(), "Cart should be empty") 323 }) 324 325 // INFO: Front-End will not allow for complete implementation of this test 326 t.It("SockShop can change address", func() { 327 var response *pkg.HTTPResponse 328 Eventually(func() (*pkg.HTTPResponse, error) { 329 var err error 330 response, err = sockShop.ChangeAddress(username, hostname) 331 return response, err 332 }, shortWaitTimeout, shortPollingInterval).Should(pkg.HasStatus(200)) 333 334 sockShop.CheckAddress(response, username) 335 }) 336 337 // INFO: Front-End will not allow for complete implementation of this test 338 t.It("SockShop can change payment", func() { 339 Eventually(func() (*pkg.HTTPResponse, error) { 340 return sockShop.ChangePayment(hostname) 341 }, shortWaitTimeout, shortPollingInterval).Should(pkg.HasStatus(200)) 342 }) 343 344 PIt("SockShop can retrieve orders", func() { 345 //TODO 346 }) 347 348 t.It("Verify '/catalogue' UI endpoint is working.", Label("f:mesh.ingress"), func() { 349 Eventually(func() (*pkg.HTTPResponse, error) { 350 url := fmt.Sprintf("https://%s/catalogue", hostname) 351 return pkg.GetWebPage(url, hostname) 352 }, shortWaitTimeout, shortPollingInterval).Should(And(pkg.HasStatus(http.StatusOK), pkg.BodyContains("For all those leg lovers out there."))) 353 }) 354 355 // Verify all the scrape targets are healthy 356 t.Context("Metrics", Label("f:observability.monitoring.prom"), func() { 357 kubeconfigPath, err := k8sutil.GetKubeConfigLocation() 358 if err != nil { 359 Expect(err).To(BeNil(), fmt.Sprintf("Failed to get default kubeconfig path: %s", err.Error())) 360 } 361 if ok, _ := pkg.IsVerrazzanoMinVersion("1.4.0", kubeconfigPath); ok { 362 t.It("Verify all scrape targets are healthy for the application", func() { 363 Eventually(func() (bool, error) { 364 var componentNames = []string{"carts", "catalog", "orders", "payment", "shipping", "users"} 365 return pkg.ScrapeTargetsHealthy(pkg.GetScrapePools(namespace, "sockshop-appconf", componentNames, true)) 366 }, shortWaitTimeout, shortPollingInterval).Should(BeTrue()) 367 }) 368 } 369 }) 370 }) 371 372 var _ = clusterDump.AfterEach(func() {}) 373 374 // undeploys the application, components, and namespace 375 var afterSuite = clusterDump.AfterSuiteFunc(func() { 376 if !skipUndeploy { 377 start := time.Now() 378 variant := getVariant() 379 t.Logs.Info("Undeploy Sock Shop application") 380 t.Logs.Info("Delete application") 381 382 Eventually(func() error { 383 file, err := pkg.FindTestDataFile("examples/sock-shop/" + variant + "/sock-shop-app.yaml") 384 if err != nil { 385 return err 386 } 387 return resource.DeleteResourceFromFileInGeneratedNamespace(file, namespace) 388 }, shortWaitTimeout, shortPollingInterval).ShouldNot(HaveOccurred()) 389 390 t.Logs.Info("Delete components") 391 Eventually(func() error { 392 file, err := pkg.FindTestDataFile("examples/sock-shop/" + variant + "/sock-shop-comp.yaml") 393 if err != nil { 394 return err 395 } 396 return resource.DeleteResourceFromFileInGeneratedNamespace(file, namespace) 397 }, shortWaitTimeout, shortPollingInterval).ShouldNot(HaveOccurred()) 398 399 t.Logs.Info("Wait for sockshop application to be deleted") 400 Eventually(func() bool { 401 _, err := pkg.GetAppConfig(namespace, sockshopAppName) 402 if err != nil && errors.IsNotFound(err) { 403 return true 404 } 405 if err != nil { 406 t.Logs.Infof("Error getting sockshop appconfig: %v\n", err.Error()) 407 pkg.Log(pkg.Info, fmt.Sprintf("Error getting sockshop appconfig: %v\n", err.Error())) 408 } 409 return false 410 }, shortWaitTimeout, shortPollingInterval).Should(BeTrue()) 411 412 t.Logs.Info("Delete namespace") 413 Eventually(func() error { 414 return pkg.DeleteNamespace(namespace) 415 }, shortWaitTimeout, shortPollingInterval).ShouldNot(HaveOccurred()) 416 417 t.Logs.Info("Wait for namespace finalizer to be removed") 418 Eventually(func() bool { 419 return pkg.CheckNamespaceFinalizerRemoved(namespace) 420 }, shortWaitTimeout, shortPollingInterval).Should(BeTrue()) 421 422 t.Logs.Info("Wait for sockshop namespace to be deleted") 423 Eventually(func() bool { 424 _, err := pkg.GetNamespace(namespace) 425 if err != nil && errors.IsNotFound(err) { 426 return true 427 } 428 if err != nil { 429 t.Logs.Infof("Error getting sockshop namespace: %v\n", err.Error()) 430 pkg.Log(pkg.Info, fmt.Sprintf("Error getting sockshop namespace: %v\n", err.Error())) 431 } 432 return false 433 }, longWaitTimeout, pollingInterval).Should(BeTrue()) 434 metrics.Emit(t.Metrics.With("undeployment_elapsed_time", time.Since(start).Milliseconds())) 435 } 436 }) 437 438 var _ = AfterSuite(afterSuite) 439 440 // sockshopPodsRunning checks whether the application pods are ready 441 func sockshopPodsRunning() bool { 442 result, err := pkg.PodsRunning(namespace, expectedPods) 443 if err != nil { 444 AbortSuite(fmt.Sprintf("One or more pods are not running in the namespace: %v, error: %v", namespace, err)) 445 } 446 return result 447 } 448 449 // getVariant returns the variant of the sock shop application being tested 450 func getVariant() string { 451 // read the variant from the environment - if not specified, default to "helidon" 452 variant := os.Getenv("SOCKS_SHOP_VARIANT") 453 if variant != "helidon" && variant != "micronaut" && variant != "spring" { 454 variant = "helidon" 455 } 456 457 return variant 458 }