github.com/Racer159/jackal@v0.32.7-0.20240401174413-0bd2339e4f2e/src/test/common.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 "bufio" 9 "context" 10 "fmt" 11 "os" 12 "regexp" 13 "runtime" 14 "strings" 15 "testing" 16 17 "slices" 18 19 "github.com/Racer159/jackal/src/pkg/utils/exec" 20 "github.com/defenseunicorns/pkg/helpers" 21 "github.com/stretchr/testify/require" 22 ) 23 24 // JackalE2ETest Struct holding common fields most of the tests will utilize. 25 type JackalE2ETest struct { 26 JackalBinPath string 27 Arch string 28 ApplianceMode bool 29 ApplianceModeKeep bool 30 RunClusterTests bool 31 } 32 33 var logRegex = regexp.MustCompile(`Saving log file to (?P<logFile>.*?\.log)`) 34 35 // GetCLIName looks at the OS and CPU architecture to determine which Jackal binary needs to be run. 36 func GetCLIName() string { 37 var binaryName string 38 switch runtime.GOOS { 39 case "linux": 40 binaryName = "jackal" 41 case "darwin": 42 switch runtime.GOARCH { 43 case "arm64": 44 binaryName = "jackal-mac-apple" 45 default: 46 binaryName = "jackal-mac-intel" 47 } 48 case "windows": 49 if runtime.GOARCH == "amd64" { 50 binaryName = "jackal.exe" 51 } 52 } 53 return binaryName 54 } 55 56 // SetupWithCluster performs actions for each test that requires a K8s cluster. 57 func (e2e *JackalE2ETest) SetupWithCluster(t *testing.T) { 58 if !e2e.RunClusterTests { 59 t.Skip("") 60 } 61 _ = exec.CmdWithPrint("sh", "-c", fmt.Sprintf("%s tools kubectl describe nodes | grep -A 99 Non-terminated", e2e.JackalBinPath)) 62 } 63 64 // Jackal executes a Jackal command. 65 func (e2e *JackalE2ETest) Jackal(args ...string) (string, string, error) { 66 if !slices.Contains(args, "--tmpdir") && !slices.Contains(args, "tools") { 67 tmpdir, err := os.MkdirTemp("", "jackal-") 68 if err != nil { 69 return "", "", err 70 } 71 defer os.RemoveAll(tmpdir) 72 args = append(args, "--tmpdir", tmpdir) 73 } 74 if !slices.Contains(args, "--jackal-cache") && !slices.Contains(args, "tools") && os.Getenv("CI") == "true" { 75 // We make the cache dir relative to the working directory to make it work on the Windows Runners 76 // - they use two drives which filepath.Rel cannot cope with. 77 cwd, err := os.Getwd() 78 if err != nil { 79 return "", "", err 80 } 81 cacheDir, err := os.MkdirTemp(cwd, "jackal-") 82 if err != nil { 83 return "", "", err 84 } 85 args = append(args, "--jackal-cache", cacheDir) 86 defer os.RemoveAll(cacheDir) 87 } 88 return exec.CmdWithContext(context.TODO(), exec.PrintCfg(), e2e.JackalBinPath, args...) 89 } 90 91 // Kubectl executes `jackal tools kubectl ...` 92 func (e2e *JackalE2ETest) Kubectl(args ...string) (string, string, error) { 93 tk := []string{"tools", "kubectl"} 94 args = append(tk, args...) 95 return e2e.Jackal(args...) 96 } 97 98 // CleanFiles removes files and directories that have been created during the test. 99 func (e2e *JackalE2ETest) CleanFiles(files ...string) { 100 for _, file := range files { 101 _ = os.RemoveAll(file) 102 } 103 } 104 105 // GetMismatchedArch determines what architecture our tests are running on, 106 // and returns the opposite architecture. 107 func (e2e *JackalE2ETest) GetMismatchedArch() string { 108 switch e2e.Arch { 109 case "arm64": 110 return "amd64" 111 default: 112 return "arm64" 113 } 114 } 115 116 // GetLogFileContents gets the log file contents from a given run's std error. 117 func (e2e *JackalE2ETest) GetLogFileContents(t *testing.T, stdErr string) string { 118 get, err := helpers.MatchRegex(logRegex, stdErr) 119 require.NoError(t, err) 120 logFile := get("logFile") 121 logContents, err := os.ReadFile(logFile) 122 require.NoError(t, err) 123 return string(logContents) 124 } 125 126 // SetupDockerRegistry uses the host machine's docker daemon to spin up a local registry for testing purposes. 127 func (e2e *JackalE2ETest) SetupDockerRegistry(t *testing.T, port int) { 128 // spin up a local registry 129 registryImage := "registry:2.8.3" 130 err := exec.CmdWithPrint("docker", "run", "-d", "--restart=always", "-p", fmt.Sprintf("%d:5000", port), "--name", fmt.Sprintf("registry-%d", port), registryImage) 131 require.NoError(t, err) 132 } 133 134 // TeardownRegistry removes the local registry. 135 func (e2e *JackalE2ETest) TeardownRegistry(t *testing.T, port int) { 136 // remove the local registry 137 err := exec.CmdWithPrint("docker", "rm", "-f", fmt.Sprintf("registry-%d", port)) 138 require.NoError(t, err) 139 } 140 141 // GetJackalVersion returns the current build/jackal version 142 func (e2e *JackalE2ETest) GetJackalVersion(t *testing.T) string { 143 // Get the version of the CLI 144 stdOut, stdErr, err := e2e.Jackal("version") 145 require.NoError(t, err, stdOut, stdErr) 146 return strings.Trim(stdOut, "\n") 147 } 148 149 // StripMessageFormatting strips any ANSI color codes and extra spaces from a given string 150 func (e2e *JackalE2ETest) StripMessageFormatting(input string) string { 151 // Regex to strip any color codes from the output - https://regex101.com/r/YFyIwC/2 152 ansiRegex := regexp.MustCompile(`\x1b\[(.*?)m`) 153 unAnsiInput := ansiRegex.ReplaceAllString(input, "") 154 // Regex to strip any more than two spaces or newline - https://regex101.com/r/wqQmys/1 155 multiSpaceRegex := regexp.MustCompile(`\s{2,}|\n`) 156 return multiSpaceRegex.ReplaceAllString(unAnsiInput, " ") 157 } 158 159 // NormalizeYAMLFilenames normalizes YAML filenames / paths across Operating Systems (i.e Windows vs Linux) 160 func (e2e *JackalE2ETest) NormalizeYAMLFilenames(input string) string { 161 if runtime.GOOS != "windows" { 162 return input 163 } 164 165 // Match YAML lines that have files in them https://regex101.com/r/C78kRD/1 166 fileMatcher := regexp.MustCompile(`^(?P<start>.* )(?P<file>[^:\n]+\/.*)$`) 167 scanner := bufio.NewScanner(strings.NewReader(input)) 168 169 output := "" 170 for scanner.Scan() { 171 line := scanner.Text() 172 get, err := helpers.MatchRegex(fileMatcher, line) 173 if err != nil { 174 output += line + "\n" 175 continue 176 } 177 output += fmt.Sprintf("%s\"%s\"\n", get("start"), strings.ReplaceAll(get("file"), "/", "\\\\")) 178 } 179 180 return output 181 }