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  }