github.com/verrazzano/verrazzano@v1.7.1/tests/e2e/multicluster/verify-cluster-sync/cluster_sync_test.go (about)

     1  // Copyright (c) 2022, 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 cluster_sync_test
     5  
     6  import (
     7  	"context"
     8  	"fmt"
     9  	"k8s.io/apimachinery/pkg/util/wait"
    10  	"os"
    11  	"time"
    12  
    13  	"github.com/google/uuid"
    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/cluster-operator/controllers/vmc"
    19  	"github.com/verrazzano/verrazzano/pkg/constants"
    20  	"github.com/verrazzano/verrazzano/pkg/log/vzlog"
    21  	"github.com/verrazzano/verrazzano/pkg/rancherutil"
    22  	"github.com/verrazzano/verrazzano/tests/e2e/pkg"
    23  	"github.com/verrazzano/verrazzano/tests/e2e/pkg/test/framework"
    24  	"k8s.io/apimachinery/pkg/api/errors"
    25  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    26  )
    27  
    28  const (
    29  	waitTimeout       = 10 * time.Minute
    30  	shortWaitTimeout  = 30 * time.Second
    31  	pollingInterval   = 10 * time.Second
    32  	shortPollInterval = 5 * time.Second
    33  )
    34  
    35  var t = framework.NewTestFramework("cluster_sync_test")
    36  
    37  var client *versioned.Clientset
    38  var rc *rancherutil.RancherConfig
    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 testRetry = wait.Backoff{
    48  	Steps:    1,
    49  	Duration: 1 * time.Millisecond,
    50  	Factor:   1.0,
    51  	Jitter:   0.1,
    52  }
    53  
    54  var rancherClusterLabels = map[string]string{"rancher-sync": "enabled"}
    55  var _ = t.Describe("Multi Cluster Rancher Validation", Label("f:platform-lcm.install"), func() {
    56  
    57  	// 1. Create clusters in Rancher with labels that match the selector configured in the Verrazzano resource
    58  	// 2. Delete the cluster in Rancher
    59  	// Verify that the VMC was created and deleted in sync
    60  	t.Context("When cluster is created and deleted in Rancher with the configured labels", func() {
    61  		var clusterName = "rancher-create-delete-" + uuid.NewString()[:7]
    62  		var clusterID string
    63  
    64  		t.BeforeEach(func() {
    65  			client, rc = initializeTestResources()
    66  		})
    67  
    68  		t.It("a VMC is automatically created", func() {
    69  			clusterID = testRancherClusterCreation(rc, client, clusterName, rancherClusterLabels, true)
    70  		})
    71  
    72  		t.It("a VMC is automatically deleted", func() {
    73  			testRancherClusterDeletion(rc, client, clusterName, clusterID)
    74  		})
    75  	})
    76  
    77  	// 1. Create clusters in Rancher with matching selector labels
    78  	// 2. Delete the cluster in Rancher
    79  	// Verify that the VMC was created and deleted in sync
    80  	t.Context("When cluster is created in Rancher without the configured labels", func() {
    81  		var clusterName = "rancher-no-label-" + uuid.NewString()[:7]
    82  		clusterID := ""
    83  		t.BeforeEach(func() {
    84  			client, rc = initializeTestResources()
    85  		})
    86  
    87  		t.AfterEach(func() {
    88  			if clusterID != "" {
    89  				// Delete cluster using Rancher API
    90  				deleted, err := vmc.DeleteClusterFromRancher(rc, clusterID, vzlog.DefaultLogger())
    91  				Expect(err).ShouldNot(HaveOccurred())
    92  				Expect(deleted).To(BeTrue())
    93  			}
    94  		})
    95  
    96  		t.It("a VMC is NOT created", func() {
    97  			clusterID = testRancherClusterCreation(rc, client, clusterName, nil, false)
    98  		})
    99  	})
   100  
   101  	// 1. Create the VMC
   102  	// 2. Delete the VMC
   103  	// Verify the Rancher cluster was created and deleted in sync
   104  	t.Context("When VMCs are created and deleted", func() {
   105  		var clusterName = "vmc-create-delete-" + uuid.NewString()[:7]
   106  
   107  		t.BeforeEach(func() {
   108  			client, rc = initializeTestResources()
   109  		})
   110  
   111  		t.It("a Rancher cluster is automatically created", func() {
   112  			testVMCCreation(rc, client, clusterName)
   113  		})
   114  
   115  		t.It("a Rancher cluster is automatically deleted", func() {
   116  			testVMCDeletion(rc, client, clusterName)
   117  		})
   118  	})
   119  
   120  	// 1. Create the VMC
   121  	// 2. Delete the cluster in Rancher
   122  	// Verify the Rancher cluster is created and then the VMC is deleted
   123  	t.Context("When VMC is created and the cluster is deleted in Rancher", func() {
   124  		var clusterName = "vmc-create-rancher-delete-" + uuid.NewString()[:7]
   125  		var clusterID string
   126  
   127  		t.BeforeEach(func() {
   128  			client, rc = initializeTestResources()
   129  		})
   130  
   131  		t.It("a Rancher cluster is automatically created", func() {
   132  			clusterID = testVMCCreation(rc, client, clusterName)
   133  		})
   134  
   135  		t.It("the VMC is automatically deleted", func() {
   136  			testRancherClusterDeletion(rc, client, clusterName, clusterID)
   137  		})
   138  	})
   139  
   140  	// 1. Create the cluster in Rancher with configured labels
   141  	// 2. Delete the VMC
   142  	// Verify the VMC is created and then the Rancher cluster is deleted
   143  	t.Context("When Rancher cluster is created with configured labels and then the VMC is deleted", func() {
   144  		var clusterName = "rancher-create-vmc-delete-" + uuid.NewString()[:7]
   145  
   146  		t.BeforeEach(func() {
   147  			client, rc = initializeTestResources()
   148  		})
   149  
   150  		t.It("a VMC is automatically created", func() {
   151  			testRancherClusterCreation(rc, client, clusterName, rancherClusterLabels, true)
   152  		})
   153  
   154  		t.It("the Rancher cluster is automatically deleted", func() {
   155  			testVMCDeletion(rc, client, clusterName)
   156  		})
   157  	})
   158  })
   159  
   160  func initializeTestResources() (*versioned.Clientset, *rancherutil.RancherConfig) {
   161  	adminKubeconfig := os.Getenv("ADMIN_KUBECONFIG")
   162  	Expect(adminKubeconfig).To(Not(BeEmpty()), "ADMIN_KUBECONFIG should not be empty")
   163  
   164  	var err error
   165  	client, err = pkg.GetClusterOperatorClientset(adminKubeconfig)
   166  	Expect(err).ShouldNot(HaveOccurred())
   167  
   168  	// Get Rancher API URL and creds
   169  	rc, err = pkg.CreateNewRancherConfig(t.Logs, adminKubeconfig)
   170  	Expect(err).ShouldNot(HaveOccurred())
   171  
   172  	return client, rc
   173  }
   174  
   175  // testRancherClusterCreation tests that a cluster created in Rancher with the right labels results in a VMC
   176  func testRancherClusterCreation(rc *rancherutil.RancherConfig, client *versioned.Clientset, clusterName string, rancherClusterLabels map[string]string, vmcExpected bool) string {
   177  	// GIVEN a Rancher cluster is created using Rancher API/UI
   178  	// WHEN the Rancher cluster is appropriately labeled
   179  	// THEN a VMC is auto-created for that cluster
   180  
   181  	savedRetry := rancherutil.DefaultRetry
   182  	defer func() {
   183  		rancherutil.DefaultRetry = savedRetry
   184  	}()
   185  	rancherutil.DefaultRetry = testRetry
   186  
   187  	// Create cluster in Rancher and label it as specified in the VZ resource installed
   188  	var err error
   189  	clusterID, err := vmc.ImportClusterToRancher(rc, clusterName, rancherClusterLabels, vzlog.DefaultLogger())
   190  	Expect(err).ShouldNot(HaveOccurred())
   191  	pkg.Log(pkg.Info, fmt.Sprintf("Got cluster id %s from Rancher\n", clusterID))
   192  
   193  	if vmcExpected {
   194  		// VMC is expected - assert that it is created
   195  		assertVMCEventuallyCreated(clusterName)
   196  	} else {
   197  		// VMC is not expected - assert that it is not created
   198  		assertVMCConsistentlyNotExists(clusterName, fmt.Sprintf("Making sure that no VMC is created for Rancher cluster %s ", clusterName))
   199  	}
   200  	return clusterID
   201  }
   202  
   203  // assertVMCConsistentlyNotExists asserts that consistently, a VMC with the given cluster name does NOT get created for some period of time
   204  func assertVMCConsistentlyNotExists(clusterName string, msg string) {
   205  	Consistently(func() bool {
   206  		pkg.Log(pkg.Info, msg)
   207  		_, err := client.ClustersV1alpha1().VerrazzanoManagedClusters(constants.VerrazzanoMultiClusterNamespace).Get(context.TODO(), clusterName, metav1.GetOptions{})
   208  		return errors.IsNotFound(err)
   209  	}).WithPolling(shortPollInterval).WithTimeout(shortWaitTimeout).Should(BeTrue())
   210  }
   211  
   212  // assertVMCEventuallyCreated asserts that eventually, a VMC with the given cluster name exists
   213  func assertVMCEventuallyCreated(clusterName string) {
   214  	// Eventually, a VMC with the given cluster name should be created
   215  	Eventually(func() (*v1alpha1.VerrazzanoManagedCluster, error) {
   216  		pkg.Log(pkg.Info, "Waiting for VMC to be created")
   217  		return client.ClustersV1alpha1().VerrazzanoManagedClusters(constants.VerrazzanoMultiClusterNamespace).Get(context.TODO(), clusterName, metav1.GetOptions{})
   218  	}).WithPolling(pollingInterval).WithTimeout(waitTimeout).ShouldNot(BeNil())
   219  }
   220  
   221  // testRancherClusterDeletion tests a cluster deleted in Rancher
   222  func testRancherClusterDeletion(rc *rancherutil.RancherConfig, client *versioned.Clientset, clusterName, clusterID string) {
   223  	// GIVEN a Rancher cluster is deleted using Rancher API/UI
   224  	// WHEN the Rancher cluster is appropriately labeled
   225  	// THEN the VMC for the cluster is deleted
   226  
   227  	savedRetry := rancherutil.DefaultRetry
   228  	defer func() {
   229  		rancherutil.DefaultRetry = savedRetry
   230  	}()
   231  	rancherutil.DefaultRetry = testRetry
   232  
   233  	// The VMC should have the clusterID field set before we attempt to delete
   234  	Eventually(func() bool {
   235  		return verifyRancherRegistration(clusterName)
   236  	}).WithPolling(pollingInterval).WithTimeout(waitTimeout).Should(BeTrue())
   237  
   238  	// Delete cluster using Rancher API
   239  	deleted, err := vmc.DeleteClusterFromRancher(rc, clusterID, vzlog.DefaultLogger())
   240  	Expect(err).ShouldNot(HaveOccurred())
   241  	Expect(deleted).To(BeTrue())
   242  
   243  	// Eventually, a VMC with that cluster name should be deleted
   244  	Eventually(func() bool {
   245  		pkg.Log(pkg.Info, "Waiting for VMC to be deleted")
   246  		_, err := client.ClustersV1alpha1().VerrazzanoManagedClusters(constants.VerrazzanoMultiClusterNamespace).Get(context.TODO(), clusterName, metav1.GetOptions{})
   247  		return errors.IsNotFound(err)
   248  	}).WithPolling(pollingInterval).WithTimeout(waitTimeout).Should(BeTrue())
   249  
   250  	// make sure VMC doesn't get re-created, and remains deleted for some time.
   251  	assertVMCConsistentlyNotExists(clusterName, "Waiting for VMC to remain deleted")
   252  }
   253  
   254  // testVMCCreation tests a VMC created for a managed cluster
   255  func testVMCCreation(rc *rancherutil.RancherConfig, client *versioned.Clientset, clusterName string) string {
   256  	// GIVEN a VMC is created for a cluster
   257  	// WHEN the Rancher clusters are prompted to sync with the VMC
   258  	// THEN a Rancher cluster should be created with the same name
   259  
   260  	savedRetry := rancherutil.DefaultRetry
   261  	defer func() {
   262  		rancherutil.DefaultRetry = savedRetry
   263  	}()
   264  	rancherutil.DefaultRetry = testRetry
   265  
   266  	// Create the VMC resource in the cluster
   267  	Eventually(func() (*v1alpha1.VerrazzanoManagedCluster, error) {
   268  		pkg.Log(pkg.Info, fmt.Sprintf("Attempting to create VMC %s", clusterName))
   269  		vmc := v1alpha1.VerrazzanoManagedCluster{
   270  			ObjectMeta: metav1.ObjectMeta{
   271  				Name: clusterName,
   272  			},
   273  		}
   274  		return client.ClustersV1alpha1().VerrazzanoManagedClusters(constants.VerrazzanoMultiClusterNamespace).Create(context.TODO(), &vmc, metav1.CreateOptions{})
   275  	}).WithPolling(pollingInterval).WithTimeout(waitTimeout).ShouldNot(BeNil())
   276  
   277  	// Verify the cluster is created in Rancher
   278  	Eventually(func() bool {
   279  		return clusterExistsInRancher(rc, clusterName)
   280  	}).WithPolling(pollingInterval).WithTimeout(waitTimeout).Should(BeTrue())
   281  
   282  	clusterID, err := vmc.GetClusterIDFromRancher(rc, clusterName, vzlog.DefaultLogger())
   283  	Expect(err).ShouldNot(HaveOccurred())
   284  	return clusterID
   285  }
   286  
   287  // testVMCDeletion tests a VMC deleted for a managed cluster
   288  func testVMCDeletion(rc *rancherutil.RancherConfig, client *versioned.Clientset, clusterName string) {
   289  	// GIVEN a VMC is deleted from the admin cluster
   290  	// WHEN the Rancher sync process runs
   291  	// THEN a Rancher cluster with that name should be deleted
   292  
   293  	savedRetry := rancherutil.DefaultRetry
   294  	defer func() {
   295  		rancherutil.DefaultRetry = savedRetry
   296  	}()
   297  	rancherutil.DefaultRetry = testRetry
   298  
   299  	// The VMC should have the clusterID field set before we attempt to delete
   300  	Eventually(func() bool {
   301  		return verifyRancherRegistration(clusterName)
   302  	}).WithPolling(pollingInterval).WithTimeout(waitTimeout).Should(BeTrue())
   303  
   304  	// Delete the VMC resource in the cluster
   305  	Eventually(func() error {
   306  		pkg.Log(pkg.Info, fmt.Sprintf("Attempting to delete VMC %s", clusterName))
   307  		return client.ClustersV1alpha1().VerrazzanoManagedClusters(constants.VerrazzanoMultiClusterNamespace).Delete(context.TODO(), clusterName, metav1.DeleteOptions{})
   308  	}).WithPolling(pollingInterval).WithTimeout(waitTimeout).Should(BeNil())
   309  
   310  	Eventually(func() bool {
   311  		return clusterExistsInRancher(rc, clusterName)
   312  	}).WithPolling(pollingInterval).WithTimeout(waitTimeout).Should(BeFalse())
   313  
   314  	Consistently(func() bool {
   315  		return clusterExistsInRancher(rc, clusterName)
   316  	}).WithPolling(shortPollInterval).WithTimeout(shortWaitTimeout).Should(BeFalse())
   317  }
   318  
   319  func verifyRancherRegistration(clusterName string) bool {
   320  	pkg.Log(pkg.Info, fmt.Sprintf("Waiting for Rancher registration to occur for VMC %s", clusterName))
   321  	vmc, err := client.ClustersV1alpha1().VerrazzanoManagedClusters(constants.VerrazzanoMultiClusterNamespace).Get(context.TODO(), clusterName, metav1.GetOptions{})
   322  	if err != nil {
   323  		pkg.Log(pkg.Error, fmt.Sprintf("Failed to get VMC %s from the cluster", clusterName))
   324  		return false
   325  	}
   326  	if vmc.Status.RancherRegistration.ClusterID == "" {
   327  		pkg.Log(pkg.Info, fmt.Sprintf("Cluster ID was empty for VMC %s, waiting until it is set to delete", clusterName))
   328  		return false
   329  	}
   330  	return true
   331  }
   332  
   333  // clusterExistsInRancher returns true if the cluster is listed by the Rancher API
   334  func clusterExistsInRancher(rc *rancherutil.RancherConfig, clusterName string) bool {
   335  	ranchClusters, _, err := vmc.GetAllClustersInRancher(rc, vzlog.DefaultLogger())
   336  	if err != nil {
   337  		pkg.Log(pkg.Error, fmt.Sprintf("Failed to get all clusters in Rancher: %v", err))
   338  		return false
   339  	}
   340  	pkg.Log(pkg.Info, fmt.Sprintf("Looking for cluster %s in Rancher", clusterName))
   341  	for _, cluster := range ranchClusters {
   342  		if cluster.Name == clusterName {
   343  			return true
   344  		}
   345  	}
   346  	return false
   347  }