github.com/verrazzano/verrazzano@v1.7.1/tests/e2e/multicluster/verify-rancher/rancher_test.go (about)

     1  // Copyright (c) 2022, 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 rancher_test
     5  
     6  import (
     7  	"context"
     8  	"encoding/json"
     9  	"fmt"
    10  	"os"
    11  	"regexp"
    12  	"time"
    13  
    14  	. "github.com/onsi/ginkgo/v2"
    15  	. "github.com/onsi/gomega"
    16  	"github.com/verrazzano/verrazzano/cluster-operator/apis/clusters/v1alpha1"
    17  	"github.com/verrazzano/verrazzano/cluster-operator/clientset/versioned"
    18  	"github.com/verrazzano/verrazzano/pkg/constants"
    19  	"github.com/verrazzano/verrazzano/tests/e2e/pkg"
    20  	"github.com/verrazzano/verrazzano/tests/e2e/pkg/test/framework"
    21  	corev1 "k8s.io/api/core/v1"
    22  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    23  	"k8s.io/client-go/kubernetes"
    24  )
    25  
    26  const (
    27  	waitTimeout           = 5 * time.Minute
    28  	pollingInterval       = 10 * time.Second
    29  	cattleSystemNamespace = "cattle-system"
    30  	searchTimeWindow      = "1h"
    31  )
    32  
    33  const (
    34  	agentSecName = "verrazzano-cluster-agent"
    35  	regSecName   = "verrazzano-cluster-registration"
    36  )
    37  
    38  var t = framework.NewTestFramework("rancher_test")
    39  
    40  var beforeSuite = t.BeforeSuiteFunc(func() {})
    41  var _ = BeforeSuite(beforeSuite)
    42  var _ = t.AfterEach(func() {})
    43  
    44  var afterSuite = t.AfterSuiteFunc(func() {})
    45  var _ = AfterSuite(afterSuite)
    46  
    47  var _ = t.Describe("Multi Cluster Rancher Validation", Label("f:platform-lcm.install"), func() {
    48  	t.It("Rancher log records do not contain any websocket bad handshake messages", func() {
    49  		// GIVEN existing system logs
    50  		// WHEN the Elasticsearch index for the cattle-system namespace is retrieved
    51  		// THEN it has a limited number of bad socket messages
    52  		adminKubeconfig := os.Getenv("ADMIN_KUBECONFIG")
    53  		indexName, err := pkg.GetOpenSearchSystemIndexWithKC(cattleSystemNamespace, adminKubeconfig)
    54  		Expect(err).ShouldNot(HaveOccurred())
    55  		Eventually(func() bool {
    56  			return pkg.LogIndexFound(indexName)
    57  		}, waitTimeout, pollingInterval).Should(BeTrue(), "Expected to find Elasticsearch index cattle-system")
    58  
    59  		// the presence of bad socket messages does not necessarily indicate that anything is wrong with Rancher
    60  		// from a user's perspective, so just log the number of bad socket messages for informational purposes
    61  		getNumBadSocketMessages()
    62  	})
    63  
    64  	t.Context("When the VMC is updated to the status of the managed cluster", func() {
    65  		var adminClient *versioned.Clientset
    66  		var managedClient *kubernetes.Clientset
    67  		BeforeEach(func() {
    68  			adminKubeconfig := os.Getenv("ADMIN_KUBECONFIG")
    69  			Expect(adminKubeconfig).To(Not(BeEmpty()))
    70  			managedKubeconfig := os.Getenv("MANAGED_KUBECONFIG")
    71  			Expect(managedKubeconfig).To(Not(BeEmpty()))
    72  
    73  			var err error
    74  
    75  			adminClient, err = pkg.GetClusterOperatorClientset(adminKubeconfig)
    76  			Expect(err).ShouldNot(HaveOccurred())
    77  			managedClient, err = pkg.GetKubernetesClientsetForCluster(managedKubeconfig)
    78  			Expect(err).ShouldNot(HaveOccurred())
    79  		})
    80  
    81  		t.It("the VMC status is updated that objects have been pushed to the managed cluster", func() {
    82  			// GIVEN the VMC has been registered
    83  			// WHEN the VMC is retrieved
    84  			// THEN the VMC should have a status condition of Type: Manifest Pushed and Status: True
    85  			Eventually(func() error {
    86  				pkg.Log(pkg.Info, "Waiting for all VMC to have status condition ManifestPushed = True")
    87  				vmcList, err := adminClient.ClustersV1alpha1().VerrazzanoManagedClusters(constants.VerrazzanoMultiClusterNamespace).List(context.TODO(), metav1.ListOptions{})
    88  				if err != nil {
    89  					return err
    90  				}
    91  
    92  				for _, vmc := range vmcList.Items {
    93  					statusPushedFound := false
    94  					for _, condition := range vmc.Status.Conditions {
    95  						if condition.Type == v1alpha1.ConditionManifestPushed && condition.Status == corev1.ConditionFalse {
    96  							return fmt.Errorf("failed to find successful condition for ManifestPushed, VMC %s/%s has condition: %s = %s",
    97  								vmc.Name, vmc.Namespace, condition.Type, condition.Status)
    98  						}
    99  						if condition.Type == v1alpha1.ConditionManifestPushed && condition.Status == corev1.ConditionTrue {
   100  							statusPushedFound = true
   101  						}
   102  					}
   103  					if !statusPushedFound {
   104  						return fmt.Errorf("failed to find expected condition, VMC %s/%s had no condition of type %s",
   105  							vmc.Name, vmc.Namespace, v1alpha1.ConditionManifestPushed)
   106  					}
   107  				}
   108  				return nil
   109  			}).WithPolling(pollingInterval).WithTimeout(waitTimeout).Should(BeNil())
   110  		})
   111  
   112  		t.It("the managed cluster should contain the pushed secrets", func() {
   113  			// GIVEN the VMC has a status of ManifestPushed = True
   114  			// WHEN we search for secrets on a managed cluster
   115  			// THEN we should see that the agent and registration secrets exist in the verrazzano-system namespace
   116  			Eventually(func() error {
   117  				adminSec, err := managedClient.CoreV1().Secrets(constants.VerrazzanoSystemNamespace).Get(context.TODO(), agentSecName, metav1.GetOptions{})
   118  				if err != nil {
   119  					return err
   120  				}
   121  				if adminSec == nil {
   122  					return fmt.Errorf("get admin secret %s returned nil on the managed cluster", agentSecName)
   123  				}
   124  
   125  				managedSec, err := managedClient.CoreV1().Secrets(constants.VerrazzanoSystemNamespace).Get(context.TODO(), regSecName, metav1.GetOptions{})
   126  				if err != nil {
   127  					return err
   128  				}
   129  				if managedSec == nil {
   130  					return fmt.Errorf("get registration secret %s returned nil on the managed cluster", regSecName)
   131  				}
   132  				return nil
   133  			}).WithPolling(pollingInterval).WithTimeout(waitTimeout).Should(BeNil())
   134  		})
   135  	})
   136  })
   137  
   138  func getNumBadSocketMessages() int {
   139  	badSocket := regexp.MustCompile(`websocket: bad handshake`)
   140  	numMessages := 0
   141  	index, err := pkg.GetOpenSearchSystemIndex(cattleSystemNamespace)
   142  	if err != nil {
   143  		pkg.Log(pkg.Error, fmt.Sprintf("Failed to get OpenSearch index: %v", err))
   144  		return -1
   145  	}
   146  
   147  	template :=
   148  		`{
   149  			"size": 1000,
   150  			"sort": [{"@timestamp": {"order": "desc"}}],
   151  			"query": {
   152  				"bool": {
   153  					"filter" : [
   154  						{"match_phrase": {"%s": "%s"}},
   155  						{"range": {"@timestamp": {"gte": "now-%s"}}}
   156  					]
   157  				}
   158  			}
   159  		}`
   160  	query := fmt.Sprintf(template, "kubernetes.labels.app.keyword", "rancher", searchTimeWindow)
   161  	resp, err := pkg.PostOpensearch(fmt.Sprintf("%s/_search", index), query)
   162  	if err != nil {
   163  		pkg.Log(pkg.Error, fmt.Sprintf("Failed to query Elasticsearch: %v", err))
   164  		return -1
   165  	}
   166  	if resp.StatusCode != 200 {
   167  		pkg.Log(pkg.Error, fmt.Sprintf("Failed to query Elasticsearch: status=%d: body=%s", resp.StatusCode, string(resp.Body)))
   168  		return -1
   169  	}
   170  	var result map[string]interface{}
   171  	json.Unmarshal(resp.Body, &result)
   172  
   173  	hits := pkg.Jq(result, "hits", "hits")
   174  	if hits == nil {
   175  		pkg.Log(pkg.Info, "Expected to find hits in log record query results")
   176  		return -1
   177  	}
   178  	pkg.Log(pkg.Info, fmt.Sprintf("Found %d records", len(hits.([]interface{}))))
   179  	if len(hits.([]interface{})) == 0 {
   180  		pkg.Log(pkg.Info, "Expected log record query results to contain at least one hit")
   181  		return -1
   182  	}
   183  	for _, h := range hits.([]interface{}) {
   184  		hit := h.(map[string]interface{})
   185  		src := hit["_source"].(map[string]interface{})
   186  		log := src["log"].(string)
   187  		if badSocket.MatchString(log) {
   188  			numMessages++
   189  		}
   190  	}
   191  
   192  	pkg.Log(pkg.Info, fmt.Sprintf("Found %d bad socket messages over the last hour", numMessages))
   193  	return numMessages
   194  }