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 }