github.com/Racer159/jackal@v0.32.7-0.20240401174413-0bd2339e4f2e/src/test/e2e/25_helm_test.go (about) 1 // SPDX-License-Identifier: Apache-2.0 2 // SPDX-FileCopyrightText: 2021-Present The Jackal Authors 3 4 // Package test provides e2e tests for Jackal. 5 package test 6 7 import ( 8 "fmt" 9 "os/exec" 10 "path/filepath" 11 "testing" 12 13 "github.com/stretchr/testify/require" 14 ) 15 16 var helmChartsPkg string 17 18 func TestHelm(t *testing.T) { 19 t.Log("E2E: Helm chart") 20 e2e.SetupWithCluster(t) 21 22 helmChartsPkg = filepath.Join("build", fmt.Sprintf("jackal-package-helm-charts-%s-0.0.1.tar.zst", e2e.Arch)) 23 24 testHelmUninstallRollback(t) 25 26 testHelmAdoption(t) 27 28 t.Run("helm charts example", testHelmChartsExample) 29 30 t.Run("helm escaping", testHelmEscaping) 31 } 32 33 func testHelmChartsExample(t *testing.T) { 34 t.Parallel() 35 t.Log("E2E: Helm chart example") 36 tmpdir := t.TempDir() 37 38 // Create a package that has a tarball as a local chart 39 localTgzChartPath := filepath.Join("src", "test", "packages", "25-local-tgz-chart") 40 stdOut, stdErr, err := e2e.Jackal("package", "create", localTgzChartPath, "--tmpdir", tmpdir, "--confirm") 41 require.NoError(t, err, stdOut, stdErr) 42 defer e2e.CleanFiles(fmt.Sprintf("jackal-package-helm-charts-local-tgz-%s-0.0.1.tar.zst", e2e.Arch)) 43 44 // Create a package that needs dependencies 45 evilChartDepsPath := filepath.Join("src", "test", "packages", "25-evil-chart-deps") 46 stdOut, stdErr, err = e2e.Jackal("package", "create", evilChartDepsPath, "--tmpdir", tmpdir, "--confirm") 47 require.Error(t, err, stdOut, stdErr) 48 require.Contains(t, e2e.StripMessageFormatting(stdErr), "could not download https://charts.jetstack.io/charts/cert-manager-v1.11.1.tgz") 49 require.FileExists(t, filepath.Join(evilChartDepsPath, "good-chart", "charts", "gitlab-runner-0.55.0.tgz")) 50 51 // Create a package with a chart name that doesn't exist in a repo 52 evilChartLookupPath := filepath.Join("src", "test", "packages", "25-evil-chart-lookup") 53 stdOut, stdErr, err = e2e.Jackal("package", "create", evilChartLookupPath, "--tmpdir", tmpdir, "--confirm") 54 require.Error(t, err, stdOut, stdErr) 55 require.Contains(t, e2e.StripMessageFormatting(stdErr), "chart \"asdf\" version \"6.4.0\" not found") 56 require.Contains(t, e2e.StripMessageFormatting(stdErr), "Available charts and versions from \"https://stefanprodan.github.io/podinfo\":") 57 58 // Create a test package (with a registry override (host+subpath to host+subpath) to test that as well) 59 stdOut, stdErr, err = e2e.Jackal("package", "create", "examples/helm-charts", "-o", "build", "--registry-override", "ghcr.io/stefanprodan=docker.io/stefanprodan", "--tmpdir", tmpdir, "--confirm") 60 require.NoError(t, err, stdOut, stdErr) 61 62 // Create a test package (with a registry override (host to host+subpath) to test that as well) 63 // expect to fail as ghcr.io is overridden and the expected final image doesn't exist but the override works well based on the error message in the output 64 stdOut, stdErr, err = e2e.Jackal("package", "create", "examples/helm-charts", "-o", "build", "--registry-override", "ghcr.io=localhost:555/noway", "--tmpdir", tmpdir, "--confirm") 65 require.Error(t, err, stdOut, stdErr) 66 require.Contains(t, string(stdErr), "localhost:555/noway") 67 68 // Create a test package (with a registry override (host+subpath to host) to test that as well) 69 // works same as the above failing test 70 stdOut, stdErr, err = e2e.Jackal("package", "create", "examples/helm-charts", "-o", "build", "--registry-override", "ghcr.io/stefanprodan=localhost:555", "--tmpdir", tmpdir, "--confirm") 71 require.Error(t, err, stdOut, stdErr) 72 require.Contains(t, string(stdErr), "localhost:555") 73 74 // Create the package (with a registry override (host to host) to test that as well) 75 stdOut, stdErr, err = e2e.Jackal("package", "create", "examples/helm-charts", "-o", "build", "--registry-override", "ghcr.io=docker.io", "--tmpdir", tmpdir, "--confirm") 76 require.NoError(t, err, stdOut, stdErr) 77 78 // Deploy the example package. 79 stdOut, stdErr, err = e2e.Jackal("package", "deploy", helmChartsPkg, "--confirm") 80 require.NoError(t, err, stdOut, stdErr) 81 require.Contains(t, string(stdErr), "registryOverrides", "registry overrides was not saved to build data") 82 require.Contains(t, string(stdErr), "docker.io", "docker.io not found in registry overrides") 83 84 // Remove the example package. 85 stdOut, stdErr, err = e2e.Jackal("package", "remove", "helm-charts", "--confirm") 86 require.NoError(t, err, stdOut, stdErr) 87 } 88 89 func testHelmEscaping(t *testing.T) { 90 t.Parallel() 91 t.Log("E2E: Helm chart escaping") 92 93 // Create the package. 94 stdOut, stdErr, err := e2e.Jackal("package", "create", "src/test/packages/25-evil-templates/", "--confirm") 95 require.NoError(t, err, stdOut, stdErr) 96 97 path := fmt.Sprintf("jackal-package-evil-templates-%s.tar.zst", e2e.Arch) 98 99 // Deploy the package. 100 stdOut, stdErr, err = e2e.Jackal("package", "deploy", path, "--confirm") 101 require.NoError(t, err, stdOut, stdErr) 102 103 // Verify the configmap was deployed, escaped, and contains all of its data 104 kubectlOut, _ := exec.Command("kubectl", "describe", "cm", "dont-template-me").Output() 105 require.Contains(t, string(kubectlOut), `alert: OOMKilled {{ "{{ \"random.Values\" }}" }}`) 106 require.Contains(t, string(kubectlOut), "backtick1: \"content with backticks `some random things`\"") 107 require.Contains(t, string(kubectlOut), "backtick2: \"nested templating with backticks {{` random.Values `}}\"") 108 require.Contains(t, string(kubectlOut), `description: Pod {{$labels.pod}} in {{$labels.namespace}} got OOMKilled`) 109 require.Contains(t, string(kubectlOut), `TG9yZW0gaXBzdW0gZG9sb3Igc2l0IGFtZXQsIGNvbnNlY3RldHVyIG`) 110 111 // Remove the package. 112 stdOut, stdErr, err = e2e.Jackal("package", "remove", "evil-templates", "--confirm") 113 require.NoError(t, err, stdOut, stdErr) 114 } 115 116 func testHelmUninstallRollback(t *testing.T) { 117 t.Log("E2E: Helm Uninstall and Rollback") 118 119 goodPath := fmt.Sprintf("build/jackal-package-dos-games-%s-1.0.0.tar.zst", e2e.Arch) 120 evilPath := fmt.Sprintf("jackal-package-dos-games-%s.tar.zst", e2e.Arch) 121 122 // Create the evil package (with the bad service). 123 stdOut, stdErr, err := e2e.Jackal("package", "create", "src/test/packages/25-evil-dos-games/", "--skip-sbom", "--confirm") 124 require.NoError(t, err, stdOut, stdErr) 125 126 // Deploy the evil package. 127 stdOut, stdErr, err = e2e.Jackal("package", "deploy", evilPath, "--timeout", "10s", "--confirm") 128 require.Error(t, err, stdOut, stdErr) 129 130 // This package contains SBOMable things but was created with --skip-sbom 131 require.Contains(t, string(stdErr), "This package does NOT contain an SBOM.") 132 133 // Ensure that this does not leave behind a dos-games chart 134 helmOut, err := exec.Command("helm", "list", "-n", "dos-games").Output() 135 require.NoError(t, err) 136 require.NotContains(t, string(helmOut), "jackal-f53a99d4a4dd9a3575bedf59cd42d48d751ae866") 137 138 // Deploy the good package. 139 stdOut, stdErr, err = e2e.Jackal("package", "deploy", goodPath, "--confirm") 140 require.NoError(t, err, stdOut, stdErr) 141 142 // Ensure that this does create a dos-games chart 143 helmOut, err = exec.Command("helm", "list", "-n", "dos-games").Output() 144 require.NoError(t, err) 145 require.Contains(t, string(helmOut), "jackal-f53a99d4a4dd9a3575bedf59cd42d48d751ae866") 146 147 // Deploy the evil package. 148 stdOut, stdErr, err = e2e.Jackal("package", "deploy", evilPath, "--timeout", "10s", "--confirm") 149 require.Error(t, err, stdOut, stdErr) 150 151 // Ensure that we rollback properly 152 helmOut, err = exec.Command("helm", "history", "-n", "dos-games", "jackal-f53a99d4a4dd9a3575bedf59cd42d48d751ae866", "--max", "1").Output() 153 require.NoError(t, err) 154 require.Contains(t, string(helmOut), "Rollback to 1") 155 156 // Deploy the evil package (again to ensure we check full history) 157 stdOut, stdErr, err = e2e.Jackal("package", "deploy", evilPath, "--timeout", "10s", "--confirm") 158 require.Error(t, err, stdOut, stdErr) 159 160 // Ensure that we rollback properly 161 helmOut, err = exec.Command("helm", "history", "-n", "dos-games", "jackal-f53a99d4a4dd9a3575bedf59cd42d48d751ae866", "--max", "1").Output() 162 require.NoError(t, err) 163 require.Contains(t, string(helmOut), "Rollback to 5") 164 165 // Remove the package. 166 stdOut, stdErr, err = e2e.Jackal("package", "remove", "dos-games", "--confirm") 167 require.NoError(t, err, stdOut, stdErr) 168 } 169 170 func testHelmAdoption(t *testing.T) { 171 t.Log("E2E: Helm Adopt a Deployment") 172 173 packagePath := fmt.Sprintf("build/jackal-package-dos-games-%s-1.0.0.tar.zst", e2e.Arch) 174 deploymentManifest := "src/test/packages/25-manifest-adoption/deployment.yaml" 175 176 // Deploy dos-games manually into the cluster without Jackal 177 kubectlOut, _, _ := e2e.Kubectl("apply", "-f", deploymentManifest) 178 require.Contains(t, string(kubectlOut), "deployment.apps/game created") 179 180 // Deploy dos-games into the cluster with Jackal 181 stdOut, stdErr, err := e2e.Jackal("package", "deploy", packagePath, "--confirm", "--adopt-existing-resources") 182 require.NoError(t, err, stdOut, stdErr) 183 184 // Ensure that this does create a dos-games chart 185 helmOut, err := exec.Command("helm", "list", "-n", "dos-games").Output() 186 require.NoError(t, err) 187 require.Contains(t, string(helmOut), "jackal-f53a99d4a4dd9a3575bedf59cd42d48d751ae866") 188 189 // Remove the package. 190 stdOut, stdErr, err = e2e.Jackal("package", "remove", "dos-games", "--confirm") 191 require.NoError(t, err, stdOut, stdErr) 192 }