| package main |
| |
| import ( |
| "flag" |
| "fmt" |
| "io/ioutil" |
| "log" |
| "os" |
| "os/exec" |
| "strings" |
| "testing" |
| |
| expect "github.com/Netflix/go-expect" |
| tfe "github.com/hashicorp/go-tfe" |
| "github.com/hashicorp/terraform/internal/e2e" |
| tfversion "github.com/hashicorp/terraform/version" |
| ) |
| |
| var terraformBin string |
| var cliConfigFileEnv string |
| |
| var tfeClient *tfe.Client |
| var tfeHostname string |
| var tfeToken string |
| var verboseMode bool |
| |
| func TestMain(m *testing.M) { |
| teardown := setup() |
| code := m.Run() |
| teardown() |
| |
| os.Exit(code) |
| } |
| |
| func accTest() bool { |
| // TF_ACC is set when we want to run acceptance tests, meaning it relies on |
| // network access. |
| return os.Getenv("TF_ACC") != "" |
| } |
| |
| func hasHostname() bool { |
| return os.Getenv("TFE_HOSTNAME") != "" |
| } |
| |
| func hasToken() bool { |
| return os.Getenv("TFE_TOKEN") != "" |
| } |
| |
| func hasRequiredEnvVars() bool { |
| return accTest() && hasHostname() && hasToken() |
| } |
| |
| func skipIfMissingEnvVar(t *testing.T) { |
| if !hasRequiredEnvVars() { |
| t.Skip("Skipping test, required environment variables missing. Use `TF_ACC`, `TFE_HOSTNAME`, `TFE_TOKEN`") |
| } |
| } |
| |
| func setup() func() { |
| tfOutput := flag.Bool("tfoutput", false, "This flag produces the terraform output from tests.") |
| flag.Parse() |
| verboseMode = *tfOutput |
| |
| setTfeClient() |
| teardown := setupBinary() |
| |
| return func() { |
| teardown() |
| } |
| } |
| func testRunner(t *testing.T, cases testCases, orgCount int, tfEnvFlags ...string) { |
| for name, tc := range cases { |
| tc := tc // rebind tc into this lexical scope |
| t.Run(name, func(subtest *testing.T) { |
| subtest.Parallel() |
| |
| orgNames := []string{} |
| for i := 0; i < orgCount; i++ { |
| organization, cleanup := createOrganization(t) |
| t.Cleanup(cleanup) |
| orgNames = append(orgNames, organization.Name) |
| } |
| |
| exp, err := expect.NewConsole(defaultOpts()...) |
| if err != nil { |
| subtest.Fatal(err) |
| } |
| defer exp.Close() |
| |
| tmpDir := t.TempDir() |
| |
| tf := e2e.NewBinary(t, terraformBin, tmpDir) |
| tfEnvFlags = append(tfEnvFlags, "TF_LOG=INFO") |
| tfEnvFlags = append(tfEnvFlags, cliConfigFileEnv) |
| for _, env := range tfEnvFlags { |
| tf.AddEnv(env) |
| } |
| |
| var orgName string |
| for index, op := range tc.operations { |
| switch orgCount { |
| case 0: |
| orgName = "" |
| case 1: |
| orgName = orgNames[0] |
| default: |
| orgName = orgNames[index] |
| } |
| |
| op.prep(t, orgName, tf.WorkDir()) |
| for _, tfCmd := range op.commands { |
| cmd := tf.Cmd(tfCmd.command...) |
| cmd.Stdin = exp.Tty() |
| cmd.Stdout = exp.Tty() |
| cmd.Stderr = exp.Tty() |
| |
| err = cmd.Start() |
| if err != nil { |
| subtest.Fatal(err) |
| } |
| |
| if tfCmd.expectedCmdOutput != "" { |
| got, err := exp.ExpectString(tfCmd.expectedCmdOutput) |
| if err != nil { |
| subtest.Fatalf("error while waiting for output\nwant: %s\nerror: %s\noutput\n%s", tfCmd.expectedCmdOutput, err, got) |
| } |
| } |
| |
| lenInput := len(tfCmd.userInput) |
| lenInputOutput := len(tfCmd.postInputOutput) |
| if lenInput > 0 { |
| for i := 0; i < lenInput; i++ { |
| input := tfCmd.userInput[i] |
| exp.SendLine(input) |
| // use the index to find the corresponding |
| // output that matches the input. |
| if lenInputOutput-1 >= i { |
| output := tfCmd.postInputOutput[i] |
| _, err := exp.ExpectString(output) |
| if err != nil { |
| subtest.Fatal(err) |
| } |
| } |
| } |
| } |
| |
| err = cmd.Wait() |
| if err != nil && !tfCmd.expectError { |
| subtest.Fatal(err) |
| } |
| } |
| } |
| |
| if tc.validations != nil { |
| tc.validations(t, orgName) |
| } |
| }) |
| } |
| } |
| |
| func setTfeClient() { |
| tfeHostname = os.Getenv("TFE_HOSTNAME") |
| tfeToken = os.Getenv("TFE_TOKEN") |
| |
| cfg := &tfe.Config{ |
| Address: fmt.Sprintf("https://%s", tfeHostname), |
| Token: tfeToken, |
| } |
| |
| if tfeHostname != "" && tfeToken != "" { |
| // Create a new TFE client. |
| client, err := tfe.NewClient(cfg) |
| if err != nil { |
| fmt.Printf("Could not create new tfe client: %v\n", err) |
| os.Exit(1) |
| } |
| tfeClient = client |
| } |
| } |
| |
| func setupBinary() func() { |
| log.Println("Setting up terraform binary") |
| tmpTerraformBinaryDir, err := ioutil.TempDir("", "terraform-test") |
| if err != nil { |
| fmt.Printf("Could not create temp directory: %v\n", err) |
| os.Exit(1) |
| } |
| log.Println(tmpTerraformBinaryDir) |
| currentDir, err := os.Getwd() |
| defer os.Chdir(currentDir) |
| if err != nil { |
| fmt.Printf("Could not change directories: %v\n", err) |
| os.Exit(1) |
| } |
| // Getting top level dir |
| dirPaths := strings.Split(currentDir, "/") |
| log.Println(currentDir) |
| topLevel := len(dirPaths) - 3 |
| topDir := strings.Join(dirPaths[0:topLevel], "/") |
| |
| if err := os.Chdir(topDir); err != nil { |
| fmt.Printf("Could not change directories: %v\n", err) |
| os.Exit(1) |
| } |
| |
| cmd := exec.Command( |
| "go", |
| "build", |
| "-o", tmpTerraformBinaryDir, |
| "-ldflags", fmt.Sprintf("-X \"github.com/hashicorp/terraform/version.Prerelease=%s\"", tfversion.Prerelease), |
| ) |
| err = cmd.Run() |
| if err != nil { |
| fmt.Printf("Could not run exec command: %v\n", err) |
| os.Exit(1) |
| } |
| |
| credFile := fmt.Sprintf("%s/dev.tfrc", tmpTerraformBinaryDir) |
| writeCredRC(credFile) |
| |
| terraformBin = fmt.Sprintf("%s/terraform", tmpTerraformBinaryDir) |
| cliConfigFileEnv = fmt.Sprintf("TF_CLI_CONFIG_FILE=%s", credFile) |
| |
| return func() { |
| os.RemoveAll(tmpTerraformBinaryDir) |
| } |
| } |
| |
| func writeCredRC(file string) { |
| creds := credentialBlock() |
| f, err := os.Create(file) |
| if err != nil { |
| fmt.Printf("Could not create file: %v\n", err) |
| os.Exit(1) |
| } |
| _, err = f.WriteString(creds) |
| if err != nil { |
| fmt.Printf("Could not write credentials: %v\n", err) |
| os.Exit(1) |
| } |
| f.Close() |
| } |
| |
| func credentialBlock() string { |
| return fmt.Sprintf(` |
| credentials "%s" { |
| token = "%s" |
| }`, tfeHostname, tfeToken) |
| } |