github.com/rancher/elemental/tests@v0.0.0-20240517125144-ae048c615b3f/e2e/install_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  	"strings"
    21  	"time"
    22  
    23  	. "github.com/onsi/ginkgo/v2"
    24  	. "github.com/onsi/gomega"
    25  	"github.com/rancher-sandbox/ele-testhelpers/kubectl"
    26  	"github.com/rancher-sandbox/ele-testhelpers/rancher"
    27  	"github.com/rancher-sandbox/ele-testhelpers/tools"
    28  )
    29  
    30  func rolloutDeployment(ns, d string) {
    31  	// NOTE: 1st or 2nd rollout command can sporadically fail, so better to use Eventually here
    32  	Eventually(func() string {
    33  		status, _ := kubectl.RunWithoutErr("rollout", "restart", "deployment/"+d,
    34  			"--namespace", ns)
    35  		return status
    36  	}, tools.SetTimeout(1*time.Minute), 20*time.Second).Should(ContainSubstring("restarted"))
    37  
    38  	// Wait for deployment to be restarted
    39  	Eventually(func() string {
    40  		status, _ := kubectl.RunWithoutErr("rollout", "status", "deployment/"+d,
    41  			"--namespace", ns)
    42  		return status
    43  	}, tools.SetTimeout(2*time.Minute), 30*time.Second).Should(ContainSubstring("successfully rolled out"))
    44  }
    45  
    46  var _ = Describe("E2E - Install Rancher Manager", Label("install"), func() {
    47  	// Create kubectl context
    48  	// Default timeout is too small, so New() cannot be used
    49  	k := &kubectl.Kubectl{
    50  		Namespace:    "",
    51  		PollTimeout:  tools.SetTimeout(300 * time.Second),
    52  		PollInterval: 500 * time.Millisecond,
    53  	}
    54  
    55  	// Define local Kubeconfig file
    56  	localKubeconfig := os.Getenv("HOME") + "/.kube/config"
    57  
    58  	It("Install upstream K8s cluster", func() {
    59  		if strings.Contains(k8sUpstreamVersion, "rke2") {
    60  			// Report to Qase
    61  			testCaseID = 60
    62  
    63  			By("Installing RKE2", func() {
    64  				// Get RKE2 installation script
    65  				fileName := "rke2-install.sh"
    66  				Eventually(func() error {
    67  					return tools.GetFileFromURL("https://get.rke2.io", fileName, true)
    68  				}, tools.SetTimeout(2*time.Minute), 10*time.Second).ShouldNot(HaveOccurred())
    69  
    70  				// Retry in case of (sporadic) failure...
    71  				count := 1
    72  				Eventually(func() error {
    73  					// Execute RKE2 installation
    74  					out, err := exec.Command("sudo", "--preserve-env=INSTALL_RKE2_VERSION", "sh", fileName).CombinedOutput()
    75  					GinkgoWriter.Printf("RKE2 installation loop %d:\n%s\n", count, out)
    76  					count++
    77  					return err
    78  				}, tools.SetTimeout(2*time.Minute), 5*time.Second).Should(BeNil())
    79  			})
    80  
    81  			if clusterType == "hardened" {
    82  				By("Configuring hardened cluster", func() {
    83  					err := exec.Command("sudo", installHardenedScript).Run()
    84  					Expect(err).To(Not(HaveOccurred()))
    85  				})
    86  			}
    87  
    88  			By("Starting RKE2", func() {
    89  				// Copy config file, this allows custom configuration for RKE2 installation
    90  				// NOTE: CopyFile cannot be used, as we need root permissions for this file
    91  				err := exec.Command("sudo", "mkdir", "-p", "/etc/rancher/rke2").Run()
    92  				Expect(err).To(Not(HaveOccurred()))
    93  				err = exec.Command("sudo", "cp", configRKE2Yaml, "/etc/rancher/rke2/config.yaml").Run()
    94  				Expect(err).To(Not(HaveOccurred()))
    95  
    96  				// Activate and start RKE2
    97  				err = exec.Command("sudo", "systemctl", "enable", "--now", "rke2-server.service").Run()
    98  				Expect(err).To(Not(HaveOccurred()))
    99  
   100  				// Delay few seconds before checking
   101  				time.Sleep(tools.SetTimeout(20 * time.Second))
   102  
   103  				err = exec.Command("sudo", "ln", "-s", "/var/lib/rancher/rke2/bin/kubectl", "/usr/local/bin/kubectl").Run()
   104  				Expect(err).To(Not(HaveOccurred()))
   105  			})
   106  
   107  			By("Waiting for RKE2 to be started", func() {
   108  				// Wait for all pods to be started
   109  				err := os.Setenv("KUBECONFIG", "/etc/rancher/rke2/rke2.yaml")
   110  				Expect(err).To(Not(HaveOccurred()))
   111  
   112  				checkList := [][]string{
   113  					{"kube-system", "k8s-app=kube-dns"},
   114  					{"kube-system", "app.kubernetes.io/name=rke2-ingress-nginx"},
   115  				}
   116  				Eventually(func() error {
   117  					return rancher.CheckPod(k, checkList)
   118  				}, tools.SetTimeout(4*time.Minute), 30*time.Second).Should(BeNil())
   119  
   120  				err = k.WaitLabelFilter("kube-system", "Ready", "rke2-ingress-nginx-controller", "app.kubernetes.io/name=rke2-ingress-nginx")
   121  				Expect(err).To(Not(HaveOccurred()))
   122  			})
   123  		} else {
   124  			// Report to Qase
   125  			testCaseID = 59
   126  
   127  			By("Installing K3s", func() {
   128  				// Get K3s installation script
   129  				fileName := "k3s-install.sh"
   130  				Eventually(func() error {
   131  					return tools.GetFileFromURL("https://get.k3s.io", fileName, true)
   132  				}, tools.SetTimeout(2*time.Minute), 10*time.Second).ShouldNot(HaveOccurred())
   133  
   134  				// Set command and arguments
   135  				installCmd := exec.Command("sh", fileName)
   136  				installCmd.Env = append(os.Environ(), "INSTALL_K3S_EXEC=--disable metrics-server")
   137  
   138  				// Retry in case of (sporadic) failure...
   139  				count := 1
   140  				Eventually(func() error {
   141  					// Execute K3s installation
   142  					out, err := installCmd.CombinedOutput()
   143  					GinkgoWriter.Printf("K3s installation loop %d:\n%s\n", count, out)
   144  					count++
   145  					return err
   146  				}, tools.SetTimeout(2*time.Minute), 5*time.Second).Should(BeNil())
   147  			})
   148  
   149  			if clusterType == "hardened" {
   150  				By("Configuring hardened cluster", func() {
   151  					err := exec.Command("sudo", installHardenedScript).Run()
   152  					Expect(err).To(Not(HaveOccurred()))
   153  				})
   154  			}
   155  
   156  			By("Starting K3s", func() {
   157  				err := exec.Command("sudo", "systemctl", "start", "k3s").Run()
   158  				Expect(err).To(Not(HaveOccurred()))
   159  
   160  				// Delay few seconds before checking
   161  				time.Sleep(tools.SetTimeout(20 * time.Second))
   162  			})
   163  
   164  			By("Waiting for K3s to be started", func() {
   165  				// Wait for all pods to be started
   166  				checkList := [][]string{
   167  					{"kube-system", "app=local-path-provisioner"},
   168  					{"kube-system", "k8s-app=kube-dns"},
   169  					{"kube-system", "app.kubernetes.io/name=traefik"},
   170  					{"kube-system", "svccontroller.k3s.cattle.io/svcname=traefik"},
   171  				}
   172  				Eventually(func() error {
   173  					return rancher.CheckPod(k, checkList)
   174  				}, tools.SetTimeout(4*time.Minute), 30*time.Second).Should(BeNil())
   175  			})
   176  		}
   177  
   178  		By("Configuring Kubeconfig file", func() {
   179  			// Copy K3s file in ~/.kube/config
   180  			// NOTE: don't check for error, as it will happen anyway (only K3s or RKE2 is installed at a time)
   181  			file, _ := exec.Command("bash", "-c", "ls /etc/rancher/{k3s,rke2}/{k3s,rke2}.yaml").Output()
   182  			Expect(file).To(Not(BeEmpty()))
   183  			err := tools.CopyFile(strings.Trim(string(file), "\n"), localKubeconfig)
   184  			Expect(err).To(Not(HaveOccurred()))
   185  
   186  			err = os.Setenv("KUBECONFIG", localKubeconfig)
   187  			Expect(err).To(Not(HaveOccurred()))
   188  		})
   189  
   190  		if caType == "private" {
   191  			By("Configuring Private CA", func() {
   192  				out, err := exec.Command(configPrivateCAScript).CombinedOutput()
   193  				GinkgoWriter.Printf("%s\n", out)
   194  				Expect(err).To(Not(HaveOccurred()))
   195  			})
   196  		} else {
   197  			By("Installing CertManager", func() {
   198  				RunHelmCmdWithRetry("repo", "add", "jetstack", "https://charts.jetstack.io")
   199  				RunHelmCmdWithRetry("repo", "update")
   200  
   201  				// Set flags for cert-manager installation
   202  				flags := []string{
   203  					"upgrade", "--install", "cert-manager", "jetstack/cert-manager",
   204  					"--namespace", "cert-manager",
   205  					"--create-namespace",
   206  					"--set", "installCRDs=true",
   207  					"--wait", "--wait-for-jobs",
   208  				}
   209  
   210  				if clusterType == "hardened" {
   211  					flags = append(flags, "--version", certManagerVersion)
   212  				}
   213  
   214  				RunHelmCmdWithRetry(flags...)
   215  
   216  				checkList := [][]string{
   217  					{"cert-manager", "app.kubernetes.io/component=controller"},
   218  					{"cert-manager", "app.kubernetes.io/component=webhook"},
   219  					{"cert-manager", "app.kubernetes.io/component=cainjector"},
   220  				}
   221  				Eventually(func() error {
   222  					return rancher.CheckPod(k, checkList)
   223  				}, tools.SetTimeout(4*time.Minute), 30*time.Second).Should(BeNil())
   224  			})
   225  		}
   226  	})
   227  
   228  	It("Install Rancher Manager", func() {
   229  		// Report to Qase
   230  		testCaseID = 61
   231  
   232  		// Inject secret for Private CA
   233  		if caType == "private" {
   234  			// The namespace must exist before adding secret
   235  			err := exec.Command("kubectl", "create", "namespace", "cattle-system").Run()
   236  			Expect(err).To(Not(HaveOccurred()))
   237  
   238  			_, err = kubectl.RunWithoutErr("create", "secret",
   239  				"--namespace", "cattle-system",
   240  				"tls", "tls-rancher-ingress",
   241  				"--cert=tls.crt",
   242  				"--key=tls.key",
   243  			)
   244  			Expect(err).To(Not(HaveOccurred()))
   245  
   246  			_, err = kubectl.RunWithoutErr("create", "secret",
   247  				"--namespace", "cattle-system",
   248  				"generic", "tls-ca",
   249  				"--from-file=cacerts.pem=./cacerts.pem",
   250  			)
   251  			Expect(err).To(Not(HaveOccurred()))
   252  		}
   253  
   254  		err := rancher.DeployRancherManager(rancherHostname, rancherChannel, rancherVersion, rancherHeadVersion, caType, proxy)
   255  		Expect(err).To(Not(HaveOccurred()))
   256  
   257  		// Wait for all pods to be started
   258  		checkList := [][]string{
   259  			{"cattle-system", "app=rancher"},
   260  			{"cattle-system", "app=rancher-webhook"},
   261  			{"cattle-fleet-local-system", "app=fleet-agent"},
   262  			{"cattle-provisioning-capi-system", "control-plane=controller-manager"},
   263  		}
   264  		Eventually(func() error {
   265  			return rancher.CheckPod(k, checkList)
   266  		}, tools.SetTimeout(10*time.Minute), 30*time.Second).Should(BeNil())
   267  
   268  		// Check issuer for Private CA
   269  		if caType == "private" {
   270  			Eventually(func() error {
   271  				out, err := exec.Command("curl", "-vk", "https://"+rancherHostname).CombinedOutput()
   272  				if err != nil {
   273  					// Show only if there's no error
   274  					GinkgoWriter.Printf("%s\n", out)
   275  				}
   276  				return err
   277  			}, tools.SetTimeout(2*time.Minute), 5*time.Second).Should(Not(HaveOccurred()))
   278  		}
   279  
   280  		By("Configuring kubectl to use Rancher admin user", func() {
   281  			// Getting internal username for admin
   282  			internalUsername, err := kubectl.RunWithoutErr("get", "user",
   283  				"-o", "jsonpath={.items[?(@.username==\"admin\")].metadata.name}",
   284  			)
   285  			Expect(err).To(Not(HaveOccurred()))
   286  			Expect(internalUsername).To(Not(BeEmpty()))
   287  
   288  			// Add token in Rancher Manager
   289  			err = tools.Sed("%ADMIN_USER%", internalUsername, ciTokenYaml)
   290  			Expect(err).To(Not(HaveOccurred()))
   291  			err = kubectl.Apply("default", ciTokenYaml)
   292  			Expect(err).To(Not(HaveOccurred()))
   293  
   294  			// Getting Rancher Manager local cluster CA
   295  			// NOTE: loop until the cmd return something, it could take some time
   296  			var rancherCA string
   297  			Eventually(func() error {
   298  				rancherCA, err = kubectl.RunWithoutErr("get", "secret",
   299  					"--namespace", "cattle-system",
   300  					"tls-rancher-ingress",
   301  					"-o", "jsonpath={.data.tls\\.crt}",
   302  				)
   303  				return err
   304  			}, tools.SetTimeout(2*time.Minute), 5*time.Second).Should(Not(HaveOccurred()))
   305  
   306  			// Copy skel file for ~/.kube/config
   307  			err = tools.CopyFile(localKubeconfigYaml, localKubeconfig)
   308  			Expect(err).To(Not(HaveOccurred()))
   309  
   310  			// Create kubeconfig for local cluster
   311  			err = tools.Sed("%RANCHER_URL%", rancherHostname, localKubeconfig)
   312  			Expect(err).To(Not(HaveOccurred()))
   313  			err = tools.Sed("%RANCHER_CA%", rancherCA, localKubeconfig)
   314  			Expect(err).To(Not(HaveOccurred()))
   315  
   316  			// Set correct file permissions
   317  			_ = exec.Command("chmod", "0600", localKubeconfig).Run()
   318  
   319  			// Remove the "old" kubeconfig file to force the use of the new one
   320  			// NOTE: in fact move it, just to keep it in case of issue
   321  			// Also don't check the returned error, as it will always not equal 0
   322  			_ = exec.Command("bash", "-c", "sudo mv -f /etc/rancher/{k3s,rke2}/{k3s,rke2}.yaml ~/").Run()
   323  		})
   324  
   325  		if testType == "ui" {
   326  			By("Workaround for upgrade test, restart Fleet controller and agent", func() {
   327  				for _, d := range [][]string{
   328  					{"cattle-fleet-local-system", "fleet-agent"},
   329  					{"cattle-fleet-system", "fleet-controller"},
   330  				} {
   331  					rolloutDeployment(d[0], d[1])
   332  				}
   333  			})
   334  		}
   335  	})
   336  
   337  	// Deploy operator in CLI test
   338  	It("Install Elemental Operator if needed", func() {
   339  		if testType == "cli" || testType == "multi" {
   340  			By("Installing Operator for CLI tests", func() {
   341  				// Report to Qase
   342  				testCaseID = 62
   343  
   344  				for _, chart := range []string{"elemental-operator-crds", "elemental-operator"} {
   345  					RunHelmCmdWithRetry("upgrade", "--install", chart,
   346  						operatorRepo+"/"+chart+"-chart",
   347  						"--namespace", "cattle-elemental-system",
   348  						"--create-namespace",
   349  						"--wait", "--wait-for-jobs",
   350  					)
   351  
   352  					// Delay few seconds for all to be installed
   353  					time.Sleep(tools.SetTimeout(20 * time.Second))
   354  				}
   355  
   356  				// Wait for pod to be started
   357  				Eventually(func() error {
   358  					return rancher.CheckPod(k, [][]string{{"cattle-elemental-system", "app=elemental-operator"}})
   359  				}, tools.SetTimeout(4*time.Minute), 30*time.Second).Should(BeNil())
   360  			})
   361  		}
   362  	})
   363  })