| package command |
| |
| import ( |
| "fmt" |
| "io/ioutil" |
| "os" |
| "path/filepath" |
| "reflect" |
| "strings" |
| "testing" |
| |
| "github.com/google/go-cmp/cmp" |
| |
| "github.com/hashicorp/terraform/internal/backend" |
| "github.com/hashicorp/terraform/internal/backend/local" |
| "github.com/hashicorp/terraform/internal/terraform" |
| "github.com/mitchellh/cli" |
| ) |
| |
| func TestMetaColorize(t *testing.T) { |
| var m *Meta |
| var args, args2 []string |
| |
| // Test basic, color |
| m = new(Meta) |
| m.Color = true |
| args = []string{"foo", "bar"} |
| args2 = []string{"foo", "bar"} |
| args = m.process(args) |
| if !reflect.DeepEqual(args, args2) { |
| t.Fatalf("bad: %#v", args) |
| } |
| if m.Colorize().Disable { |
| t.Fatal("should not be disabled") |
| } |
| |
| // Test basic, no change |
| m = new(Meta) |
| args = []string{"foo", "bar"} |
| args2 = []string{"foo", "bar"} |
| args = m.process(args) |
| if !reflect.DeepEqual(args, args2) { |
| t.Fatalf("bad: %#v", args) |
| } |
| if !m.Colorize().Disable { |
| t.Fatal("should be disabled") |
| } |
| |
| // Test disable #1 |
| m = new(Meta) |
| m.Color = true |
| args = []string{"foo", "-no-color", "bar"} |
| args2 = []string{"foo", "bar"} |
| args = m.process(args) |
| if !reflect.DeepEqual(args, args2) { |
| t.Fatalf("bad: %#v", args) |
| } |
| if !m.Colorize().Disable { |
| t.Fatal("should be disabled") |
| } |
| |
| // Test disable #2 |
| // Verify multiple -no-color options are removed from args slice. |
| // E.g. an additional -no-color arg could be added by TF_CLI_ARGS. |
| m = new(Meta) |
| m.Color = true |
| args = []string{"foo", "-no-color", "bar", "-no-color"} |
| args2 = []string{"foo", "bar"} |
| args = m.process(args) |
| if !reflect.DeepEqual(args, args2) { |
| t.Fatalf("bad: %#v", args) |
| } |
| if !m.Colorize().Disable { |
| t.Fatal("should be disabled") |
| } |
| } |
| |
| func TestMetaInputMode(t *testing.T) { |
| test = false |
| defer func() { test = true }() |
| |
| m := new(Meta) |
| args := []string{} |
| |
| fs := m.extendedFlagSet("foo") |
| if err := fs.Parse(args); err != nil { |
| t.Fatalf("err: %s", err) |
| } |
| |
| if m.InputMode() != terraform.InputModeStd { |
| t.Fatalf("bad: %#v", m.InputMode()) |
| } |
| } |
| |
| func TestMetaInputMode_envVar(t *testing.T) { |
| test = false |
| defer func() { test = true }() |
| old := os.Getenv(InputModeEnvVar) |
| defer os.Setenv(InputModeEnvVar, old) |
| |
| m := new(Meta) |
| args := []string{} |
| |
| fs := m.extendedFlagSet("foo") |
| if err := fs.Parse(args); err != nil { |
| t.Fatalf("err: %s", err) |
| } |
| |
| off := terraform.InputMode(0) |
| on := terraform.InputModeStd |
| cases := []struct { |
| EnvVar string |
| Expected terraform.InputMode |
| }{ |
| {"false", off}, |
| {"0", off}, |
| {"true", on}, |
| {"1", on}, |
| } |
| |
| for _, tc := range cases { |
| os.Setenv(InputModeEnvVar, tc.EnvVar) |
| if m.InputMode() != tc.Expected { |
| t.Fatalf("expected InputMode: %#v, got: %#v", tc.Expected, m.InputMode()) |
| } |
| } |
| } |
| |
| func TestMetaInputMode_disable(t *testing.T) { |
| test = false |
| defer func() { test = true }() |
| |
| m := new(Meta) |
| args := []string{"-input=false"} |
| |
| fs := m.extendedFlagSet("foo") |
| if err := fs.Parse(args); err != nil { |
| t.Fatalf("err: %s", err) |
| } |
| |
| if m.InputMode() > 0 { |
| t.Fatalf("bad: %#v", m.InputMode()) |
| } |
| } |
| |
| func TestMeta_initStatePaths(t *testing.T) { |
| m := new(Meta) |
| m.initStatePaths() |
| |
| if m.statePath != DefaultStateFilename { |
| t.Fatalf("bad: %#v", m) |
| } |
| if m.stateOutPath != DefaultStateFilename { |
| t.Fatalf("bad: %#v", m) |
| } |
| if m.backupPath != DefaultStateFilename+DefaultBackupExtension { |
| t.Fatalf("bad: %#v", m) |
| } |
| |
| m = new(Meta) |
| m.statePath = "foo" |
| m.initStatePaths() |
| |
| if m.stateOutPath != "foo" { |
| t.Fatalf("bad: %#v", m) |
| } |
| if m.backupPath != "foo"+DefaultBackupExtension { |
| t.Fatalf("bad: %#v", m) |
| } |
| |
| m = new(Meta) |
| m.stateOutPath = "foo" |
| m.initStatePaths() |
| |
| if m.statePath != DefaultStateFilename { |
| t.Fatalf("bad: %#v", m) |
| } |
| if m.backupPath != "foo"+DefaultBackupExtension { |
| t.Fatalf("bad: %#v", m) |
| } |
| } |
| |
| func TestMeta_Env(t *testing.T) { |
| td := t.TempDir() |
| os.MkdirAll(td, 0755) |
| defer testChdir(t, td)() |
| |
| m := new(Meta) |
| |
| env, err := m.Workspace() |
| if err != nil { |
| t.Fatal(err) |
| } |
| |
| if env != backend.DefaultStateName { |
| t.Fatalf("expected env %q, got env %q", backend.DefaultStateName, env) |
| } |
| |
| testEnv := "test_env" |
| if err := m.SetWorkspace(testEnv); err != nil { |
| t.Fatal("error setting env:", err) |
| } |
| |
| env, _ = m.Workspace() |
| if env != testEnv { |
| t.Fatalf("expected env %q, got env %q", testEnv, env) |
| } |
| |
| if err := m.SetWorkspace(backend.DefaultStateName); err != nil { |
| t.Fatal("error setting env:", err) |
| } |
| |
| env, _ = m.Workspace() |
| if env != backend.DefaultStateName { |
| t.Fatalf("expected env %q, got env %q", backend.DefaultStateName, env) |
| } |
| } |
| |
| func TestMeta_Workspace_override(t *testing.T) { |
| defer func(value string) { |
| os.Setenv(WorkspaceNameEnvVar, value) |
| }(os.Getenv(WorkspaceNameEnvVar)) |
| |
| m := new(Meta) |
| |
| testCases := map[string]struct { |
| workspace string |
| err error |
| }{ |
| "": { |
| "default", |
| nil, |
| }, |
| "development": { |
| "development", |
| nil, |
| }, |
| "invalid name": { |
| "", |
| errInvalidWorkspaceNameEnvVar, |
| }, |
| } |
| |
| for name, tc := range testCases { |
| t.Run(name, func(t *testing.T) { |
| os.Setenv(WorkspaceNameEnvVar, name) |
| workspace, err := m.Workspace() |
| if workspace != tc.workspace { |
| t.Errorf("Unexpected workspace\n got: %s\nwant: %s\n", workspace, tc.workspace) |
| } |
| if err != tc.err { |
| t.Errorf("Unexpected error\n got: %s\nwant: %s\n", err, tc.err) |
| } |
| }) |
| } |
| } |
| |
| func TestMeta_Workspace_invalidSelected(t *testing.T) { |
| td := t.TempDir() |
| os.MkdirAll(td, 0755) |
| defer testChdir(t, td)() |
| |
| // this is an invalid workspace name |
| workspace := "test workspace" |
| |
| // create the workspace directories |
| if err := os.MkdirAll(filepath.Join(local.DefaultWorkspaceDir, workspace), 0755); err != nil { |
| t.Fatal(err) |
| } |
| |
| // create the workspace file to select it |
| if err := os.MkdirAll(DefaultDataDir, 0755); err != nil { |
| t.Fatal(err) |
| } |
| if err := ioutil.WriteFile(filepath.Join(DefaultDataDir, local.DefaultWorkspaceFile), []byte(workspace), 0644); err != nil { |
| t.Fatal(err) |
| } |
| |
| m := new(Meta) |
| |
| ws, err := m.Workspace() |
| if ws != workspace { |
| t.Errorf("Unexpected workspace\n got: %s\nwant: %s\n", ws, workspace) |
| } |
| if err != nil { |
| t.Errorf("Unexpected error: %s", err) |
| } |
| } |
| |
| func TestMeta_process(t *testing.T) { |
| test = false |
| defer func() { test = true }() |
| |
| // Create a temporary directory for our cwd |
| d := t.TempDir() |
| os.MkdirAll(d, 0755) |
| defer testChdir(t, d)() |
| |
| // At one point it was the responsibility of this process function to |
| // insert fake additional -var-file options into the command line |
| // if the automatic tfvars files were present. This is no longer the |
| // responsibility of process (it happens in collectVariableValues instead) |
| // but we're still testing with these files in place to verify that |
| // they _aren't_ being interpreted by process, since that could otherwise |
| // cause them to be added more than once and mess up the precedence order. |
| defaultVarsfile := "terraform.tfvars" |
| err := ioutil.WriteFile( |
| filepath.Join(d, defaultVarsfile), |
| []byte(""), |
| 0644) |
| if err != nil { |
| t.Fatalf("err: %s", err) |
| } |
| fileFirstAlphabetical := "a-file.auto.tfvars" |
| err = ioutil.WriteFile( |
| filepath.Join(d, fileFirstAlphabetical), |
| []byte(""), |
| 0644) |
| if err != nil { |
| t.Fatalf("err: %s", err) |
| } |
| fileLastAlphabetical := "z-file.auto.tfvars" |
| err = ioutil.WriteFile( |
| filepath.Join(d, fileLastAlphabetical), |
| []byte(""), |
| 0644) |
| if err != nil { |
| t.Fatalf("err: %s", err) |
| } |
| // Regular tfvars files will not be autoloaded |
| fileIgnored := "ignored.tfvars" |
| err = ioutil.WriteFile( |
| filepath.Join(d, fileIgnored), |
| []byte(""), |
| 0644) |
| if err != nil { |
| t.Fatalf("err: %s", err) |
| } |
| |
| tests := []struct { |
| GivenArgs []string |
| FilteredArgs []string |
| ExtraCheck func(*testing.T, *Meta) |
| }{ |
| { |
| []string{}, |
| []string{}, |
| func(t *testing.T, m *Meta) { |
| if got, want := m.color, true; got != want { |
| t.Errorf("wrong m.color value %#v; want %#v", got, want) |
| } |
| if got, want := m.Color, true; got != want { |
| t.Errorf("wrong m.Color value %#v; want %#v", got, want) |
| } |
| }, |
| }, |
| { |
| []string{"-no-color"}, |
| []string{}, |
| func(t *testing.T, m *Meta) { |
| if got, want := m.color, false; got != want { |
| t.Errorf("wrong m.color value %#v; want %#v", got, want) |
| } |
| if got, want := m.Color, false; got != want { |
| t.Errorf("wrong m.Color value %#v; want %#v", got, want) |
| } |
| }, |
| }, |
| } |
| |
| for _, test := range tests { |
| t.Run(fmt.Sprintf("%s", test.GivenArgs), func(t *testing.T) { |
| m := new(Meta) |
| m.Color = true // this is the default also for normal use, overridden by -no-color |
| args := test.GivenArgs |
| args = m.process(args) |
| |
| if !cmp.Equal(test.FilteredArgs, args) { |
| t.Errorf("wrong filtered arguments\n%s", cmp.Diff(test.FilteredArgs, args)) |
| } |
| |
| if test.ExtraCheck != nil { |
| test.ExtraCheck(t, m) |
| } |
| }) |
| } |
| } |
| |
| func TestCommand_checkRequiredVersion(t *testing.T) { |
| // Create a temporary working directory that is empty |
| td := t.TempDir() |
| testCopyDir(t, testFixturePath("command-check-required-version"), td) |
| defer testChdir(t, td)() |
| |
| ui := cli.NewMockUi() |
| meta := Meta{ |
| Ui: ui, |
| } |
| |
| diags := meta.checkRequiredVersion() |
| if diags == nil { |
| t.Fatalf("diagnostics should contain unmet version constraint, but is nil") |
| } |
| |
| meta.showDiagnostics(diags) |
| |
| // Required version diags are correct |
| errStr := ui.ErrorWriter.String() |
| if !strings.Contains(errStr, `required_version = "~> 0.9.0"`) { |
| t.Fatalf("output should point to unmet version constraint, but is:\n\n%s", errStr) |
| } |
| if strings.Contains(errStr, `required_version = ">= 0.13.0"`) { |
| t.Fatalf("output should not point to met version constraint, but is:\n\n%s", errStr) |
| } |
| } |