github.com/rancher/elemental/tests@v0.0.0-20240517125144-ae048c615b3f/e2e/multi-cluster_test.go (about) 1 /* 2 Copyright © 2022 - 2024 SUSE LLC 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 http://www.apache.org/licenses/LICENSE-2.0 8 Unless required by applicable law or agreed to in writing, software 9 distributed under the License is distributed on an "AS IS" BASIS, 10 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 See the License for the specific language governing permissions and 12 limitations under the License. 13 */ 14 15 package e2e_test 16 17 import ( 18 "os" 19 "os/exec" 20 "strconv" 21 "sync" 22 "time" 23 24 . "github.com/onsi/ginkgo/v2" 25 . "github.com/onsi/gomega" 26 "github.com/rancher-sandbox/ele-testhelpers/kubectl" 27 "github.com/rancher-sandbox/ele-testhelpers/rancher" 28 "github.com/rancher-sandbox/ele-testhelpers/tools" 29 "github.com/rancher/elemental/tests/e2e/helpers/elemental" 30 ) 31 32 var _ = Describe("E2E - Bootstrapping nodes", Label("multi-cluster"), func() { 33 // Define some variables 34 const seedImageName = "seed-image-multi" 35 const machineRegName = "machine-registration-multi" 36 37 var ( 38 basePatterns []YamlPattern 39 globalNodeID int 40 wg sync.WaitGroup 41 ) 42 43 BeforeEach(func() { 44 45 // Patterns to replace 46 basePatterns = []YamlPattern{ 47 { 48 key: "%K8S_VERSION%", 49 value: k8sDownstreamVersion, 50 }, 51 { 52 key: "%SNAP_TYPE%", 53 value: snapType, 54 }, 55 { 56 key: "%PASSWORD%", 57 value: userPassword, 58 }, 59 { 60 key: "%USER%", 61 value: userName, 62 }, 63 { 64 key: "%VM_NAME%", 65 value: vmNameRoot, 66 }, 67 } 68 }) 69 70 It("Configure Libvirt", func() { 71 // Report to Qase 72 testCaseID = 68 73 74 By("Starting default network", func() { 75 // Don't check return code, as the default network could be already removed 76 for _, c := range []string{"net-destroy", "net-undefine"} { 77 _ = exec.Command("sudo", "virsh", c, "default").Run() 78 } 79 80 // Wait a bit between virsh commands 81 time.Sleep(1 * time.Minute) 82 err := exec.Command("sudo", "virsh", "net-create", netDefaultFileName).Run() 83 Expect(err).To(Not(HaveOccurred())) 84 }) 85 }) 86 87 It("Configure and create ISO image", func() { 88 // Report to Qase 89 testCaseID = 38 90 91 By("Adding MachineRegistration", func() { 92 // Set temporary file 93 registrationTmp, err := tools.CreateTemp("machineRegistration") 94 Expect(err).To(Not(HaveOccurred())) 95 defer os.Remove(registrationTmp) 96 97 // Save original file as it may have to be modified twice 98 err = tools.CopyFile(registrationYaml, registrationTmp) 99 Expect(err).To(Not(HaveOccurred())) 100 101 // Create Yaml file 102 for _, p := range basePatterns { 103 err := tools.Sed(p.key, p.value, registrationTmp) 104 Expect(err).To(Not(HaveOccurred())) 105 } 106 107 // Apply to k8s 108 Eventually(func() error { 109 return kubectl.Apply(clusterNS, registrationTmp) 110 }, tools.SetTimeout(2*time.Minute), 10*time.Second).ShouldNot(HaveOccurred()) 111 112 // Check that the machine registration is correctly created 113 CheckCreatedRegistration(clusterNS, "machine-registration-multi") 114 }) 115 116 By("Downloading MachineRegistration file", func() { 117 // Download the new YAML installation config file 118 tokenURL, err := kubectl.RunWithoutErr("get", "MachineRegistration", 119 "--namespace", clusterNS, machineRegName, 120 "-o", "jsonpath={.status.registrationURL}") 121 Expect(err).To(Not(HaveOccurred())) 122 123 Eventually(func() error { 124 return tools.GetFileFromURL(tokenURL, installConfigYaml, false) 125 }, tools.SetTimeout(2*time.Minute), 10*time.Second).ShouldNot(HaveOccurred()) 126 }) 127 128 By("Creating ISO from SeedImage", func() { 129 // Wait for list of OS versions to be populated 130 WaitForOSVersion(clusterNS) 131 132 // Get OSVersion name 133 OSVersion, err := exec.Command(getOSScript, os2Test, "true").Output() 134 Expect(err).To(Not(HaveOccurred())) 135 Expect(OSVersion).To(Not(BeEmpty())) 136 137 // Extract container image URL 138 baseImageURL, err := elemental.GetImageURI(clusterNS, string(OSVersion)) 139 Expect(err).To(Not(HaveOccurred())) 140 Expect(baseImageURL).To(Not(BeEmpty())) 141 142 // Set temporary file 143 seedImageTmp, err := tools.CreateTemp("seedimage") 144 Expect(err).To(Not(HaveOccurred())) 145 defer os.Remove(seedImageTmp) 146 147 // Save original file as it may have to be modified twice 148 err = tools.CopyFile(seedImageYaml, seedImageTmp) 149 Expect(err).To(Not(HaveOccurred())) 150 151 seedImagePatterns := []YamlPattern{ 152 { 153 key: "%BASE_IMAGE%", 154 value: baseImageURL, 155 }, 156 } 157 patterns := append(basePatterns, seedImagePatterns...) 158 159 // Create Yaml file 160 for _, p := range patterns { 161 err := tools.Sed(p.key, p.value, seedImageTmp) 162 Expect(err).To(Not(HaveOccurred())) 163 } 164 165 // Apply to k8s 166 err = kubectl.Apply(clusterNS, seedImageTmp) 167 Expect(err).To(Not(HaveOccurred())) 168 }) 169 }) 170 171 It("Downloading ISO built by SeedImage", func() { 172 // Report to Qase 173 testCaseID = 38 174 175 DownloadBuiltISO(clusterNS, seedImageName, "../../elemental-multi.iso") 176 }) 177 178 It("Create clusters and deploy nodes", func() { 179 // Report to Qase 180 testCaseID = 9 181 182 // Loop on all clusters to create 183 for clusterIndex := 1; clusterIndex <= numberOfClusters; clusterIndex++ { 184 createdClusterName := clusterName + "-" + strconv.Itoa(clusterIndex) 185 186 // Patterns to replace 187 addPatterns := []YamlPattern{ 188 { 189 key: "%CLUSTER_NAME%", 190 value: createdClusterName, 191 }, 192 } 193 patterns := append(basePatterns, addPatterns...) 194 195 By("Creating cluster "+createdClusterName, func() { 196 // Set temporary file 197 clusterTmp, err := tools.CreateTemp(createdClusterName) 198 Expect(err).To(Not(HaveOccurred())) 199 defer os.Remove(clusterTmp) 200 201 // Save original file as it may have to be modified twice 202 err = tools.CopyFile(clusterYaml, clusterTmp) 203 Expect(err).To(Not(HaveOccurred())) 204 205 // Create Yaml file 206 for _, p := range patterns { 207 err := tools.Sed(p.key, p.value, clusterTmp) 208 Expect(err).To(Not(HaveOccurred())) 209 } 210 211 // Apply to k8s 212 err = kubectl.Apply(clusterNS, clusterTmp) 213 Expect(err).To(Not(HaveOccurred())) 214 215 // Check that the cluster is correctly created 216 CheckCreatedCluster(clusterNS, createdClusterName) 217 }) 218 219 By("Creating cluster selector for cluster "+createdClusterName, func() { 220 // Set temporary file 221 selectorTmp, err := tools.CreateTemp("selector") 222 Expect(err).To(Not(HaveOccurred())) 223 defer os.Remove(selectorTmp) 224 225 // Save original file as it may have to be modified twice 226 err = tools.CopyFile(selectorYaml, selectorTmp) 227 Expect(err).To(Not(HaveOccurred())) 228 229 // Create Yaml file 230 for _, p := range patterns { 231 err := tools.Sed(p.key, p.value, selectorTmp) 232 Expect(err).To(Not(HaveOccurred())) 233 } 234 235 // Apply to k8s 236 err = kubectl.Apply(clusterNS, selectorTmp) 237 Expect(err).To(Not(HaveOccurred())) 238 239 // Check that the selector template is correctly created 240 CheckCreatedSelectorTemplate(clusterNS, "selector-"+createdClusterName) 241 }) 242 243 // Loop on node provisionning 244 for nodeIndex := 1; nodeIndex <= 3; nodeIndex++ { 245 // Incremente global node index 246 globalNodeID++ 247 248 // Set node hostname 249 hostName := elemental.SetHostname(vmNameRoot+"-"+createdClusterName, nodeIndex) 250 Expect(hostName).To(Not(BeNil())) 251 252 // Add node in network configuration 253 err := rancher.AddNode(netDefaultFileName, hostName, globalNodeID) 254 Expect(err).To(Not(HaveOccurred())) 255 256 // Get generated MAC address 257 _, macAdrs := GetNodeInfo(hostName) 258 Expect(macAdrs).To(Not(BeNil())) 259 260 wg.Add(1) 261 go func(s, h, m string) { 262 defer wg.Done() 263 defer GinkgoRecover() 264 265 By("Installing node "+h+" on cluster "+createdClusterName, func() { 266 // Execute node deployment in parallel 267 err := exec.Command(s, h, m).Run() 268 Expect(err).To(Not(HaveOccurred())) 269 }) 270 }(installVMScript, hostName, macAdrs) 271 } 272 273 // Wait for all parallel jobs 274 wg.Wait() 275 276 // Add needed label on provisionned nodes 277 for nodeIndex := 1; nodeIndex <= 3; nodeIndex++ { 278 // Set node hostname 279 hostName := elemental.SetHostname(vmNameRoot+"-"+createdClusterName, nodeIndex) 280 Expect(hostName).To(Not(BeNil())) 281 282 // Get node's IP 283 ip := GetNodeIP(hostName) 284 285 // Get MachineInventory name 286 nodeName, err := kubectl.RunWithoutErr("get", "MachineInventory", 287 "--namespace", clusterNS, 288 "-o", "jsonpath={.items[?(@.metadata.annotations.elemental\\.cattle\\.io/registration-ip==\""+ip+"\")].metadata.name}") 289 Expect(err).To(Not(HaveOccurred())) 290 291 // Add label 292 elemental.SetMachineInventoryLabel(clusterNS, nodeName, "clusterName", createdClusterName) 293 294 // Get node information 295 client, _ := GetNodeInfo(hostName) 296 297 // Restart node(s) 298 wg.Add(1) 299 go func(h string, cl *tools.Client) { 300 defer wg.Done() 301 defer GinkgoRecover() 302 303 By("Restarting "+h+" to add it in cluster "+createdClusterName, func() { 304 err := exec.Command("sudo", "virsh", "start", h).Run() 305 Expect(err).To(Not(HaveOccurred())) 306 }) 307 308 By("Checking "+h+" SSH connection", func() { 309 CheckSSH(cl) 310 }) 311 }(hostName, client) 312 } 313 314 // Wait for all parallel jobs 315 wg.Wait() 316 317 By("Waiting for cluster "+createdClusterName+" to be Active", func() { 318 WaitCluster(clusterNS, createdClusterName) 319 }) 320 } 321 322 // Loop on all clusters to check 323 for clusterIndex := 1; clusterIndex <= numberOfClusters; clusterIndex++ { 324 createdClusterName := clusterName + "-" + strconv.Itoa(clusterIndex) 325 326 // Do a final check on all created clusters to validate them 327 // NOTE: do it in parallel to speed-up the checking process 328 wg.Add(1) 329 go func(ns, c string) { 330 defer wg.Done() 331 defer GinkgoRecover() 332 By("Waiting for cluster "+c+" to be Active", func() { 333 WaitCluster(ns, c) 334 }) 335 }(clusterNS, createdClusterName) 336 337 // Wait for all parallel jobs 338 wg.Wait() 339 } 340 }) 341 })