github.com/opentofu/opentofu@v1.7.1/internal/cloud/e2e/main_test.go (about) 1 // Copyright (c) The OpenTofu Authors 2 // SPDX-License-Identifier: MPL-2.0 3 // Copyright (c) 2023 HashiCorp, Inc. 4 // SPDX-License-Identifier: MPL-2.0 5 6 package main 7 8 import ( 9 "flag" 10 "fmt" 11 "log" 12 "os" 13 "os/exec" 14 "strings" 15 "testing" 16 17 expect "github.com/Netflix/go-expect" 18 tfe "github.com/hashicorp/go-tfe" 19 "github.com/opentofu/opentofu/internal/e2e" 20 tfversion "github.com/opentofu/opentofu/version" 21 ) 22 23 var tofuBin string 24 var cliConfigFileEnv string 25 26 var tfeClient *tfe.Client 27 var tfeHostname string 28 var tfeToken string 29 var verboseMode bool 30 31 func TestMain(m *testing.M) { 32 teardown := setup() 33 code := m.Run() 34 teardown() 35 36 os.Exit(code) 37 } 38 39 func accTest() bool { 40 // TF_ACC is set when we want to run acceptance tests, meaning it relies on 41 // network access. 42 return os.Getenv("TF_ACC") != "" 43 } 44 45 func hasHostname() bool { 46 return os.Getenv("TFE_HOSTNAME") != "" 47 } 48 49 func hasToken() bool { 50 return os.Getenv("TFE_TOKEN") != "" 51 } 52 53 func hasRequiredEnvVars() bool { 54 return accTest() && hasHostname() && hasToken() 55 } 56 57 func skipIfMissingEnvVar(t *testing.T) { 58 if !hasRequiredEnvVars() { 59 t.Skip("Skipping test, required environment variables missing. Use `TF_ACC`, `TFE_HOSTNAME`, `TFE_TOKEN`") 60 } 61 } 62 63 func setup() func() { 64 tfOutput := flag.Bool("tfoutput", false, "This flag produces the terraform output from tests.") 65 flag.Parse() 66 verboseMode = *tfOutput 67 68 setTfeClient() 69 teardown := setupBinary() 70 71 return func() { 72 teardown() 73 } 74 } 75 func testRunner(t *testing.T, cases testCases, orgCount int, tfEnvFlags ...string) { 76 for name, tc := range cases { 77 tc := tc // rebind tc into this lexical scope 78 t.Run(name, func(subtest *testing.T) { 79 subtest.Parallel() 80 81 orgNames := []string{} 82 for i := 0; i < orgCount; i++ { 83 organization, cleanup := createOrganization(t) 84 t.Cleanup(cleanup) 85 orgNames = append(orgNames, organization.Name) 86 } 87 88 exp, err := expect.NewConsole(defaultOpts()...) 89 if err != nil { 90 subtest.Fatal(err) 91 } 92 defer exp.Close() 93 94 tmpDir := t.TempDir() 95 96 tf := e2e.NewBinary(t, tofuBin, tmpDir) 97 tfEnvFlags = append(tfEnvFlags, "TF_LOG=INFO") 98 tfEnvFlags = append(tfEnvFlags, cliConfigFileEnv) 99 for _, env := range tfEnvFlags { 100 tf.AddEnv(env) 101 } 102 103 var orgName string 104 for index, op := range tc.operations { 105 switch orgCount { 106 case 0: 107 orgName = "" 108 case 1: 109 orgName = orgNames[0] 110 default: 111 orgName = orgNames[index] 112 } 113 114 op.prep(t, orgName, tf.WorkDir()) 115 for _, tfCmd := range op.commands { 116 cmd := tf.Cmd(tfCmd.command...) 117 cmd.Stdin = exp.Tty() 118 cmd.Stdout = exp.Tty() 119 cmd.Stderr = exp.Tty() 120 121 err = cmd.Start() 122 if err != nil { 123 subtest.Fatal(err) 124 } 125 126 if tfCmd.expectedCmdOutput != "" { 127 got, err := exp.ExpectString(tfCmd.expectedCmdOutput) 128 if err != nil { 129 subtest.Fatalf("error while waiting for output\nwant: %s\nerror: %s\noutput\n%s", tfCmd.expectedCmdOutput, err, got) 130 } 131 } 132 133 lenInput := len(tfCmd.userInput) 134 lenInputOutput := len(tfCmd.postInputOutput) 135 if lenInput > 0 { 136 for i := 0; i < lenInput; i++ { 137 input := tfCmd.userInput[i] 138 exp.SendLine(input) 139 // use the index to find the corresponding 140 // output that matches the input. 141 if lenInputOutput-1 >= i { 142 output := tfCmd.postInputOutput[i] 143 _, err := exp.ExpectString(output) 144 if err != nil { 145 subtest.Fatal(err) 146 } 147 } 148 } 149 } 150 151 err = cmd.Wait() 152 if err != nil && !tfCmd.expectError { 153 subtest.Fatal(err) 154 } 155 } 156 } 157 158 if tc.validations != nil { 159 tc.validations(t, orgName) 160 } 161 }) 162 } 163 } 164 165 func setTfeClient() { 166 tfeHostname = os.Getenv("TFE_HOSTNAME") 167 tfeToken = os.Getenv("TFE_TOKEN") 168 169 cfg := &tfe.Config{ 170 Address: fmt.Sprintf("https://%s", tfeHostname), 171 Token: tfeToken, 172 } 173 174 if tfeHostname != "" && tfeToken != "" { 175 // Create a new TFE client. 176 client, err := tfe.NewClient(cfg) 177 if err != nil { 178 fmt.Printf("Could not create new tfe client: %v\n", err) 179 os.Exit(1) 180 } 181 tfeClient = client 182 } 183 } 184 185 func setupBinary() func() { 186 log.Println("Setting up terraform binary") 187 tmpTerraformBinaryDir, err := os.MkdirTemp("", "terraform-test") 188 if err != nil { 189 fmt.Printf("Could not create temp directory: %v\n", err) 190 os.Exit(1) 191 } 192 log.Println(tmpTerraformBinaryDir) 193 currentDir, err := os.Getwd() 194 defer os.Chdir(currentDir) 195 if err != nil { 196 fmt.Printf("Could not change directories: %v\n", err) 197 os.Exit(1) 198 } 199 // Getting top level dir 200 dirPaths := strings.Split(currentDir, "/") 201 log.Println(currentDir) 202 topLevel := len(dirPaths) - 3 203 topDir := strings.Join(dirPaths[0:topLevel], "/") 204 205 if err := os.Chdir(topDir); err != nil { 206 fmt.Printf("Could not change directories: %v\n", err) 207 os.Exit(1) 208 } 209 210 cmd := exec.Command( 211 "go", 212 "build", 213 "-o", tmpTerraformBinaryDir, 214 "-ldflags", fmt.Sprintf("-X \"github.com/opentofu/opentofu/version.Prerelease=%s\"", tfversion.Prerelease), 215 "./cmd/tofu", 216 ) 217 err = cmd.Run() 218 if err != nil { 219 fmt.Printf("Could not run exec command: %v\n", err) 220 os.Exit(1) 221 } 222 223 credFile := fmt.Sprintf("%s/dev.tfrc", tmpTerraformBinaryDir) 224 writeCredRC(credFile) 225 226 tofuBin = fmt.Sprintf("%s/terraform", tmpTerraformBinaryDir) 227 cliConfigFileEnv = fmt.Sprintf("TF_CLI_CONFIG_FILE=%s", credFile) 228 229 return func() { 230 os.RemoveAll(tmpTerraformBinaryDir) 231 } 232 } 233 234 func writeCredRC(file string) { 235 creds := credentialBlock() 236 f, err := os.Create(file) 237 if err != nil { 238 fmt.Printf("Could not create file: %v\n", err) 239 os.Exit(1) 240 } 241 _, err = f.WriteString(creds) 242 if err != nil { 243 fmt.Printf("Could not write credentials: %v\n", err) 244 os.Exit(1) 245 } 246 f.Close() 247 } 248 249 func credentialBlock() string { 250 return fmt.Sprintf(` 251 credentials "%s" { 252 token = "%s" 253 }`, tfeHostname, tfeToken) 254 }