cli: Remove legacy positional path arguments

Several commands continued to support the legacy positional path
argument to specify a working directory. This functionality has been
replaced with the global -chdir flag, which is specified before any
other arguments, including the sub-command name.

This commit removes support for the trailing path parameter from
most commands. The only command which still supports a path argument is
fmt, which also supports "-" to indicate receiving configuration from
standard input.

Any invocation of a command with an invalid trailing path parameter will
result in a short error message, pointing at the -chdir alternative.

There are many test updates in this commit, almost all of which are
migrations from using positional arguments to specify a working
directory. Because of the layer at which these tests run, we are unable
to use the -chdir argument, so the churn in test files is larger than
ideal. Sorry!
This commit is contained in:
Alisdair McDiarmid 2021-02-02 10:35:45 -05:00
parent 9c16e5726e
commit ca23a096d8
20 changed files with 425 additions and 422 deletions

View File

@ -7,6 +7,7 @@ import (
"strings" "strings"
"github.com/hashicorp/terraform/backend" "github.com/hashicorp/terraform/backend"
"github.com/hashicorp/terraform/plans/planfile"
"github.com/hashicorp/terraform/repl" "github.com/hashicorp/terraform/repl"
"github.com/hashicorp/terraform/states" "github.com/hashicorp/terraform/states"
"github.com/hashicorp/terraform/tfdiags" "github.com/hashicorp/terraform/tfdiags"
@ -50,6 +51,12 @@ func (c *ApplyCommand) Run(args []string) int {
var diags tfdiags.Diagnostics var diags tfdiags.Diagnostics
args = cmdFlags.Args() args = cmdFlags.Args()
var planPath string
if len(args) > 0 {
planPath = args[0]
args = args[1:]
}
configPath, err := ModulePath(args) configPath, err := ModulePath(args)
if err != nil { if err != nil {
c.Ui.Error(err.Error()) c.Ui.Error(err.Error())
@ -62,12 +69,15 @@ func (c *ApplyCommand) Run(args []string) int {
return 1 return 1
} }
// Check if the path is a plan // Try to load plan if path is specified
planFile, err := c.PlanFile(configPath) var planFile *planfile.Reader
if planPath != "" {
planFile, err = c.PlanFile(planPath)
if err != nil { if err != nil {
c.Ui.Error(err.Error()) c.Ui.Error(err.Error())
return 1 return 1
} }
}
if c.Destroy && planFile != nil { if c.Destroy && planFile != nil {
c.Ui.Error("Destroy can't be called with a plan file.") c.Ui.Error("Destroy can't be called with a plan file.")
return 1 return 1
@ -297,7 +307,7 @@ Options:
func (c *ApplyCommand) helpDestroy() string { func (c *ApplyCommand) helpDestroy() string {
helpText := ` helpText := `
Usage: terraform destroy [options] [DIR] Usage: terraform destroy [options]
Destroy Terraform-managed infrastructure. Destroy Terraform-managed infrastructure.

View File

@ -26,6 +26,12 @@ import (
) )
func TestApply(t *testing.T) { func TestApply(t *testing.T) {
// Create a temporary working directory that is empty
td := tempDir(t)
testCopyDir(t, testFixturePath("apply"), td)
defer os.RemoveAll(td)
defer testChdir(t, td)()
statePath := testTempFile(t) statePath := testTempFile(t)
p := applyFixtureProvider() p := applyFixtureProvider()
@ -41,7 +47,6 @@ func TestApply(t *testing.T) {
args := []string{ args := []string{
"-state", statePath, "-state", statePath,
"-auto-approve", "-auto-approve",
testFixturePath("apply"),
} }
if code := c.Run(args); code != 0 { if code := c.Run(args); code != 0 {
t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
@ -59,6 +64,12 @@ func TestApply(t *testing.T) {
// test apply with locked state // test apply with locked state
func TestApply_lockedState(t *testing.T) { func TestApply_lockedState(t *testing.T) {
// Create a temporary working directory that is empty
td := tempDir(t)
testCopyDir(t, testFixturePath("apply"), td)
defer os.RemoveAll(td)
defer testChdir(t, td)()
statePath := testTempFile(t) statePath := testTempFile(t)
unlock, err := testLockState(testDataDir, statePath) unlock, err := testLockState(testDataDir, statePath)
@ -79,7 +90,6 @@ func TestApply_lockedState(t *testing.T) {
args := []string{ args := []string{
"-state", statePath, "-state", statePath,
"-auto-approve", "-auto-approve",
testFixturePath("apply"),
} }
if code := c.Run(args); code == 0 { if code := c.Run(args); code == 0 {
t.Fatal("expected error") t.Fatal("expected error")
@ -93,6 +103,12 @@ func TestApply_lockedState(t *testing.T) {
// test apply with locked state, waiting for unlock // test apply with locked state, waiting for unlock
func TestApply_lockedStateWait(t *testing.T) { func TestApply_lockedStateWait(t *testing.T) {
// Create a temporary working directory that is empty
td := tempDir(t)
testCopyDir(t, testFixturePath("apply"), td)
defer os.RemoveAll(td)
defer testChdir(t, td)()
statePath := testTempFile(t) statePath := testTempFile(t)
unlock, err := testLockState(testDataDir, statePath) unlock, err := testLockState(testDataDir, statePath)
@ -121,7 +137,6 @@ func TestApply_lockedStateWait(t *testing.T) {
"-state", statePath, "-state", statePath,
"-lock-timeout", "4s", "-lock-timeout", "4s",
"-auto-approve", "-auto-approve",
testFixturePath("apply"),
} }
if code := c.Run(args); code != 0 { if code := c.Run(args); code != 0 {
t.Fatalf("lock should have succeeded in less than 3s: %s", ui.ErrorWriter) t.Fatalf("lock should have succeeded in less than 3s: %s", ui.ErrorWriter)
@ -157,6 +172,12 @@ func (t *hwm) Max() int {
} }
func TestApply_parallelism(t *testing.T) { func TestApply_parallelism(t *testing.T) {
// Create a temporary working directory that is empty
td := tempDir(t)
testCopyDir(t, testFixturePath("parallelism"), td)
defer os.RemoveAll(td)
defer testChdir(t, td)()
statePath := testTempFile(t) statePath := testTempFile(t)
par := 4 par := 4
@ -217,7 +238,6 @@ func TestApply_parallelism(t *testing.T) {
"-state", statePath, "-state", statePath,
"-auto-approve", "-auto-approve",
fmt.Sprintf("-parallelism=%d", par), fmt.Sprintf("-parallelism=%d", par),
testFixturePath("parallelism"),
} }
// Run in a goroutine. We can get any errors from the ui.OutputWriter // Run in a goroutine. We can get any errors from the ui.OutputWriter
@ -258,6 +278,12 @@ func TestApply_parallelism(t *testing.T) {
} }
func TestApply_configInvalid(t *testing.T) { func TestApply_configInvalid(t *testing.T) {
// Create a temporary working directory that is empty
td := tempDir(t)
testCopyDir(t, testFixturePath("apply-config-invalid"), td)
defer os.RemoveAll(td)
defer testChdir(t, td)()
p := testProvider() p := testProvider()
ui := new(cli.MockUi) ui := new(cli.MockUi)
c := &ApplyCommand{ c := &ApplyCommand{
@ -270,7 +296,6 @@ func TestApply_configInvalid(t *testing.T) {
args := []string{ args := []string{
"-state", testTempFile(t), "-state", testTempFile(t),
"-auto-approve", "-auto-approve",
testFixturePath("apply-config-invalid"),
} }
if code := c.Run(args); code != 1 { if code := c.Run(args); code != 1 {
t.Fatalf("bad: \n%s", ui.OutputWriter.String()) t.Fatalf("bad: \n%s", ui.OutputWriter.String())
@ -278,7 +303,12 @@ func TestApply_configInvalid(t *testing.T) {
} }
func TestApply_defaultState(t *testing.T) { func TestApply_defaultState(t *testing.T) {
td := testTempDir(t) // Create a temporary working directory that is empty
td := tempDir(t)
testCopyDir(t, testFixturePath("apply"), td)
defer os.RemoveAll(td)
defer testChdir(t, td)()
statePath := filepath.Join(td, DefaultStateFilename) statePath := filepath.Join(td, DefaultStateFilename)
// Change to the temporary directory // Change to the temporary directory
@ -308,7 +338,6 @@ func TestApply_defaultState(t *testing.T) {
args := []string{ args := []string{
"-auto-approve", "-auto-approve",
testFixturePath("apply"),
} }
if code := c.Run(args); code != 0 { if code := c.Run(args); code != 0 {
t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
@ -325,6 +354,12 @@ func TestApply_defaultState(t *testing.T) {
} }
func TestApply_error(t *testing.T) { func TestApply_error(t *testing.T) {
// Create a temporary working directory that is empty
td := tempDir(t)
testCopyDir(t, testFixturePath("apply-error"), td)
defer os.RemoveAll(td)
defer testChdir(t, td)()
statePath := testTempFile(t) statePath := testTempFile(t)
p := testProvider() p := testProvider()
@ -376,7 +411,6 @@ func TestApply_error(t *testing.T) {
args := []string{ args := []string{
"-state", statePath, "-state", statePath,
"-auto-approve", "-auto-approve",
testFixturePath("apply-error"),
} }
if ui.ErrorWriter != nil { if ui.ErrorWriter != nil {
t.Logf("stdout:\n%s", ui.OutputWriter.String()) t.Logf("stdout:\n%s", ui.OutputWriter.String())
@ -400,6 +434,12 @@ func TestApply_error(t *testing.T) {
} }
func TestApply_input(t *testing.T) { func TestApply_input(t *testing.T) {
// Create a temporary working directory that is empty
td := tempDir(t)
testCopyDir(t, testFixturePath("apply-input"), td)
defer os.RemoveAll(td)
defer testChdir(t, td)()
// Disable test mode so input would be asked // Disable test mode so input would be asked
test = false test = false
defer func() { test = true }() defer func() { test = true }()
@ -426,7 +466,6 @@ func TestApply_input(t *testing.T) {
args := []string{ args := []string{
"-state", statePath, "-state", statePath,
"-auto-approve", "-auto-approve",
testFixturePath("apply-input"),
} }
if code := c.Run(args); code != 0 { if code := c.Run(args); code != 0 {
t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
@ -444,6 +483,12 @@ result = foo
// When only a partial set of the variables are set, Terraform // When only a partial set of the variables are set, Terraform
// should still ask for the unset ones by default (with -input=true) // should still ask for the unset ones by default (with -input=true)
func TestApply_inputPartial(t *testing.T) { func TestApply_inputPartial(t *testing.T) {
// Create a temporary working directory that is empty
td := tempDir(t)
testCopyDir(t, testFixturePath("apply-input-partial"), td)
defer os.RemoveAll(td)
defer testChdir(t, td)()
// Disable test mode so input would be asked // Disable test mode so input would be asked
test = false test = false
defer func() { test = true }() defer func() { test = true }()
@ -467,7 +512,6 @@ func TestApply_inputPartial(t *testing.T) {
"-state", statePath, "-state", statePath,
"-auto-approve", "-auto-approve",
"-var", "foo=foovalue", "-var", "foo=foovalue",
testFixturePath("apply-input-partial"),
} }
if code := c.Run(args); code != 0 { if code := c.Run(args); code != 0 {
t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
@ -484,14 +528,11 @@ foo = foovalue
} }
func TestApply_noArgs(t *testing.T) { func TestApply_noArgs(t *testing.T) {
cwd, err := os.Getwd() // Create a temporary working directory that is empty
if err != nil { td := tempDir(t)
t.Fatalf("err: %s", err) testCopyDir(t, testFixturePath("apply"), td)
} defer os.RemoveAll(td)
if err := os.Chdir(testFixturePath("apply")); err != nil { defer testChdir(t, td)()
t.Fatalf("err: %s", err)
}
defer os.Chdir(cwd)
statePath := testTempFile(t) statePath := testTempFile(t)
@ -517,9 +558,6 @@ func TestApply_noArgs(t *testing.T) {
} }
state := testStateRead(t, statePath) state := testStateRead(t, statePath)
if err != nil {
t.Fatalf("err: %s", err)
}
if state == nil { if state == nil {
t.Fatal("state should not be nil") t.Fatal("state should not be nil")
} }
@ -800,6 +838,12 @@ func TestApply_planNoModuleFiles(t *testing.T) {
} }
func TestApply_refresh(t *testing.T) { func TestApply_refresh(t *testing.T) {
// Create a temporary working directory that is empty
td := tempDir(t)
testCopyDir(t, testFixturePath("apply"), td)
defer os.RemoveAll(td)
defer testChdir(t, td)()
originalState := states.BuildState(func(s *states.SyncState) { originalState := states.BuildState(func(s *states.SyncState) {
s.SetResourceInstanceCurrent( s.SetResourceInstanceCurrent(
addrs.Resource{ addrs.Resource{
@ -831,7 +875,6 @@ func TestApply_refresh(t *testing.T) {
args := []string{ args := []string{
"-state", statePath, "-state", statePath,
"-auto-approve", "-auto-approve",
testFixturePath("apply"),
} }
if code := c.Run(args); code != 0 { if code := c.Run(args); code != 0 {
t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
@ -861,6 +904,12 @@ func TestApply_refresh(t *testing.T) {
} }
func TestApply_shutdown(t *testing.T) { func TestApply_shutdown(t *testing.T) {
// Create a temporary working directory that is empty
td := tempDir(t)
testCopyDir(t, testFixturePath("apply-shutdown"), td)
defer os.RemoveAll(td)
defer testChdir(t, td)()
cancelled := make(chan struct{}) cancelled := make(chan struct{})
shutdownCh := make(chan struct{}) shutdownCh := make(chan struct{})
@ -920,7 +969,6 @@ func TestApply_shutdown(t *testing.T) {
args := []string{ args := []string{
"-state", statePath, "-state", statePath,
"-auto-approve", "-auto-approve",
testFixturePath("apply-shutdown"),
} }
if code := c.Run(args); code != 1 { if code := c.Run(args); code != 1 {
t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
@ -943,6 +991,12 @@ func TestApply_shutdown(t *testing.T) {
} }
func TestApply_state(t *testing.T) { func TestApply_state(t *testing.T) {
// Create a temporary working directory that is empty
td := tempDir(t)
testCopyDir(t, testFixturePath("apply"), td)
defer os.RemoveAll(td)
defer testChdir(t, td)()
originalState := states.BuildState(func(s *states.SyncState) { originalState := states.BuildState(func(s *states.SyncState) {
s.SetResourceInstanceCurrent( s.SetResourceInstanceCurrent(
addrs.Resource{ addrs.Resource{
@ -986,7 +1040,6 @@ func TestApply_state(t *testing.T) {
args := []string{ args := []string{
"-state", statePath, "-state", statePath,
"-auto-approve", "-auto-approve",
testFixturePath("apply"),
} }
if code := c.Run(args); code != 0 { if code := c.Run(args); code != 0 {
t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
@ -1031,6 +1084,12 @@ func TestApply_state(t *testing.T) {
} }
func TestApply_stateNoExist(t *testing.T) { func TestApply_stateNoExist(t *testing.T) {
// Create a temporary working directory that is empty
td := tempDir(t)
testCopyDir(t, testFixturePath("apply"), td)
defer os.RemoveAll(td)
defer testChdir(t, td)()
p := applyFixtureProvider() p := applyFixtureProvider()
ui := new(cli.MockUi) ui := new(cli.MockUi)
c := &ApplyCommand{ c := &ApplyCommand{
@ -1042,7 +1101,6 @@ func TestApply_stateNoExist(t *testing.T) {
args := []string{ args := []string{
"idontexist.tfstate", "idontexist.tfstate",
testFixturePath("apply"),
} }
if code := c.Run(args); code != 1 { if code := c.Run(args); code != 1 {
t.Fatalf("bad: \n%s", ui.OutputWriter.String()) t.Fatalf("bad: \n%s", ui.OutputWriter.String())
@ -1050,6 +1108,12 @@ func TestApply_stateNoExist(t *testing.T) {
} }
func TestApply_sensitiveOutput(t *testing.T) { func TestApply_sensitiveOutput(t *testing.T) {
// Create a temporary working directory that is empty
td := tempDir(t)
testCopyDir(t, testFixturePath("apply-sensitive-output"), td)
defer os.RemoveAll(td)
defer testChdir(t, td)()
p := testProvider() p := testProvider()
ui := new(cli.MockUi) ui := new(cli.MockUi)
c := &ApplyCommand{ c := &ApplyCommand{
@ -1064,7 +1128,6 @@ func TestApply_sensitiveOutput(t *testing.T) {
args := []string{ args := []string{
"-state", statePath, "-state", statePath,
"-auto-approve", "-auto-approve",
testFixturePath("apply-sensitive-output"),
} }
if code := c.Run(args); code != 0 { if code := c.Run(args); code != 0 {
@ -1081,6 +1144,12 @@ func TestApply_sensitiveOutput(t *testing.T) {
} }
func TestApply_vars(t *testing.T) { func TestApply_vars(t *testing.T) {
// Create a temporary working directory that is empty
td := tempDir(t)
testCopyDir(t, testFixturePath("apply-vars"), td)
defer os.RemoveAll(td)
defer testChdir(t, td)()
statePath := testTempFile(t) statePath := testTempFile(t)
p := testProvider() p := testProvider()
@ -1120,7 +1189,6 @@ func TestApply_vars(t *testing.T) {
"-auto-approve", "-auto-approve",
"-var", "foo=bar", "-var", "foo=bar",
"-state", statePath, "-state", statePath,
testFixturePath("apply-vars"),
} }
if code := c.Run(args); code != 0 { if code := c.Run(args); code != 0 {
t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
@ -1132,6 +1200,12 @@ func TestApply_vars(t *testing.T) {
} }
func TestApply_varFile(t *testing.T) { func TestApply_varFile(t *testing.T) {
// Create a temporary working directory that is empty
td := tempDir(t)
testCopyDir(t, testFixturePath("apply-vars"), td)
defer os.RemoveAll(td)
defer testChdir(t, td)()
varFilePath := testTempFile(t) varFilePath := testTempFile(t)
if err := ioutil.WriteFile(varFilePath, []byte(applyVarFile), 0644); err != nil { if err := ioutil.WriteFile(varFilePath, []byte(applyVarFile), 0644); err != nil {
t.Fatalf("err: %s", err) t.Fatalf("err: %s", err)
@ -1176,7 +1250,6 @@ func TestApply_varFile(t *testing.T) {
"-auto-approve", "-auto-approve",
"-var-file", varFilePath, "-var-file", varFilePath,
"-state", statePath, "-state", statePath,
testFixturePath("apply-vars"),
} }
if code := c.Run(args); code != 0 { if code := c.Run(args); code != 0 {
t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
@ -1188,23 +1261,19 @@ func TestApply_varFile(t *testing.T) {
} }
func TestApply_varFileDefault(t *testing.T) { func TestApply_varFileDefault(t *testing.T) {
varFileDir := testTempDir(t) // Create a temporary working directory that is empty
varFilePath := filepath.Join(varFileDir, "terraform.tfvars") td := tempDir(t)
testCopyDir(t, testFixturePath("apply-vars"), td)
defer os.RemoveAll(td)
defer testChdir(t, td)()
varFilePath := filepath.Join(td, "terraform.tfvars")
if err := ioutil.WriteFile(varFilePath, []byte(applyVarFile), 0644); err != nil { if err := ioutil.WriteFile(varFilePath, []byte(applyVarFile), 0644); err != nil {
t.Fatalf("err: %s", err) t.Fatalf("err: %s", err)
} }
statePath := testTempFile(t) statePath := testTempFile(t)
cwd, err := os.Getwd()
if err != nil {
t.Fatalf("err: %s", err)
}
if err := os.Chdir(varFileDir); err != nil {
t.Fatalf("err: %s", err)
}
defer os.Chdir(cwd)
p := testProvider() p := testProvider()
ui := new(cli.MockUi) ui := new(cli.MockUi)
c := &ApplyCommand{ c := &ApplyCommand{
@ -1241,7 +1310,6 @@ func TestApply_varFileDefault(t *testing.T) {
args := []string{ args := []string{
"-auto-approve", "-auto-approve",
"-state", statePath, "-state", statePath,
testFixturePath("apply-vars"),
} }
if code := c.Run(args); code != 0 { if code := c.Run(args); code != 0 {
t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
@ -1253,23 +1321,19 @@ func TestApply_varFileDefault(t *testing.T) {
} }
func TestApply_varFileDefaultJSON(t *testing.T) { func TestApply_varFileDefaultJSON(t *testing.T) {
varFileDir := testTempDir(t) // Create a temporary working directory that is empty
varFilePath := filepath.Join(varFileDir, "terraform.tfvars.json") td := tempDir(t)
testCopyDir(t, testFixturePath("apply-vars"), td)
defer os.RemoveAll(td)
defer testChdir(t, td)()
varFilePath := filepath.Join(td, "terraform.tfvars.json")
if err := ioutil.WriteFile(varFilePath, []byte(applyVarFileJSON), 0644); err != nil { if err := ioutil.WriteFile(varFilePath, []byte(applyVarFileJSON), 0644); err != nil {
t.Fatalf("err: %s", err) t.Fatalf("err: %s", err)
} }
statePath := testTempFile(t) statePath := testTempFile(t)
cwd, err := os.Getwd()
if err != nil {
t.Fatalf("err: %s", err)
}
if err := os.Chdir(varFileDir); err != nil {
t.Fatalf("err: %s", err)
}
defer os.Chdir(cwd)
p := testProvider() p := testProvider()
ui := new(cli.MockUi) ui := new(cli.MockUi)
c := &ApplyCommand{ c := &ApplyCommand{
@ -1306,7 +1370,6 @@ func TestApply_varFileDefaultJSON(t *testing.T) {
args := []string{ args := []string{
"-auto-approve", "-auto-approve",
"-state", statePath, "-state", statePath,
testFixturePath("apply-vars"),
} }
if code := c.Run(args); code != 0 { if code := c.Run(args); code != 0 {
t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
@ -1318,6 +1381,12 @@ func TestApply_varFileDefaultJSON(t *testing.T) {
} }
func TestApply_backup(t *testing.T) { func TestApply_backup(t *testing.T) {
// Create a temporary working directory that is empty
td := tempDir(t)
testCopyDir(t, testFixturePath("apply"), td)
defer os.RemoveAll(td)
defer testChdir(t, td)()
originalState := states.BuildState(func(s *states.SyncState) { originalState := states.BuildState(func(s *states.SyncState) {
s.SetResourceInstanceCurrent( s.SetResourceInstanceCurrent(
addrs.Resource{ addrs.Resource{
@ -1358,7 +1427,6 @@ func TestApply_backup(t *testing.T) {
"-auto-approve", "-auto-approve",
"-state", statePath, "-state", statePath,
"-backup", backupPath, "-backup", backupPath,
testFixturePath("apply"),
} }
if code := c.Run(args); code != 0 { if code := c.Run(args); code != 0 {
t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
@ -1389,6 +1457,12 @@ func TestApply_backup(t *testing.T) {
} }
func TestApply_disableBackup(t *testing.T) { func TestApply_disableBackup(t *testing.T) {
// Create a temporary working directory that is empty
td := tempDir(t)
testCopyDir(t, testFixturePath("apply"), td)
defer os.RemoveAll(td)
defer testChdir(t, td)()
originalState := testState() originalState := testState()
statePath := testStateFile(t, originalState) statePath := testStateFile(t, originalState)
@ -1412,7 +1486,6 @@ func TestApply_disableBackup(t *testing.T) {
"-auto-approve", "-auto-approve",
"-state", statePath, "-state", statePath,
"-backup", "-", "-backup", "-",
testFixturePath("apply"),
} }
if code := c.Run(args); code != 0 { if code := c.Run(args); code != 0 {
t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
@ -1462,6 +1535,12 @@ func TestApply_disableBackup(t *testing.T) {
// Test that the Terraform env is passed through // Test that the Terraform env is passed through
func TestApply_terraformEnv(t *testing.T) { func TestApply_terraformEnv(t *testing.T) {
// Create a temporary working directory that is empty
td := tempDir(t)
testCopyDir(t, testFixturePath("apply-terraform-env"), td)
defer os.RemoveAll(td)
defer testChdir(t, td)()
statePath := testTempFile(t) statePath := testTempFile(t)
p := testProvider() p := testProvider()
@ -1476,7 +1555,6 @@ func TestApply_terraformEnv(t *testing.T) {
args := []string{ args := []string{
"-auto-approve", "-auto-approve",
"-state", statePath, "-state", statePath,
testFixturePath("apply-terraform-env"),
} }
if code := c.Run(args); code != 0 { if code := c.Run(args); code != 0 {
t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
@ -1495,7 +1573,7 @@ output = default
func TestApply_terraformEnvNonDefault(t *testing.T) { func TestApply_terraformEnvNonDefault(t *testing.T) {
// Create a temporary working directory that is empty // Create a temporary working directory that is empty
td := tempDir(t) td := tempDir(t)
os.MkdirAll(td, 0755) testCopyDir(t, testFixturePath("apply-terraform-env"), td)
defer os.RemoveAll(td) defer os.RemoveAll(td)
defer testChdir(t, td)() defer testChdir(t, td)()
@ -1531,7 +1609,6 @@ func TestApply_terraformEnvNonDefault(t *testing.T) {
args := []string{ args := []string{
"-auto-approve", "-auto-approve",
testFixturePath("apply-terraform-env"),
} }
if code := c.Run(args); code != 0 { if code := c.Run(args); code != 0 {
t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())

View File

@ -47,29 +47,25 @@ is configured to use a non-local backend. This backend doesn't support this
operation. operation.
` `
// ModulePath returns the path to the root module from the CLI args. // ModulePath returns the path to the root module and validates CLI arguments.
// //
// This centralizes the logic for any commands that expect a module path // This centralizes the logic for any commands that previously accepted
// on their CLI args. This will verify that only one argument is given // a module path via CLI arguments. This will error if any extraneous arguments
// and that it is a path to configuration. // are given and suggest using the -chdir flag instead.
// //
// If your command accepts more than one arg, then change the slice bounds // If your command accepts more than one arg, then change the slice bounds
// to pass validation. // to pass validation.
func ModulePath(args []string) (string, error) { func ModulePath(args []string) (string, error) {
// TODO: test // TODO: test
if len(args) > 1 { if len(args) > 0 {
return "", fmt.Errorf("Too many command line arguments. Configuration path expected.") return "", fmt.Errorf("Too many command line arguments. Did you mean to use -chdir?")
} }
if len(args) == 0 {
path, err := os.Getwd() path, err := os.Getwd()
if err != nil { if err != nil {
return "", fmt.Errorf("Error getting pwd: %s", err) return "", fmt.Errorf("Error getting pwd: %s", err)
} }
return path, nil return path, nil
}
return args[0], nil
} }

View File

@ -175,7 +175,7 @@ func (c *ConsoleCommand) modePiped(session *repl.Session, ui cli.Ui) int {
func (c *ConsoleCommand) Help() string { func (c *ConsoleCommand) Help() string {
helpText := ` helpText := `
Usage: terraform console [options] [DIR] Usage: terraform console [options]
Starts an interactive console for experimenting with Terraform Starts an interactive console for experimenting with Terraform
interpolations. interpolations.
@ -187,9 +187,6 @@ Usage: terraform console [options] [DIR]
This command will never modify your state. This command will never modify your state.
DIR can be set to a directory with a Terraform state to load. By
default, this will default to the current working directory.
Options: Options:
-state=path Path to read state. Defaults to "terraform.tfstate" -state=path Path to read state. Defaults to "terraform.tfstate"

View File

@ -52,11 +52,13 @@ func TestConsole_basic(t *testing.T) {
} }
func TestConsole_tfvars(t *testing.T) { func TestConsole_tfvars(t *testing.T) {
tmp, cwd := testCwd(t) td := tempDir(t)
defer testFixCwd(t, tmp, cwd) testCopyDir(t, testFixturePath("apply-vars"), td)
defer os.RemoveAll(td)
defer testChdir(t, td)()
// Write a terraform.tvars // Write a terraform.tvars
varFilePath := filepath.Join(tmp, "terraform.tfvars") varFilePath := filepath.Join(td, "terraform.tfvars")
if err := ioutil.WriteFile(varFilePath, []byte(applyVarFile), 0644); err != nil { if err := ioutil.WriteFile(varFilePath, []byte(applyVarFile), 0644); err != nil {
t.Fatalf("err: %s", err) t.Fatalf("err: %s", err)
} }
@ -85,9 +87,7 @@ func TestConsole_tfvars(t *testing.T) {
defer testStdinPipe(t, strings.NewReader("var.foo\n"))() defer testStdinPipe(t, strings.NewReader("var.foo\n"))()
outCloser := testStdoutCapture(t, &output) outCloser := testStdoutCapture(t, &output)
args := []string{ args := []string{}
testFixturePath("apply-vars"),
}
code := c.Run(args) code := c.Run(args)
outCloser() outCloser()
if code != 0 { if code != 0 {
@ -106,9 +106,13 @@ func TestConsole_unsetRequiredVars(t *testing.T) {
// "terraform console" producing an interactive prompt for those variables // "terraform console" producing an interactive prompt for those variables
// or producing errors. Instead, it should allow evaluation in that // or producing errors. Instead, it should allow evaluation in that
// partial context but see the unset variables values as being unknown. // partial context but see the unset variables values as being unknown.
//
tmp, cwd := testCwd(t) // This test fixture includes variable "foo" {}, which we are
defer testFixCwd(t, tmp, cwd) // intentionally not setting here.
td := tempDir(t)
testCopyDir(t, testFixturePath("apply-vars"), td)
defer os.RemoveAll(td)
defer testChdir(t, td)()
p := testProvider() p := testProvider()
p.GetSchemaResponse = &providers.GetSchemaResponse{ p.GetSchemaResponse = &providers.GetSchemaResponse{
@ -134,11 +138,7 @@ func TestConsole_unsetRequiredVars(t *testing.T) {
defer testStdinPipe(t, strings.NewReader("var.foo\n"))() defer testStdinPipe(t, strings.NewReader("var.foo\n"))()
outCloser := testStdoutCapture(t, &output) outCloser := testStdoutCapture(t, &output)
args := []string{ args := []string{}
// This test fixture includes variable "foo" {}, which we are
// intentionally not setting here.
testFixturePath("apply-vars"),
}
code := c.Run(args) code := c.Run(args)
outCloser() outCloser()
@ -152,8 +152,10 @@ func TestConsole_unsetRequiredVars(t *testing.T) {
} }
func TestConsole_variables(t *testing.T) { func TestConsole_variables(t *testing.T) {
tmp, cwd := testCwd(t) td := tempDir(t)
defer testFixCwd(t, tmp, cwd) testCopyDir(t, testFixturePath("variables"), td)
defer os.RemoveAll(td)
defer testChdir(t, td)()
p := testProvider() p := testProvider()
ui := cli.NewMockUi() ui := cli.NewMockUi()
@ -171,9 +173,7 @@ func TestConsole_variables(t *testing.T) {
"local.snack_bar\n": "[\n \"popcorn\",\n (sensitive),\n]\n", "local.snack_bar\n": "[\n \"popcorn\",\n (sensitive),\n]\n",
} }
args := []string{ args := []string{}
testFixturePath("variables"),
}
for cmd, val := range commands { for cmd, val := range commands {
var output bytes.Buffer var output bytes.Buffer
@ -214,9 +214,7 @@ func TestConsole_modules(t *testing.T) {
"local.foo\n": "3\n", "local.foo\n": "3\n",
} }
args := []string{ args := []string{}
testFixturePath("modules"),
}
for cmd, val := range commands { for cmd, val := range commands {
var output bytes.Buffer var output bytes.Buffer

View File

@ -9,8 +9,10 @@ import (
) )
func TestGet(t *testing.T) { func TestGet(t *testing.T) {
tmp, cwd := testCwd(t) td := tempDir(t)
defer testFixCwd(t, tmp, cwd) testCopyDir(t, testFixturePath("get"), td)
defer os.RemoveAll(td)
defer testChdir(t, td)()
ui := new(cli.MockUi) ui := new(cli.MockUi)
c := &GetCommand{ c := &GetCommand{
@ -21,9 +23,7 @@ func TestGet(t *testing.T) {
}, },
} }
args := []string{ args := []string{}
testFixturePath("get"),
}
if code := c.Run(args); code != 0 { if code := c.Run(args); code != 0 {
t.Fatalf("bad: \n%s", ui.ErrorWriter.String()) t.Fatalf("bad: \n%s", ui.ErrorWriter.String())
} }
@ -53,7 +53,7 @@ func TestGet_multipleArgs(t *testing.T) {
} }
} }
func TestGet_noArgs(t *testing.T) { func TestGet_update(t *testing.T) {
td := tempDir(t) td := tempDir(t)
testCopyDir(t, testFixturePath("get"), td) testCopyDir(t, testFixturePath("get"), td)
defer os.RemoveAll(td) defer os.RemoveAll(td)
@ -68,33 +68,8 @@ func TestGet_noArgs(t *testing.T) {
}, },
} }
args := []string{}
if code := c.Run(args); code != 0 {
t.Fatalf("bad: \n%s", ui.ErrorWriter.String())
}
output := ui.OutputWriter.String()
if !strings.Contains(output, "- foo in") {
t.Fatalf("doesn't look like get: %s", output)
}
}
func TestGet_update(t *testing.T) {
tmp, cwd := testCwd(t)
defer testFixCwd(t, tmp, cwd)
ui := new(cli.MockUi)
c := &GetCommand{
Meta: Meta{
testingOverrides: metaOverridesForProvider(testProvider()),
Ui: ui,
dataDir: tempDir(t),
},
}
args := []string{ args := []string{
"-update", "-update",
testFixturePath("get"),
} }
if code := c.Run(args); code != 0 { if code := c.Run(args); code != 0 {
t.Fatalf("bad: \n%s", ui.ErrorWriter.String()) t.Fatalf("bad: \n%s", ui.ErrorWriter.String())

View File

@ -4,7 +4,7 @@ import (
"fmt" "fmt"
"strings" "strings"
"github.com/hashicorp/terraform/plans" "github.com/hashicorp/terraform/plans/planfile"
"github.com/hashicorp/terraform/tfdiags" "github.com/hashicorp/terraform/tfdiags"
"github.com/hashicorp/terraform/backend" "github.com/hashicorp/terraform/backend"
@ -36,7 +36,13 @@ func (c *GraphCommand) Run(args []string) int {
return 1 return 1
} }
configPath, err := ModulePath(cmdFlags.Args()) args = cmdFlags.Args()
var planPath string
if len(args) > 0 {
planPath = args[0]
args = args[1:]
}
configPath, err := ModulePath(args)
if err != nil { if err != nil {
c.Ui.Error(err.Error()) c.Ui.Error(err.Error())
return 1 return 1
@ -48,16 +54,14 @@ func (c *GraphCommand) Run(args []string) int {
return 1 return 1
} }
// Check if the path is a plan // Try to load plan if path is specified
var plan *plans.Plan var planFile *planfile.Reader
planFile, err := c.PlanFile(configPath) if planPath != "" {
planFile, err = c.PlanFile(planPath)
if err != nil { if err != nil {
c.Ui.Error(err.Error()) c.Ui.Error(err.Error())
return 1 return 1
} }
if planFile != nil {
// Reset for backend loading
configPath = ""
} }
var diags tfdiags.Diagnostics var diags tfdiags.Diagnostics
@ -112,7 +116,7 @@ func (c *GraphCommand) Run(args []string) int {
// Determine the graph type // Determine the graph type
graphType := terraform.GraphTypePlan graphType := terraform.GraphTypePlan
if plan != nil { if planFile != nil {
graphType = terraform.GraphTypeApply graphType = terraform.GraphTypeApply
} }
@ -163,10 +167,10 @@ func (c *GraphCommand) Run(args []string) int {
func (c *GraphCommand) Help() string { func (c *GraphCommand) Help() string {
helpText := ` helpText := `
Usage: terraform graph [options] [DIR] Usage: terraform graph [options]
Outputs the visual execution graph of Terraform resources according to Outputs the visual execution graph of Terraform resources according to
configuration files in DIR (or the current directory if omitted). configuration files in the current directory.
The graph is outputted in DOT format. The typical program that can The graph is outputted in DOT format. The typical program that can
read this format is GraphViz, but many web services are also available read this format is GraphViz, but many web services are also available

View File

@ -14,8 +14,10 @@ import (
) )
func TestGraph(t *testing.T) { func TestGraph(t *testing.T) {
tmp, cwd := testCwd(t) td := tempDir(t)
defer testFixCwd(t, tmp, cwd) testCopyDir(t, testFixturePath("graph"), td)
defer os.RemoveAll(td)
defer testChdir(t, td)()
ui := new(cli.MockUi) ui := new(cli.MockUi)
c := &GraphCommand{ c := &GraphCommand{
@ -25,9 +27,7 @@ func TestGraph(t *testing.T) {
}, },
} }
args := []string{ args := []string{}
testFixturePath("graph"),
}
if code := c.Run(args); code != 0 { if code := c.Run(args); code != 0 {
t.Fatalf("bad: \n%s", ui.ErrorWriter.String()) t.Fatalf("bad: \n%s", ui.ErrorWriter.String())
} }
@ -57,14 +57,10 @@ func TestGraph_multipleArgs(t *testing.T) {
} }
func TestGraph_noArgs(t *testing.T) { func TestGraph_noArgs(t *testing.T) {
cwd, err := os.Getwd() td := tempDir(t)
if err != nil { testCopyDir(t, testFixturePath("graph"), td)
t.Fatalf("err: %s", err) defer os.RemoveAll(td)
} defer testChdir(t, td)()
if err := os.Chdir(testFixturePath("graph")); err != nil {
t.Fatalf("err: %s", err)
}
defer os.Chdir(cwd)
ui := new(cli.MockUi) ui := new(cli.MockUi)
c := &GraphCommand{ c := &GraphCommand{

View File

@ -4,7 +4,6 @@ import (
"context" "context"
"fmt" "fmt"
"log" "log"
"os"
"strings" "strings"
"github.com/hashicorp/hcl/v2" "github.com/hashicorp/hcl/v2"
@ -59,11 +58,11 @@ func (c *InitCommand) Run(args []string) int {
c.pluginPath = flagPluginPath c.pluginPath = flagPluginPath
} }
// Validate the arg count // Validate the arg count and get the working directory
args = cmdFlags.Args() args = cmdFlags.Args()
if len(args) > 1 { path, err := ModulePath(args)
c.Ui.Error("The init command expects at most one argument.\n") if err != nil {
cmdFlags.Usage() c.Ui.Error(err.Error())
return 1 return 1
} }
@ -72,20 +71,6 @@ func (c *InitCommand) Run(args []string) int {
return 1 return 1
} }
// Get our pwd. We don't always need it but always getting it is easier
// than the logic to determine if it is or isn't needed.
pwd, err := os.Getwd()
if err != nil {
c.Ui.Error(fmt.Sprintf("Error getting pwd: %s", err))
return 1
}
// If an argument is provided then it overrides our working directory.
path := pwd
if len(args) == 1 {
path = args[0]
}
// This will track whether we outputted anything so that we know whether // This will track whether we outputted anything so that we know whether
// to output a newline before the success message // to output a newline before the success message
var header bool var header bool
@ -924,7 +909,7 @@ func (c *InitCommand) AutocompleteFlags() complete.Flags {
func (c *InitCommand) Help() string { func (c *InitCommand) Help() string {
helpText := ` helpText := `
Usage: terraform init [options] [DIR] Usage: terraform init [options]
Initialize a new or existing Terraform working directory by creating Initialize a new or existing Terraform working directory by creating
initial files, loading any remote state, downloading modules, etc. initial files, loading any remote state, downloading modules, etc.
@ -939,9 +924,6 @@ Usage: terraform init [options] [DIR]
state. Even so, if you have important information, please back it up prior state. Even so, if you have important information, please back it up prior
to running this command, just in case. to running this command, just in case.
If no arguments are given, the configuration in this working directory
is initialized.
Options: Options:
-backend=true Configure the backend for this configuration. -backend=true Configure the backend for this configuration.

View File

@ -72,42 +72,6 @@ func TestInit_multipleArgs(t *testing.T) {
} }
} }
func TestInit_fromModule_explicitDest(t *testing.T) {
td := tempDir(t)
os.MkdirAll(td, 0755)
defer os.RemoveAll(td)
defer testChdir(t, td)()
ui := new(cli.MockUi)
c := &InitCommand{
Meta: Meta{
testingOverrides: metaOverridesForProvider(testProvider()),
Ui: ui,
},
}
if _, err := os.Stat(DefaultStateFilename); err == nil {
// This should never happen; it indicates a bug in another test
// is causing a terraform.tfstate to get left behind in our directory
// here, which can interfere with our init process in a way that
// isn't relevant to this test.
fullPath, _ := filepath.Abs(DefaultStateFilename)
t.Fatalf("some other test has left terraform.tfstate behind:\n%s", fullPath)
}
args := []string{
"-from-module=" + testFixturePath("init"),
td,
}
if code := c.Run(args); code != 0 {
t.Fatalf("bad: \n%s", ui.ErrorWriter.String())
}
if _, err := os.Stat(filepath.Join(td, "hello.tf")); err != nil {
t.Fatalf("err: %s", err)
}
}
func TestInit_fromModule_cwdDest(t *testing.T) { func TestInit_fromModule_cwdDest(t *testing.T) {
// Create a temporary working directory that is empty // Create a temporary working directory that is empty
td := tempDir(t) td := tempDir(t)
@ -161,6 +125,10 @@ func TestInit_fromModule_dstInSrc(t *testing.T) {
t.Fatalf("err: %s", err) t.Fatalf("err: %s", err)
} }
if err := os.Chdir("foo"); err != nil {
t.Fatalf("err: %s", err)
}
ui := new(cli.MockUi) ui := new(cli.MockUi)
c := &InitCommand{ c := &InitCommand{
Meta: Meta{ Meta: Meta{
@ -170,8 +138,7 @@ func TestInit_fromModule_dstInSrc(t *testing.T) {
} }
args := []string{ args := []string{
"-from-module=.", "-from-module=./..",
"foo",
} }
if code := c.Run(args); code != 0 { if code := c.Run(args); code != 0 {
t.Fatalf("bad: \n%s", ui.ErrorWriter.String()) t.Fatalf("bad: \n%s", ui.ErrorWriter.String())
@ -212,7 +179,7 @@ func TestInit_get(t *testing.T) {
func TestInit_getUpgradeModules(t *testing.T) { func TestInit_getUpgradeModules(t *testing.T) {
// Create a temporary working directory that is empty // Create a temporary working directory that is empty
td := tempDir(t) td := tempDir(t)
os.MkdirAll(td, 0755) testCopyDir(t, testFixturePath("init-get"), td)
defer os.RemoveAll(td) defer os.RemoveAll(td)
defer testChdir(t, td)() defer testChdir(t, td)()
@ -227,7 +194,6 @@ func TestInit_getUpgradeModules(t *testing.T) {
args := []string{ args := []string{
"-get=true", "-get=true",
"-upgrade", "-upgrade",
testFixturePath("init-get"),
} }
if code := c.Run(args); code != 0 { if code := c.Run(args); code != 0 {
t.Fatalf("command did not complete successfully:\n%s", ui.ErrorWriter.String()) t.Fatalf("command did not complete successfully:\n%s", ui.ErrorWriter.String())
@ -484,7 +450,7 @@ func TestInit_backendConfigFilePowershellConfusion(t *testing.T) {
} }
output := ui.ErrorWriter.String() output := ui.ErrorWriter.String()
if got, want := output, `Module directory ./input.config does not exist`; !strings.Contains(got, want) { if got, want := output, `Too many command line arguments`; !strings.Contains(got, want) {
t.Fatalf("wrong output\ngot:\n%s\n\nwant: message containing %q", got, want) t.Fatalf("wrong output\ngot:\n%s\n\nwant: message containing %q", got, want)
} }
} }
@ -681,41 +647,6 @@ func TestInit_backendCli_no_config_block(t *testing.T) {
} }
} }
func TestInit_targetSubdir(t *testing.T) {
// Create a temporary working directory that is empty
td := tempDir(t)
os.MkdirAll(td, 0755)
defer os.RemoveAll(td)
defer testChdir(t, td)()
// copy the source into a subdir
testCopyDir(t, testFixturePath("init-backend"), filepath.Join(td, "source"))
ui := new(cli.MockUi)
c := &InitCommand{
Meta: Meta{
testingOverrides: metaOverridesForProvider(testProvider()),
Ui: ui,
},
}
args := []string{
"source",
}
if code := c.Run(args); code != 0 {
t.Fatalf("bad: \n%s", ui.ErrorWriter.String())
}
if _, err := os.Stat(filepath.Join(td, DefaultDataDir, DefaultStateFilename)); err != nil {
t.Fatalf("err: %s", err)
}
// a data directory should not have been added to out working dir
if _, err := os.Stat(filepath.Join(td, "source", DefaultDataDir)); !os.IsNotExist(err) {
t.Fatalf("err: %s", err)
}
}
func TestInit_backendReinitWithExtra(t *testing.T) { func TestInit_backendReinitWithExtra(t *testing.T) {
td := tempDir(t) td := tempDir(t)
testCopyDir(t, testFixturePath("init-backend-empty"), td) testCopyDir(t, testFixturePath("init-backend-empty"), td)
@ -913,11 +844,11 @@ func TestInit_getProvider(t *testing.T) {
ui := new(cli.MockUi) ui := new(cli.MockUi)
providerSource, close := newMockProviderSource(t, map[string][]string{ providerSource, close := newMockProviderSource(t, map[string][]string{
// looking for an exact version // looking for an exact version
"exact": []string{"1.2.3"}, "exact": {"1.2.3"},
// config requires >= 2.3.3 // config requires >= 2.3.3
"greater-than": []string{"2.3.4", "2.3.3", "2.3.0"}, "greater-than": {"2.3.4", "2.3.3", "2.3.0"},
// config specifies // config specifies
"between": []string{"3.4.5", "2.3.4", "1.2.3"}, "between": {"3.4.5", "2.3.4", "1.2.3"},
}) })
defer close() defer close()
m := Meta{ m := Meta{
@ -1015,10 +946,10 @@ func TestInit_getProviderSource(t *testing.T) {
ui := new(cli.MockUi) ui := new(cli.MockUi)
providerSource, close := newMockProviderSource(t, map[string][]string{ providerSource, close := newMockProviderSource(t, map[string][]string{
// looking for an exact version // looking for an exact version
"acme/alpha": []string{"1.2.3"}, "acme/alpha": {"1.2.3"},
// config doesn't specify versions for other providers // config doesn't specify versions for other providers
"registry.example.com/acme/beta": []string{"1.0.0"}, "registry.example.com/acme/beta": {"1.0.0"},
"gamma": []string{"2.0.0"}, "gamma": {"2.0.0"},
}) })
defer close() defer close()
m := Meta{ m := Meta{
@ -1166,8 +1097,8 @@ func TestInit_getProviderDetectedLegacy(t *testing.T) {
// unknown provider, and the registry source will allow us to look up the // unknown provider, and the registry source will allow us to look up the
// appropriate namespace if possible. // appropriate namespace if possible.
providerSource, psClose := newMockProviderSource(t, map[string][]string{ providerSource, psClose := newMockProviderSource(t, map[string][]string{
"hashicorp/foo": []string{"1.2.3"}, "hashicorp/foo": {"1.2.3"},
"terraform-providers/baz": []string{"2.3.4"}, // this will not be installed "terraform-providers/baz": {"2.3.4"}, // this will not be installed
}) })
defer psClose() defer psClose()
registrySource, rsClose := testRegistrySource(t) registrySource, rsClose := testRegistrySource(t)
@ -1223,15 +1154,14 @@ func TestInit_getProviderDetectedLegacy(t *testing.T) {
func TestInit_providerSource(t *testing.T) { func TestInit_providerSource(t *testing.T) {
// Create a temporary working directory that is empty // Create a temporary working directory that is empty
td := tempDir(t) td := tempDir(t)
configDirName := "init-required-providers" testCopyDir(t, testFixturePath("init-required-providers"), td)
testCopyDir(t, testFixturePath(configDirName), filepath.Join(td, configDirName))
defer os.RemoveAll(td) defer os.RemoveAll(td)
defer testChdir(t, td)() defer testChdir(t, td)()
providerSource, close := newMockProviderSource(t, map[string][]string{ providerSource, close := newMockProviderSource(t, map[string][]string{
"test": []string{"1.2.3", "1.2.4"}, "test": {"1.2.3", "1.2.4"},
"test-beta": []string{"1.2.4"}, "test-beta": {"1.2.4"},
"source": []string{"1.2.2", "1.2.3", "1.2.1"}, "source": {"1.2.2", "1.2.3", "1.2.1"},
}) })
defer close() defer close()
@ -1246,7 +1176,7 @@ func TestInit_providerSource(t *testing.T) {
Meta: m, Meta: m,
} }
args := []string{configDirName} args := []string{}
if code := c.Run(args); code != 0 { if code := c.Run(args); code != 0 {
t.Fatalf("bad: \n%s", ui.ErrorWriter.String()) t.Fatalf("bad: \n%s", ui.ErrorWriter.String())
@ -1330,15 +1260,14 @@ func TestInit_cancel(t *testing.T) {
// platforms) were sent to it, testing that it is interruptible. // platforms) were sent to it, testing that it is interruptible.
td := tempDir(t) td := tempDir(t)
configDirName := "init-required-providers" testCopyDir(t, testFixturePath("init-required-providers"), td)
testCopyDir(t, testFixturePath(configDirName), filepath.Join(td, configDirName))
defer os.RemoveAll(td) defer os.RemoveAll(td)
defer testChdir(t, td)() defer testChdir(t, td)()
providerSource, closeSrc := newMockProviderSource(t, map[string][]string{ providerSource, closeSrc := newMockProviderSource(t, map[string][]string{
"test": []string{"1.2.3", "1.2.4"}, "test": {"1.2.3", "1.2.4"},
"test-beta": []string{"1.2.4"}, "test-beta": {"1.2.4"},
"source": []string{"1.2.2", "1.2.3", "1.2.1"}, "source": {"1.2.2", "1.2.3", "1.2.1"},
}) })
defer closeSrc() defer closeSrc()
@ -1359,7 +1288,7 @@ func TestInit_cancel(t *testing.T) {
Meta: m, Meta: m,
} }
args := []string{configDirName} args := []string{}
if code := c.Run(args); code == 0 { if code := c.Run(args); code == 0 {
t.Fatalf("succeeded; wanted error") t.Fatalf("succeeded; wanted error")
@ -1382,11 +1311,11 @@ func TestInit_getUpgradePlugins(t *testing.T) {
providerSource, close := newMockProviderSource(t, map[string][]string{ providerSource, close := newMockProviderSource(t, map[string][]string{
// looking for an exact version // looking for an exact version
"exact": []string{"1.2.3"}, "exact": {"1.2.3"},
// config requires >= 2.3.3 // config requires >= 2.3.3
"greater-than": []string{"2.3.4", "2.3.3", "2.3.0"}, "greater-than": {"2.3.4", "2.3.3", "2.3.0"},
// config specifies > 1.0.0 , < 3.0.0 // config specifies > 1.0.0 , < 3.0.0
"between": []string{"3.4.5", "2.3.4", "1.2.3"}, "between": {"3.4.5", "2.3.4", "1.2.3"},
}) })
defer close() defer close()
@ -1398,8 +1327,8 @@ func TestInit_getUpgradePlugins(t *testing.T) {
} }
installFakeProviderPackages(t, &m, map[string][]string{ installFakeProviderPackages(t, &m, map[string][]string{
"exact": []string{"0.0.1"}, "exact": {"0.0.1"},
"greater-than": []string{"2.3.3"}, "greater-than": {"2.3.3"},
}) })
c := &InitCommand{ c := &InitCommand{
@ -1506,11 +1435,11 @@ func TestInit_getProviderMissing(t *testing.T) {
providerSource, close := newMockProviderSource(t, map[string][]string{ providerSource, close := newMockProviderSource(t, map[string][]string{
// looking for exact version 1.2.3 // looking for exact version 1.2.3
"exact": []string{"1.2.4"}, "exact": {"1.2.4"},
// config requires >= 2.3.3 // config requires >= 2.3.3
"greater-than": []string{"2.3.4", "2.3.3", "2.3.0"}, "greater-than": {"2.3.4", "2.3.3", "2.3.0"},
// config specifies // config specifies
"between": []string{"3.4.5", "2.3.4", "1.2.3"}, "between": {"3.4.5", "2.3.4", "1.2.3"},
}) })
defer close() defer close()
@ -1716,14 +1645,14 @@ func TestInit_pluginDirProviders(t *testing.T) {
// for a moment that they are provider cache directories just because that // for a moment that they are provider cache directories just because that
// allows us to lean on our existing test helper functions to do this. // allows us to lean on our existing test helper functions to do this.
for i, def := range [][]string{ for i, def := range [][]string{
[]string{"exact", "1.2.3"}, {"exact", "1.2.3"},
[]string{"greater-than", "2.3.4"}, {"greater-than", "2.3.4"},
[]string{"between", "2.3.4"}, {"between", "2.3.4"},
} { } {
name, version := def[0], def[1] name, version := def[0], def[1]
dir := providercache.NewDir(pluginPath[i]) dir := providercache.NewDir(pluginPath[i])
installFakeProviderPackagesElsewhere(t, dir, map[string][]string{ installFakeProviderPackagesElsewhere(t, dir, map[string][]string{
name: []string{version}, name: {version},
}) })
} }
@ -1789,7 +1718,7 @@ func TestInit_pluginDirProvidersDoesNotGet(t *testing.T) {
// but we should ignore it because -plugin-dir is set and thus this // but we should ignore it because -plugin-dir is set and thus this
// source is temporarily overridden during install. // source is temporarily overridden during install.
providerSource, close := newMockProviderSource(t, map[string][]string{ providerSource, close := newMockProviderSource(t, map[string][]string{
"between": []string{"2.3.4"}, "between": {"2.3.4"},
}) })
defer close() defer close()
@ -1816,13 +1745,13 @@ func TestInit_pluginDirProvidersDoesNotGet(t *testing.T) {
// for a moment that they are provider cache directories just because that // for a moment that they are provider cache directories just because that
// allows us to lean on our existing test helper functions to do this. // allows us to lean on our existing test helper functions to do this.
for i, def := range [][]string{ for i, def := range [][]string{
[]string{"exact", "1.2.3"}, {"exact", "1.2.3"},
[]string{"greater-than", "2.3.4"}, {"greater-than", "2.3.4"},
} { } {
name, version := def[0], def[1] name, version := def[0], def[1]
dir := providercache.NewDir(pluginPath[i]) dir := providercache.NewDir(pluginPath[i])
installFakeProviderPackagesElsewhere(t, dir, map[string][]string{ installFakeProviderPackagesElsewhere(t, dir, map[string][]string{
name: []string{version}, name: {version},
}) })
} }

View File

@ -47,21 +47,6 @@ func (c *PlanCommand) Run(args []string) int {
return 1 return 1
} }
// Check if the path is a plan, which is not permitted
planFileReader, err := c.PlanFile(configPath)
if err != nil {
c.Ui.Error(err.Error())
return 1
}
if planFileReader != nil {
c.showDiagnostics(tfdiags.Sourceless(
tfdiags.Error,
"Invalid configuration directory",
fmt.Sprintf("Cannot pass a saved plan file to the 'terraform plan' command. To apply a saved plan, use: terraform apply %s", configPath),
))
return 1
}
var diags tfdiags.Diagnostics var diags tfdiags.Diagnostics
var backendConfig *configs.Backend var backendConfig *configs.Backend
@ -191,7 +176,7 @@ func (c *PlanCommand) Run(args []string) int {
func (c *PlanCommand) Help() string { func (c *PlanCommand) Help() string {
helpText := ` helpText := `
Usage: terraform plan [options] [DIR] Usage: terraform plan [options]
Generates a speculative execution plan, showing what actions Terraform Generates a speculative execution plan, showing what actions Terraform
would take to apply the current configuration. This command will not would take to apply the current configuration. This command will not

View File

@ -4,6 +4,7 @@ import (
"bytes" "bytes"
"io/ioutil" "io/ioutil"
"os" "os"
"path"
"path/filepath" "path/filepath"
"strings" "strings"
"sync" "sync"
@ -98,6 +99,11 @@ func TestPlan_plan(t *testing.T) {
} }
func TestPlan_destroy(t *testing.T) { func TestPlan_destroy(t *testing.T) {
td := tempDir(t)
testCopyDir(t, testFixturePath("plan"), td)
defer os.RemoveAll(td)
defer testChdir(t, td)()
originalState := states.BuildState(func(s *states.SyncState) { originalState := states.BuildState(func(s *states.SyncState) {
s.SetResourceInstanceCurrent( s.SetResourceInstanceCurrent(
addrs.Resource{ addrs.Resource{
@ -131,7 +137,6 @@ func TestPlan_destroy(t *testing.T) {
"-destroy", "-destroy",
"-out", outPath, "-out", outPath,
"-state", statePath, "-state", statePath,
testFixturePath("plan"),
} }
if code := c.Run(args); code != 0 { if code := c.Run(args); code != 0 {
t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
@ -146,8 +151,10 @@ func TestPlan_destroy(t *testing.T) {
} }
func TestPlan_noState(t *testing.T) { func TestPlan_noState(t *testing.T) {
tmp, cwd := testCwd(t) td := tempDir(t)
defer testFixCwd(t, tmp, cwd) testCopyDir(t, testFixturePath("plan"), td)
defer os.RemoveAll(td)
defer testChdir(t, td)()
p := planFixtureProvider() p := planFixtureProvider()
ui := new(cli.MockUi) ui := new(cli.MockUi)
@ -158,9 +165,7 @@ func TestPlan_noState(t *testing.T) {
}, },
} }
args := []string{ args := []string{}
testFixturePath("plan"),
}
if code := c.Run(args); code != 0 { if code := c.Run(args); code != 0 {
t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
} }
@ -179,10 +184,11 @@ func TestPlan_noState(t *testing.T) {
} }
func TestPlan_outPath(t *testing.T) { func TestPlan_outPath(t *testing.T) {
tmp, cwd := testCwd(t) td := tempDir(t)
defer testFixCwd(t, tmp, cwd) testCopyDir(t, testFixturePath("plan"), td)
defer os.RemoveAll(td)
defer testChdir(t, td)()
td := testTempDir(t)
outPath := filepath.Join(td, "test.plan") outPath := filepath.Join(td, "test.plan")
p := planFixtureProvider() p := planFixtureProvider()
@ -200,7 +206,6 @@ func TestPlan_outPath(t *testing.T) {
args := []string{ args := []string{
"-out", outPath, "-out", outPath,
testFixturePath("plan"),
} }
if code := c.Run(args); code != 0 { if code := c.Run(args); code != 0 {
t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
@ -210,6 +215,11 @@ func TestPlan_outPath(t *testing.T) {
} }
func TestPlan_outPathNoChange(t *testing.T) { func TestPlan_outPathNoChange(t *testing.T) {
td := tempDir(t)
testCopyDir(t, testFixturePath("plan"), td)
defer os.RemoveAll(td)
defer testChdir(t, td)()
originalState := states.BuildState(func(s *states.SyncState) { originalState := states.BuildState(func(s *states.SyncState) {
s.SetResourceInstanceCurrent( s.SetResourceInstanceCurrent(
addrs.Resource{ addrs.Resource{
@ -232,7 +242,6 @@ func TestPlan_outPathNoChange(t *testing.T) {
}) })
statePath := testStateFile(t, originalState) statePath := testStateFile(t, originalState)
td := testTempDir(t)
outPath := filepath.Join(td, "test.plan") outPath := filepath.Join(td, "test.plan")
p := planFixtureProvider() p := planFixtureProvider()
@ -247,7 +256,6 @@ func TestPlan_outPathNoChange(t *testing.T) {
args := []string{ args := []string{
"-out", outPath, "-out", outPath,
"-state", statePath, "-state", statePath,
testFixturePath("plan"),
} }
if code := c.Run(args); code != 0 { if code := c.Run(args); code != 0 {
t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
@ -360,8 +368,11 @@ func TestPlan_outBackend(t *testing.T) {
} }
func TestPlan_refreshFalse(t *testing.T) { func TestPlan_refreshFalse(t *testing.T) {
tmp, cwd := testCwd(t) // Create a temporary working directory that is empty
defer testFixCwd(t, tmp, cwd) td := tempDir(t)
testCopyDir(t, testFixturePath("plan"), td)
defer os.RemoveAll(td)
defer testChdir(t, td)()
p := planFixtureProvider() p := planFixtureProvider()
ui := new(cli.MockUi) ui := new(cli.MockUi)
@ -374,7 +385,6 @@ func TestPlan_refreshFalse(t *testing.T) {
args := []string{ args := []string{
"-refresh=false", "-refresh=false",
testFixturePath("plan"),
} }
if code := c.Run(args); code != 0 { if code := c.Run(args); code != 0 {
t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
@ -386,6 +396,12 @@ func TestPlan_refreshFalse(t *testing.T) {
} }
func TestPlan_state(t *testing.T) { func TestPlan_state(t *testing.T) {
// Create a temporary working directory that is empty
td := tempDir(t)
testCopyDir(t, testFixturePath("plan"), td)
defer os.RemoveAll(td)
defer testChdir(t, td)()
originalState := testState() originalState := testState()
statePath := testStateFile(t, originalState) statePath := testStateFile(t, originalState)
@ -400,7 +416,6 @@ func TestPlan_state(t *testing.T) {
args := []string{ args := []string{
"-state", statePath, "-state", statePath,
testFixturePath("plan"),
} }
if code := c.Run(args); code != 0 { if code := c.Run(args); code != 0 {
t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
@ -422,18 +437,16 @@ func TestPlan_state(t *testing.T) {
} }
func TestPlan_stateDefault(t *testing.T) { func TestPlan_stateDefault(t *testing.T) {
// Create a temporary working directory that is empty
td := tempDir(t)
testCopyDir(t, testFixturePath("plan"), td)
defer os.RemoveAll(td)
defer testChdir(t, td)()
// Generate state and move it to the default path
originalState := testState() originalState := testState()
statePath := testStateFile(t, originalState) statePath := testStateFile(t, originalState)
os.Rename(statePath, path.Join(td, "terraform.tfstate"))
// Change to that directory
cwd, err := os.Getwd()
if err != nil {
t.Fatalf("err: %s", err)
}
if err := os.Chdir(filepath.Dir(statePath)); err != nil {
t.Fatalf("err: %s", err)
}
defer os.Chdir(cwd)
p := planFixtureProvider() p := planFixtureProvider()
ui := new(cli.MockUi) ui := new(cli.MockUi)
@ -444,10 +457,7 @@ func TestPlan_stateDefault(t *testing.T) {
}, },
} }
args := []string{ args := []string{}
"-state", statePath,
testFixturePath("plan"),
}
if code := c.Run(args); code != 0 { if code := c.Run(args); code != 0 {
t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
} }
@ -514,8 +524,11 @@ func TestPlan_validate(t *testing.T) {
} }
func TestPlan_vars(t *testing.T) { func TestPlan_vars(t *testing.T) {
tmp, cwd := testCwd(t) // Create a temporary working directory that is empty
defer testFixCwd(t, tmp, cwd) td := tempDir(t)
testCopyDir(t, testFixturePath("plan-vars"), td)
defer os.RemoveAll(td)
defer testChdir(t, td)()
p := planVarsFixtureProvider() p := planVarsFixtureProvider()
ui := new(cli.MockUi) ui := new(cli.MockUi)
@ -535,7 +548,6 @@ func TestPlan_vars(t *testing.T) {
args := []string{ args := []string{
"-var", "foo=bar", "-var", "foo=bar",
testFixturePath("plan-vars"),
} }
if code := c.Run(args); code != 0 { if code := c.Run(args); code != 0 {
t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
@ -547,8 +559,11 @@ func TestPlan_vars(t *testing.T) {
} }
func TestPlan_varsUnset(t *testing.T) { func TestPlan_varsUnset(t *testing.T) {
tmp, cwd := testCwd(t) // Create a temporary working directory that is empty
defer testFixCwd(t, tmp, cwd) td := tempDir(t)
testCopyDir(t, testFixturePath("plan-vars"), td)
defer os.RemoveAll(td)
defer testChdir(t, td)()
// The plan command will prompt for interactive input of var.foo. // The plan command will prompt for interactive input of var.foo.
// We'll answer "bar" to that prompt, which should then allow this // We'll answer "bar" to that prompt, which should then allow this
@ -569,9 +584,7 @@ func TestPlan_varsUnset(t *testing.T) {
}, },
} }
args := []string{ args := []string{}
testFixturePath("plan-vars"),
}
if code := c.Run(args); code != 0 { if code := c.Run(args); code != 0 {
t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
} }
@ -581,8 +594,11 @@ func TestPlan_varsUnset(t *testing.T) {
// processing of user input: // processing of user input:
// https://github.com/hashicorp/terraform/issues/26035 // https://github.com/hashicorp/terraform/issues/26035
func TestPlan_providerArgumentUnset(t *testing.T) { func TestPlan_providerArgumentUnset(t *testing.T) {
tmp, cwd := testCwd(t) // Create a temporary working directory that is empty
defer testFixCwd(t, tmp, cwd) td := tempDir(t)
testCopyDir(t, testFixturePath("plan"), td)
defer os.RemoveAll(td)
defer testChdir(t, td)()
// Disable test mode so input would be asked // Disable test mode so input would be asked
test = false test = false
@ -631,17 +647,18 @@ func TestPlan_providerArgumentUnset(t *testing.T) {
}, },
} }
args := []string{ args := []string{}
testFixturePath("plan"),
}
if code := c.Run(args); code != 0 { if code := c.Run(args); code != 0 {
t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
} }
} }
func TestPlan_varFile(t *testing.T) { func TestPlan_varFile(t *testing.T) {
tmp, cwd := testCwd(t) // Create a temporary working directory that is empty
defer testFixCwd(t, tmp, cwd) td := tempDir(t)
testCopyDir(t, testFixturePath("plan-vars"), td)
defer os.RemoveAll(td)
defer testChdir(t, td)()
varFilePath := testTempFile(t) varFilePath := testTempFile(t)
if err := ioutil.WriteFile(varFilePath, []byte(planVarFile), 0644); err != nil { if err := ioutil.WriteFile(varFilePath, []byte(planVarFile), 0644); err != nil {
@ -666,7 +683,6 @@ func TestPlan_varFile(t *testing.T) {
args := []string{ args := []string{
"-var-file", varFilePath, "-var-file", varFilePath,
testFixturePath("plan-vars"),
} }
if code := c.Run(args); code != 0 { if code := c.Run(args); code != 0 {
t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
@ -678,21 +694,17 @@ func TestPlan_varFile(t *testing.T) {
} }
func TestPlan_varFileDefault(t *testing.T) { func TestPlan_varFileDefault(t *testing.T) {
varFileDir := testTempDir(t) // Create a temporary working directory that is empty
varFilePath := filepath.Join(varFileDir, "terraform.tfvars") td := tempDir(t)
testCopyDir(t, testFixturePath("plan-vars"), td)
defer os.RemoveAll(td)
defer testChdir(t, td)()
varFilePath := filepath.Join(td, "terraform.tfvars")
if err := ioutil.WriteFile(varFilePath, []byte(planVarFile), 0644); err != nil { if err := ioutil.WriteFile(varFilePath, []byte(planVarFile), 0644); err != nil {
t.Fatalf("err: %s", err) t.Fatalf("err: %s", err)
} }
cwd, err := os.Getwd()
if err != nil {
t.Fatalf("err: %s", err)
}
if err := os.Chdir(varFileDir); err != nil {
t.Fatalf("err: %s", err)
}
defer os.Chdir(cwd)
p := planVarsFixtureProvider() p := planVarsFixtureProvider()
ui := new(cli.MockUi) ui := new(cli.MockUi)
c := &PlanCommand{ c := &PlanCommand{
@ -709,9 +721,7 @@ func TestPlan_varFileDefault(t *testing.T) {
return return
} }
args := []string{ args := []string{}
testFixturePath("plan-vars"),
}
if code := c.Run(args); code != 0 { if code := c.Run(args); code != 0 {
t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
} }
@ -722,8 +732,11 @@ func TestPlan_varFileDefault(t *testing.T) {
} }
func TestPlan_varFileWithDecls(t *testing.T) { func TestPlan_varFileWithDecls(t *testing.T) {
tmp, cwd := testCwd(t) // Create a temporary working directory that is empty
defer testFixCwd(t, tmp, cwd) td := tempDir(t)
testCopyDir(t, testFixturePath("plan-vars"), td)
defer os.RemoveAll(td)
defer testChdir(t, td)()
varFilePath := testTempFile(t) varFilePath := testTempFile(t)
if err := ioutil.WriteFile(varFilePath, []byte(planVarFileWithDecl), 0644); err != nil { if err := ioutil.WriteFile(varFilePath, []byte(planVarFileWithDecl), 0644); err != nil {
@ -741,7 +754,6 @@ func TestPlan_varFileWithDecls(t *testing.T) {
args := []string{ args := []string{
"-var-file", varFilePath, "-var-file", varFilePath,
testFixturePath("plan-vars"),
} }
if code := c.Run(args); code == 0 { if code := c.Run(args); code == 0 {
t.Fatalf("succeeded; want failure\n\n%s", ui.OutputWriter.String()) t.Fatalf("succeeded; want failure\n\n%s", ui.OutputWriter.String())
@ -796,6 +808,12 @@ func TestPlan_detailedExitcode_emptyDiff(t *testing.T) {
} }
func TestPlan_shutdown(t *testing.T) { func TestPlan_shutdown(t *testing.T) {
// Create a temporary working directory that is empty
td := tempDir(t)
testCopyDir(t, testFixturePath("apply-shutdown"), td)
defer os.RemoveAll(td)
defer testChdir(t, td)()
cancelled := make(chan struct{}) cancelled := make(chan struct{})
shutdownCh := make(chan struct{}) shutdownCh := make(chan struct{})
@ -847,14 +865,7 @@ func TestPlan_shutdown(t *testing.T) {
}, },
} }
code := c.Run([]string{ code := c.Run([]string{})
// Unfortunately it seems like this test can inadvertently pick up
// leftover state from other tests without this. Ideally we should
// find which test is leaving a terraform.tfstate behind and stop it
// doing that, but this will stop this test flapping for now.
"-state=nonexistent.tfstate",
testFixturePath("apply-shutdown"),
})
if code != 1 { if code != 1 {
t.Errorf("wrong exit code %d; want 1\noutput:\n%s", code, ui.OutputWriter.String()) t.Errorf("wrong exit code %d; want 1\noutput:\n%s", code, ui.OutputWriter.String())
} }

View File

@ -104,7 +104,7 @@ func (c *RefreshCommand) Run(args []string) int {
func (c *RefreshCommand) Help() string { func (c *RefreshCommand) Help() string {
helpText := ` helpText := `
Usage: terraform refresh [options] [dir] Usage: terraform refresh [options]
Update the state file of your infrastructure with metadata that matches Update the state file of your infrastructure with metadata that matches
the physical resources they are tracking. the physical resources they are tracking.

View File

@ -27,6 +27,12 @@ import (
var equateEmpty = cmpopts.EquateEmpty() var equateEmpty = cmpopts.EquateEmpty()
func TestRefresh(t *testing.T) { func TestRefresh(t *testing.T) {
// Create a temporary working directory that is empty
td := tempDir(t)
testCopyDir(t, testFixturePath("refresh"), td)
defer os.RemoveAll(td)
defer testChdir(t, td)()
state := testState() state := testState()
statePath := testStateFile(t, state) statePath := testStateFile(t, state)
@ -49,7 +55,6 @@ func TestRefresh(t *testing.T) {
args := []string{ args := []string{
"-state", statePath, "-state", statePath,
testFixturePath("refresh"),
} }
if code := c.Run(args); code != 0 { if code := c.Run(args); code != 0 {
t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
@ -100,9 +105,7 @@ func TestRefresh_empty(t *testing.T) {
}), }),
} }
args := []string{ args := []string{}
td,
}
if code := c.Run(args); code != 0 { if code := c.Run(args); code != 0 {
t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
} }
@ -113,6 +116,12 @@ func TestRefresh_empty(t *testing.T) {
} }
func TestRefresh_lockedState(t *testing.T) { func TestRefresh_lockedState(t *testing.T) {
// Create a temporary working directory that is empty
td := tempDir(t)
testCopyDir(t, testFixturePath("refresh"), td)
defer os.RemoveAll(td)
defer testChdir(t, td)()
state := testState() state := testState()
statePath := testStateFile(t, state) statePath := testStateFile(t, state)
@ -141,7 +150,6 @@ func TestRefresh_lockedState(t *testing.T) {
args := []string{ args := []string{
"-state", statePath, "-state", statePath,
testFixturePath("refresh"),
} }
if code := c.Run(args); code == 0 { if code := c.Run(args); code == 0 {
@ -214,6 +222,12 @@ func TestRefresh_cwd(t *testing.T) {
} }
func TestRefresh_defaultState(t *testing.T) { func TestRefresh_defaultState(t *testing.T) {
// Create a temporary working directory that is empty
td := tempDir(t)
testCopyDir(t, testFixturePath("refresh"), td)
defer os.RemoveAll(td)
defer testChdir(t, td)()
originalState := testState() originalState := testState()
// Write the state file in a temporary directory with the // Write the state file in a temporary directory with the
@ -258,7 +272,6 @@ func TestRefresh_defaultState(t *testing.T) {
args := []string{ args := []string{
"-state", statePath, "-state", statePath,
testFixturePath("refresh"),
} }
if code := c.Run(args); code != 0 { if code := c.Run(args); code != 0 {
t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
@ -290,6 +303,12 @@ func TestRefresh_defaultState(t *testing.T) {
} }
func TestRefresh_outPath(t *testing.T) { func TestRefresh_outPath(t *testing.T) {
// Create a temporary working directory that is empty
td := tempDir(t)
testCopyDir(t, testFixturePath("refresh"), td)
defer os.RemoveAll(td)
defer testChdir(t, td)()
state := testState() state := testState()
statePath := testStateFile(t, state) statePath := testStateFile(t, state)
@ -322,7 +341,6 @@ func TestRefresh_outPath(t *testing.T) {
args := []string{ args := []string{
"-state", statePath, "-state", statePath,
"-state-out", outPath, "-state-out", outPath,
testFixturePath("refresh"),
} }
if code := c.Run(args); code != 0 { if code := c.Run(args); code != 0 {
t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
@ -353,6 +371,12 @@ func TestRefresh_outPath(t *testing.T) {
} }
func TestRefresh_var(t *testing.T) { func TestRefresh_var(t *testing.T) {
// Create a temporary working directory that is empty
td := tempDir(t)
testCopyDir(t, testFixturePath("refresh-var"), td)
defer os.RemoveAll(td)
defer testChdir(t, td)()
state := testState() state := testState()
statePath := testStateFile(t, state) statePath := testStateFile(t, state)
@ -369,7 +393,6 @@ func TestRefresh_var(t *testing.T) {
args := []string{ args := []string{
"-var", "foo=bar", "-var", "foo=bar",
"-state", statePath, "-state", statePath,
testFixturePath("refresh-var"),
} }
if code := c.Run(args); code != 0 { if code := c.Run(args); code != 0 {
t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
@ -384,6 +407,12 @@ func TestRefresh_var(t *testing.T) {
} }
func TestRefresh_varFile(t *testing.T) { func TestRefresh_varFile(t *testing.T) {
// Create a temporary working directory that is empty
td := tempDir(t)
testCopyDir(t, testFixturePath("refresh-var"), td)
defer os.RemoveAll(td)
defer testChdir(t, td)()
state := testState() state := testState()
statePath := testStateFile(t, state) statePath := testStateFile(t, state)
@ -405,7 +434,6 @@ func TestRefresh_varFile(t *testing.T) {
args := []string{ args := []string{
"-var-file", varFilePath, "-var-file", varFilePath,
"-state", statePath, "-state", statePath,
testFixturePath("refresh-var"),
} }
if code := c.Run(args); code != 0 { if code := c.Run(args); code != 0 {
t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
@ -420,6 +448,12 @@ func TestRefresh_varFile(t *testing.T) {
} }
func TestRefresh_varFileDefault(t *testing.T) { func TestRefresh_varFileDefault(t *testing.T) {
// Create a temporary working directory that is empty
td := tempDir(t)
testCopyDir(t, testFixturePath("refresh-var"), td)
defer os.RemoveAll(td)
defer testChdir(t, td)()
state := testState() state := testState()
statePath := testStateFile(t, state) statePath := testStateFile(t, state)
@ -433,24 +467,13 @@ func TestRefresh_varFileDefault(t *testing.T) {
} }
p.GetSchemaResponse = refreshVarFixtureSchema() p.GetSchemaResponse = refreshVarFixtureSchema()
varFileDir := testTempDir(t) varFilePath := filepath.Join(td, "terraform.tfvars")
varFilePath := filepath.Join(varFileDir, "terraform.tfvars")
if err := ioutil.WriteFile(varFilePath, []byte(refreshVarFile), 0644); err != nil { if err := ioutil.WriteFile(varFilePath, []byte(refreshVarFile), 0644); err != nil {
t.Fatalf("err: %s", err) t.Fatalf("err: %s", err)
} }
cwd, err := os.Getwd()
if err != nil {
t.Fatalf("err: %s", err)
}
if err := os.Chdir(varFileDir); err != nil {
t.Fatalf("err: %s", err)
}
defer os.Chdir(cwd)
args := []string{ args := []string{
"-state", statePath, "-state", statePath,
testFixturePath("refresh-var"),
} }
if code := c.Run(args); code != 0 { if code := c.Run(args); code != 0 {
t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
@ -465,6 +488,12 @@ func TestRefresh_varFileDefault(t *testing.T) {
} }
func TestRefresh_varsUnset(t *testing.T) { func TestRefresh_varsUnset(t *testing.T) {
// Create a temporary working directory that is empty
td := tempDir(t)
testCopyDir(t, testFixturePath("refresh-unset-var"), td)
defer os.RemoveAll(td)
defer testChdir(t, td)()
// Disable test mode so input would be asked // Disable test mode so input would be asked
test = false test = false
defer func() { test = true }() defer func() { test = true }()
@ -497,7 +526,6 @@ func TestRefresh_varsUnset(t *testing.T) {
args := []string{ args := []string{
"-state", statePath, "-state", statePath,
testFixturePath("refresh-unset-var"),
} }
if code := c.Run(args); code != 0 { if code := c.Run(args); code != 0 {
t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
@ -505,6 +533,12 @@ func TestRefresh_varsUnset(t *testing.T) {
} }
func TestRefresh_backup(t *testing.T) { func TestRefresh_backup(t *testing.T) {
// Create a temporary working directory that is empty
td := tempDir(t)
testCopyDir(t, testFixturePath("refresh"), td)
defer os.RemoveAll(td)
defer testChdir(t, td)()
state := testState() state := testState()
statePath := testStateFile(t, state) statePath := testStateFile(t, state)
@ -553,7 +587,6 @@ func TestRefresh_backup(t *testing.T) {
"-state", statePath, "-state", statePath,
"-state-out", outPath, "-state-out", outPath,
"-backup", backupPath, "-backup", backupPath,
testFixturePath("refresh"),
} }
if code := c.Run(args); code != 0 { if code := c.Run(args); code != 0 {
t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
@ -584,6 +617,12 @@ func TestRefresh_backup(t *testing.T) {
} }
func TestRefresh_disableBackup(t *testing.T) { func TestRefresh_disableBackup(t *testing.T) {
// Create a temporary working directory that is empty
td := tempDir(t)
testCopyDir(t, testFixturePath("refresh"), td)
defer os.RemoveAll(td)
defer testChdir(t, td)()
state := testState() state := testState()
statePath := testStateFile(t, state) statePath := testStateFile(t, state)
@ -617,7 +656,6 @@ func TestRefresh_disableBackup(t *testing.T) {
"-state", statePath, "-state", statePath,
"-state-out", outPath, "-state-out", outPath,
"-backup", "-", "-backup", "-",
testFixturePath("refresh"),
} }
if code := c.Run(args); code != 0 { if code := c.Run(args); code != 0 {
t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
@ -653,6 +691,12 @@ func TestRefresh_disableBackup(t *testing.T) {
} }
func TestRefresh_displaysOutputs(t *testing.T) { func TestRefresh_displaysOutputs(t *testing.T) {
// Create a temporary working directory that is empty
td := tempDir(t)
testCopyDir(t, testFixturePath("refresh-output"), td)
defer os.RemoveAll(td)
defer testChdir(t, td)()
state := testState() state := testState()
statePath := testStateFile(t, state) statePath := testStateFile(t, state)
@ -679,7 +723,6 @@ func TestRefresh_displaysOutputs(t *testing.T) {
args := []string{ args := []string{
"-state", statePath, "-state", statePath,
testFixturePath("refresh-output"),
} }
if code := c.Run(args); code != 0 { if code := c.Run(args); code != 0 {
t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())

View File

@ -30,8 +30,8 @@ func (c *UnlockCommand) Run(args []string) int {
} }
args = cmdFlags.Args() args = cmdFlags.Args()
if len(args) == 0 { if len(args) != 1 {
c.Ui.Error("unlock requires a lock id argument") c.Ui.Error("Expected a single argument: LOCK_ID")
return cli.RunResultHelp return cli.RunResultHelp
} }
@ -116,7 +116,7 @@ func (c *UnlockCommand) Run(args []string) int {
func (c *UnlockCommand) Help() string { func (c *UnlockCommand) Help() string {
helpText := ` helpText := `
Usage: terraform force-unlock LOCK_ID [DIR] Usage: terraform force-unlock LOCK_ID
Manually unlock the state for the defined configuration. Manually unlock the state for the defined configuration.

View File

@ -56,7 +56,7 @@ func TestUnlock(t *testing.T) {
"-force", "-force",
} }
if code := c.Run(args); code != 1 { if code := c.Run(args); code != cli.RunResultHelp {
t.Fatalf("bad: %d\n%s\n%s", code, ui.OutputWriter.String(), ui.ErrorWriter.String()) t.Fatalf("bad: %d\n%s\n%s", code, ui.OutputWriter.String(), ui.ErrorWriter.String())
} }
} }

View File

@ -35,8 +35,8 @@ func (c *WorkspaceDeleteCommand) Run(args []string) int {
} }
args = cmdFlags.Args() args = cmdFlags.Args()
if len(args) == 0 { if len(args) != 1 {
c.Ui.Error("expected NAME.\n") c.Ui.Error("Expected a single argument: NAME.\n")
return cli.RunResultHelp return cli.RunResultHelp
} }
@ -182,7 +182,7 @@ func (c *WorkspaceDeleteCommand) AutocompleteFlags() complete.Flags {
func (c *WorkspaceDeleteCommand) Help() string { func (c *WorkspaceDeleteCommand) Help() string {
helpText := ` helpText := `
Usage: terraform workspace delete [OPTIONS] NAME [DIR] Usage: terraform workspace delete [OPTIONS] NAME
Delete a Terraform workspace Delete a Terraform workspace

View File

@ -91,7 +91,7 @@ func (c *WorkspaceListCommand) AutocompleteFlags() complete.Flags {
func (c *WorkspaceListCommand) Help() string { func (c *WorkspaceListCommand) Help() string {
helpText := ` helpText := `
Usage: terraform workspace list [DIR] Usage: terraform workspace list
List Terraform workspaces. List Terraform workspaces.

View File

@ -37,7 +37,7 @@ func (c *WorkspaceNewCommand) Run(args []string) int {
} }
args = cmdFlags.Args() args = cmdFlags.Args()
if len(args) == 0 { if len(args) != 1 {
c.Ui.Error("Expected a single argument: NAME.\n") c.Ui.Error("Expected a single argument: NAME.\n")
return cli.RunResultHelp return cli.RunResultHelp
} }
@ -176,7 +176,7 @@ func (c *WorkspaceNewCommand) AutocompleteFlags() complete.Flags {
func (c *WorkspaceNewCommand) Help() string { func (c *WorkspaceNewCommand) Help() string {
helpText := ` helpText := `
Usage: terraform workspace new [OPTIONS] NAME [DIR] Usage: terraform workspace new [OPTIONS] NAME
Create a new Terraform workspace. Create a new Terraform workspace.

View File

@ -26,7 +26,7 @@ func (c *WorkspaceSelectCommand) Run(args []string) int {
} }
args = cmdFlags.Args() args = cmdFlags.Args()
if len(args) == 0 { if len(args) != 1 {
c.Ui.Error("Expected a single argument: NAME.\n") c.Ui.Error("Expected a single argument: NAME.\n")
return cli.RunResultHelp return cli.RunResultHelp
} }
@ -129,7 +129,7 @@ func (c *WorkspaceSelectCommand) AutocompleteFlags() complete.Flags {
func (c *WorkspaceSelectCommand) Help() string { func (c *WorkspaceSelectCommand) Help() string {
helpText := ` helpText := `
Usage: terraform workspace select NAME [DIR] Usage: terraform workspace select NAME
Select a different Terraform workspace. Select a different Terraform workspace.