From 642fed03565309eb1ca0d5d16979d56e98fb050b Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Tue, 5 Aug 2014 09:32:01 -0700 Subject: [PATCH] command: terraform.tfvars loaded by default if it exists --- CHANGELOG.md | 1 + command/apply.go | 2 +- command/apply_test.go | 51 +++++++++++++++++++++++++++++++++++++++++ command/command.go | 3 +++ command/command_test.go | 9 ++++++++ command/graph.go | 2 +- command/meta.go | 13 ++++++++++- command/meta_test.go | 6 ++--- command/output.go | 2 +- command/plan.go | 2 +- command/plan_test.go | 48 ++++++++++++++++++++++++++++++++++++++ command/refresh.go | 2 +- command/refresh_test.go | 51 +++++++++++++++++++++++++++++++++++++++++ command/show.go | 2 +- command/version.go | 2 +- 15 files changed, 185 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 673a1ac8f..6815b0c8d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ IMPROVEMENTS: BUG FIXES: + * core: Default variable file "terraform.tfvars" is auto-loaded. [GH-59] * providers/cloudflare: Include the proper bins so the cloudflare provider is compiled * providers/aws: Engine version for RDS now properly set [GH-118] diff --git a/command/apply.go b/command/apply.go index a1453bf99..41d44db14 100644 --- a/command/apply.go +++ b/command/apply.go @@ -23,7 +23,7 @@ func (c *ApplyCommand) Run(args []string) int { var refresh bool var statePath, stateOutPath, backupPath string - args = c.Meta.process(args) + args = c.Meta.process(args, true) cmdFlags := c.Meta.flagSet("apply") cmdFlags.BoolVar(&refresh, "refresh", true, "refresh") diff --git a/command/apply_test.go b/command/apply_test.go index 7b8289ea2..9a5edcf60 100644 --- a/command/apply_test.go +++ b/command/apply_test.go @@ -652,6 +652,57 @@ func TestApply_varFile(t *testing.T) { } } +func TestApply_varFileDefault(t *testing.T) { + varFileDir := testTempDir(t) + varFilePath := filepath.Join(varFileDir, "terraform.tfvars") + if err := ioutil.WriteFile(varFilePath, []byte(applyVarFile), 0644); err != nil { + t.Fatalf("err: %s", err) + } + + 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() + ui := new(cli.MockUi) + c := &ApplyCommand{ + Meta: Meta{ + ContextOpts: testCtxConfig(p), + Ui: ui, + }, + } + + actual := "" + p.DiffFn = func( + s *terraform.ResourceState, + c *terraform.ResourceConfig) (*terraform.ResourceDiff, error) { + if v, ok := c.Config["value"]; ok { + actual = v.(string) + } + + return &terraform.ResourceDiff{}, nil + } + + args := []string{ + "-state", statePath, + testFixturePath("apply-vars"), + } + if code := c.Run(args); code != 0 { + t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) + } + + if actual != "bar" { + t.Fatal("didn't work") + } +} + func TestApply_backup(t *testing.T) { originalState := &terraform.State{ Resources: map[string]*terraform.ResourceState{ diff --git a/command/command.go b/command/command.go index faa9cfcc3..840c2ae1d 100644 --- a/command/command.go +++ b/command/command.go @@ -10,6 +10,9 @@ import ( // DefaultStateFilename is the default filename used for the state file. const DefaultStateFilename = "terraform.tfstate" +// DefaultVarsFilename is the default filename used for vars +const DefaultVarsFilename = "terraform.tfvars" + // DefaultBackupExtention is added to the state file to form the path const DefaultBackupExtention = ".backup" diff --git a/command/command_test.go b/command/command_test.go index b1541f406..09f8022f2 100644 --- a/command/command_test.go +++ b/command/command_test.go @@ -116,3 +116,12 @@ func testTempFile(t *testing.T) string { return result } + +func testTempDir(t *testing.T) string { + d, err := ioutil.TempDir("", "tf") + if err != nil { + t.Fatalf("err: %s", err) + } + + return d +} diff --git a/command/graph.go b/command/graph.go index 49b1a5e5b..f974f1668 100644 --- a/command/graph.go +++ b/command/graph.go @@ -16,7 +16,7 @@ type GraphCommand struct { } func (c *GraphCommand) Run(args []string) int { - args = c.Meta.process(args) + args = c.Meta.process(args, false) cmdFlags := flag.NewFlagSet("graph", flag.ContinueOnError) cmdFlags.Usage = func() { c.Ui.Error(c.Help()) } diff --git a/command/meta.go b/command/meta.go index 0867ffdda..cf09be0a2 100644 --- a/command/meta.go +++ b/command/meta.go @@ -134,7 +134,7 @@ func (m *Meta) flagSet(n string) *flag.FlagSet { // process will process the meta-parameters out of the arguments. This // will potentially modify the args in-place. It will return the resulting // slice. -func (m *Meta) process(args []string) []string { +func (m *Meta) process(args []string, vars bool) []string { // We do this so that we retain the ability to technically call // process multiple times, even if we have no plans to do so if m.oldUi != nil { @@ -159,6 +159,17 @@ func (m *Meta) process(args []string) []string { Ui: m.oldUi, } + // If we support vars and the default var file exists, add it to + // the args... + if vars { + if _, err := os.Stat(DefaultVarsFilename); err == nil { + args = append(args, "", "") + copy(args[2:], args[0:]) + args[0] = "-var-file" + args[1] = DefaultVarsFilename + } + } + return args } diff --git a/command/meta_test.go b/command/meta_test.go index 0b46ebaea..ae209fe6a 100644 --- a/command/meta_test.go +++ b/command/meta_test.go @@ -14,7 +14,7 @@ func TestMetaColorize(t *testing.T) { m.Color = true args = []string{"foo", "bar"} args2 = []string{"foo", "bar"} - args = m.process(args) + args = m.process(args, false) if !reflect.DeepEqual(args, args2) { t.Fatalf("bad: %#v", args) } @@ -26,7 +26,7 @@ func TestMetaColorize(t *testing.T) { m = new(Meta) args = []string{"foo", "bar"} args2 = []string{"foo", "bar"} - args = m.process(args) + args = m.process(args, false) if !reflect.DeepEqual(args, args2) { t.Fatalf("bad: %#v", args) } @@ -38,7 +38,7 @@ func TestMetaColorize(t *testing.T) { m = new(Meta) args = []string{"foo", "-no-color", "bar"} args2 = []string{"foo", "bar"} - args = m.process(args) + args = m.process(args, false) if !reflect.DeepEqual(args, args2) { t.Fatalf("bad: %#v", args) } diff --git a/command/output.go b/command/output.go index 6c9d94b8e..ab7292dd6 100644 --- a/command/output.go +++ b/command/output.go @@ -18,7 +18,7 @@ type OutputCommand struct { func (c *OutputCommand) Run(args []string) int { var statePath string - args = c.Meta.process(args) + args = c.Meta.process(args, false) cmdFlags := flag.NewFlagSet("output", flag.ContinueOnError) cmdFlags.StringVar(&statePath, "state", DefaultStateFilename, "path") diff --git a/command/plan.go b/command/plan.go index 322bc6fa4..5b77f956b 100644 --- a/command/plan.go +++ b/command/plan.go @@ -19,7 +19,7 @@ func (c *PlanCommand) Run(args []string) int { var destroy, refresh bool var outPath, statePath, backupPath string - args = c.Meta.process(args) + args = c.Meta.process(args, true) cmdFlags := c.Meta.flagSet("plan") cmdFlags.BoolVar(&destroy, "destroy", false, "destroy") diff --git a/command/plan_test.go b/command/plan_test.go index d02dba1b2..af211cd2f 100644 --- a/command/plan_test.go +++ b/command/plan_test.go @@ -370,6 +370,54 @@ func TestPlan_varFile(t *testing.T) { } } +func TestPlan_varFileDefault(t *testing.T) { + varFileDir := testTempDir(t) + varFilePath := filepath.Join(varFileDir, "terraform.tfvars") + if err := ioutil.WriteFile(varFilePath, []byte(planVarFile), 0644); err != nil { + 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 := testProvider() + ui := new(cli.MockUi) + c := &PlanCommand{ + Meta: Meta{ + ContextOpts: testCtxConfig(p), + Ui: ui, + }, + } + + actual := "" + p.DiffFn = func( + s *terraform.ResourceState, + c *terraform.ResourceConfig) (*terraform.ResourceDiff, error) { + if v, ok := c.Config["value"]; ok { + actual = v.(string) + } + + return nil, nil + } + + args := []string{ + testFixturePath("plan-vars"), + } + if code := c.Run(args); code != 0 { + t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) + } + + if actual != "bar" { + t.Fatal("didn't work") + } +} + func TestPlan_backup(t *testing.T) { // Write out some prior state tf, err := ioutil.TempFile("", "tf") diff --git a/command/refresh.go b/command/refresh.go index 1603a75c8..a3f536bd0 100644 --- a/command/refresh.go +++ b/command/refresh.go @@ -18,7 +18,7 @@ type RefreshCommand struct { func (c *RefreshCommand) Run(args []string) int { var statePath, stateOutPath, backupPath string - args = c.Meta.process(args) + args = c.Meta.process(args, true) cmdFlags := c.Meta.flagSet("refresh") cmdFlags.StringVar(&statePath, "state", DefaultStateFilename, "path") diff --git a/command/refresh_test.go b/command/refresh_test.go index 976612eaf..145c4699a 100644 --- a/command/refresh_test.go +++ b/command/refresh_test.go @@ -408,6 +408,57 @@ func TestRefresh_varFile(t *testing.T) { } } +func TestRefresh_varFileDefault(t *testing.T) { + state := &terraform.State{ + Resources: map[string]*terraform.ResourceState{ + "test_instance.foo": &terraform.ResourceState{ + ID: "bar", + Type: "test_instance", + }, + }, + } + statePath := testStateFile(t, state) + + p := testProvider() + ui := new(cli.MockUi) + c := &RefreshCommand{ + Meta: Meta{ + ContextOpts: testCtxConfig(p), + Ui: ui, + }, + } + + varFileDir := testTempDir(t) + varFilePath := filepath.Join(varFileDir, "terraform.tfvars") + if err := ioutil.WriteFile(varFilePath, []byte(refreshVarFile), 0644); err != nil { + 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{ + "-state", statePath, + testFixturePath("refresh-var"), + } + if code := c.Run(args); code != 0 { + t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) + } + + if !p.ConfigureCalled { + t.Fatal("configure should be called") + } + if p.ConfigureConfig.Config["value"].(string) != "bar" { + t.Fatalf("bad: %#v", p.ConfigureConfig.Config) + } +} + func TestRefresh_backup(t *testing.T) { state := &terraform.State{ Resources: map[string]*terraform.ResourceState{ diff --git a/command/show.go b/command/show.go index 6f7a895b8..8660dbc54 100644 --- a/command/show.go +++ b/command/show.go @@ -16,7 +16,7 @@ type ShowCommand struct { } func (c *ShowCommand) Run(args []string) int { - args = c.Meta.process(args) + args = c.Meta.process(args, false) cmdFlags := flag.NewFlagSet("show", flag.ContinueOnError) cmdFlags.Usage = func() { c.Ui.Error(c.Help()) } diff --git a/command/version.go b/command/version.go index 999af7abf..17015e604 100644 --- a/command/version.go +++ b/command/version.go @@ -21,7 +21,7 @@ func (c *VersionCommand) Help() string { func (c *VersionCommand) Run(args []string) int { var versionString bytes.Buffer - args = c.Meta.process(args) + args = c.Meta.process(args, false) fmt.Fprintf(&versionString, "Terraform v%s", c.Version) if c.VersionPrerelease != "" {