Use all tfvars files in working directory

As a side effect, several commands that previously did not have a failure
state can now fail during meta-parameter processing.
This commit is contained in:
Robert Liebowitz 2017-03-07 23:09:48 -05:00 committed by Martin Atkins
parent 29b2368fa0
commit 006744bfe0
39 changed files with 273 additions and 73 deletions

View File

@ -30,7 +30,10 @@ type ApplyCommand struct {
func (c *ApplyCommand) Run(args []string) int {
var destroyForce, refresh, autoApprove bool
args = c.Meta.process(args, true)
args, err := c.Meta.process(args, true)
if err != nil {
return 1
}
cmdName := "apply"
if c.Destroy {

View File

@ -23,7 +23,11 @@ type ConsoleCommand struct {
}
func (c *ConsoleCommand) Run(args []string) int {
args = c.Meta.process(args, true)
args, err := c.Meta.process(args, true)
if err != nil {
return 1
}
cmdFlags := c.Meta.flagSet("console")
cmdFlags.StringVar(&c.Meta.statePath, "state", DefaultStateFilename, "path")
cmdFlags.Usage = func() { c.Ui.Error(c.Help()) }

View File

@ -16,7 +16,10 @@ type DebugJSON2DotCommand struct {
}
func (c *DebugJSON2DotCommand) Run(args []string) int {
args = c.Meta.process(args, true)
args, err := c.Meta.process(args, true)
if err != nil {
return 1
}
cmdFlags := c.Meta.flagSet("debug json2dot")
if err := cmdFlags.Parse(args); err != nil {

View File

@ -29,7 +29,10 @@ func (c *FmtCommand) Run(args []string) int {
c.input = os.Stdin
}
args = c.Meta.process(args, false)
args, err := c.Meta.process(args, false)
if err != nil {
return 1
}
cmdFlags := flag.NewFlagSet("fmt", flag.ContinueOnError)
cmdFlags.BoolVar(&c.opts.List, "list", true, "list")
@ -59,7 +62,7 @@ func (c *FmtCommand) Run(args []string) int {
}
output := &cli.UiWriter{Ui: c.Ui}
err := fmtcmd.Run(dirs, []string{fileExtension}, c.input, output, c.opts)
err = fmtcmd.Run(dirs, []string{fileExtension}, c.input, output, c.opts)
if err != nil {
c.Ui.Error(fmt.Sprintf("Error running fmt: %s", err))
return 2

View File

@ -17,7 +17,10 @@ type GetCommand struct {
func (c *GetCommand) Run(args []string) int {
var update bool
args = c.Meta.process(args, false)
args, err := c.Meta.process(args, false)
if err != nil {
return 1
}
cmdFlags := flag.NewFlagSet("get", flag.ContinueOnError)
cmdFlags.BoolVar(&update, "update", false, "update")
@ -26,7 +29,6 @@ func (c *GetCommand) Run(args []string) int {
return 1
}
var path string
path, err := ModulePath(cmdFlags.Args())
if err != nil {
c.Ui.Error(err.Error())

View File

@ -24,7 +24,10 @@ func (c *GraphCommand) Run(args []string) int {
var drawCycles bool
var graphTypeStr string
args = c.Meta.process(args, false)
args, err := c.Meta.process(args, false)
if err != nil {
return 1
}
cmdFlags := flag.NewFlagSet("graph", flag.ContinueOnError)
c.addModuleDepthFlag(cmdFlags, &moduleDepth)

View File

@ -27,7 +27,10 @@ func (c *ImportCommand) Run(args []string) int {
}
var configPath string
args = c.Meta.process(args, true)
args, err = c.Meta.process(args, true)
if err != nil {
return 1
}
cmdFlags := c.Meta.flagSet("import")
cmdFlags.IntVar(&c.Meta.parallelism, "parallelism", 0, "parallelism")

View File

@ -38,7 +38,10 @@ func (c *InitCommand) Run(args []string) int {
var flagPluginPath FlagStringSlice
var flagVerifyPlugins bool
args = c.Meta.process(args, false)
args, err := c.Meta.process(args, false)
if err != nil {
return 1
}
cmdFlags := c.flagSet("init")
cmdFlags.BoolVar(&flagBackend, "backend", true, "")
cmdFlags.Var((*variables.FlagAny)(&flagConfigExtra), "backend-config", "")

View File

@ -18,6 +18,7 @@ import (
"github.com/hashicorp/go-getter"
"github.com/hashicorp/terraform/backend"
"github.com/hashicorp/terraform/backend/local"
"github.com/hashicorp/terraform/config"
"github.com/hashicorp/terraform/helper/experiment"
"github.com/hashicorp/terraform/helper/variables"
"github.com/hashicorp/terraform/helper/wrappedstreams"
@ -348,7 +349,7 @@ func (m *Meta) moduleStorage(root string) getter.Storage {
// slice.
//
// vars says whether or not we support variables.
func (m *Meta) process(args []string, vars bool) []string {
func (m *Meta) process(args []string, vars bool) ([]string, error) {
// 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 {
@ -381,24 +382,49 @@ func (m *Meta) process(args []string, vars bool) []string {
// the args...
m.autoKey = ""
if vars {
if _, err := os.Stat(DefaultVarsFilename); err == nil {
m.autoKey = "var-file-default"
args = append(args, "", "")
copy(args[2:], args[0:])
args[0] = "-" + m.autoKey
args[1] = DefaultVarsFilename
wd, err := os.Getwd()
if err != nil {
return nil, err
}
f, err := os.Open(wd)
if err != nil {
return nil, err
}
defer f.Close()
fi, err := f.Stat()
if err != nil {
return nil, err
}
if !fi.IsDir() {
return nil, err
}
if _, err := os.Stat(DefaultVarsFilename + ".json"); err == nil {
m.autoKey = "var-file-default"
args = append(args, "", "")
copy(args[2:], args[0:])
args[0] = "-" + m.autoKey
args[1] = DefaultVarsFilename + ".json"
err = nil
var preArgs []string
for err != io.EOF {
var fis []os.FileInfo
fis, err = f.Readdir(128)
if err != nil && err != io.EOF {
return nil, err
}
for _, fi := range fis {
name := fi.Name()
// Ignore directories, non-var-files, and ignored files
if fi.IsDir() || !isVarFile(name) || config.IsIgnoredFile(name) {
continue
}
m.autoKey = "var-file-default"
preArgs = append(preArgs, "-"+m.autoKey, name)
}
}
args = append(preArgs, args...)
}
return args
return args, nil
}
// uiHook returns the UiHook to use with the context.
@ -543,3 +569,9 @@ func (m *Meta) SetWorkspace(name string) error {
}
return nil
}
// isVarFile determines if the file ends with .tfvars or .tfvars.json
func isVarFile(path string) bool {
return strings.HasSuffix(path, ".tfvars") ||
strings.HasSuffix(path, ".tfvars.json")
}

View File

@ -21,7 +21,10 @@ func TestMetaColorize(t *testing.T) {
m.Color = true
args = []string{"foo", "bar"}
args2 = []string{"foo", "bar"}
args = m.process(args, false)
args, err := m.process(args, false)
if err != nil {
t.Fatalf("err: %s", err)
}
if !reflect.DeepEqual(args, args2) {
t.Fatalf("bad: %#v", args)
}
@ -33,7 +36,10 @@ func TestMetaColorize(t *testing.T) {
m = new(Meta)
args = []string{"foo", "bar"}
args2 = []string{"foo", "bar"}
args = m.process(args, false)
args, err = m.process(args, false)
if err != nil {
t.Fatalf("err: %s", err)
}
if !reflect.DeepEqual(args, args2) {
t.Fatalf("bad: %#v", args)
}
@ -46,7 +52,10 @@ func TestMetaColorize(t *testing.T) {
m.Color = true
args = []string{"foo", "-no-color", "bar"}
args2 = []string{"foo", "bar"}
args = m.process(args, false)
args, err = m.process(args, false)
if err != nil {
t.Fatalf("err: %s", err)
}
if !reflect.DeepEqual(args, args2) {
t.Fatalf("bad: %#v", args)
}
@ -152,7 +161,10 @@ func TestMetaInputMode_defaultVars(t *testing.T) {
m := new(Meta)
args := []string{}
args = m.process(args, true)
args, err = m.process(args, false)
if err != nil {
t.Fatalf("err: %s", err)
}
fs := m.flagSet("foo")
if err := fs.Parse(args); err != nil {
@ -307,3 +319,60 @@ func TestMeta_Env(t *testing.T) {
t.Fatalf("expected env %q, got env %q", backend.DefaultStateName, env)
}
}
func TestMeta_process(t *testing.T) {
test = false
defer func() { test = true }()
// Create a temporary directory for our cwd
d := tempDir(t)
if err := os.MkdirAll(d, 0755); err != nil {
t.Fatalf("err: %s", err)
}
cwd, err := os.Getwd()
if err != nil {
t.Fatalf("err: %s", err)
}
if err := os.Chdir(d); err != nil {
t.Fatalf("err: %s", err)
}
defer os.Chdir(cwd)
// Create two vars files
file1 := "file1.tfvars"
err = ioutil.WriteFile(
filepath.Join(d, file1),
[]byte(""),
0644)
if err != nil {
t.Fatalf("err: %s", err)
}
file2 := "file2.tfvars"
err = ioutil.WriteFile(
filepath.Join(d, file2),
[]byte(""),
0644)
if err != nil {
t.Fatalf("err: %s", err)
}
m := new(Meta)
args := []string{}
args, err = m.process(args, true)
if err != nil {
t.Fatalf("err: %s", err)
}
if args[0] != "-var-file-default" {
t.Fatalf("expected %q, got %q", "-var-file-default", args[0])
}
if args[1] != file1 {
t.Fatalf("expected %q, got %q", file1, args[1])
}
if args[2] != "-var-file-default" {
t.Fatalf("expected %q, got %q", "-var-file-default", args[0])
}
if args[3] != file2 {
t.Fatalf("expected %q, got %q", file2, args[3])
}
}

View File

@ -16,7 +16,10 @@ type OutputCommand struct {
}
func (c *OutputCommand) Run(args []string) int {
args = c.Meta.process(args, false)
args, err := c.Meta.process(args, false)
if err != nil {
return 1
}
var module string
var jsonOutput bool

View File

@ -21,7 +21,10 @@ func (c *PlanCommand) Run(args []string) int {
var outPath string
var moduleDepth int
args = c.Meta.process(args, true)
args, err := c.Meta.process(args, true)
if err != nil {
return 1
}
cmdFlags := c.Meta.flagSet("plan")
cmdFlags.BoolVar(&destroy, "destroy", false, "destroy")

View File

@ -29,7 +29,10 @@ func (c *PushCommand) Run(args []string) int {
var archiveVCS, moduleUpload bool
var name string
var overwrite []string
args = c.Meta.process(args, true)
args, err := c.Meta.process(args, true)
if err != nil {
return 1
}
cmdFlags := c.Meta.flagSet("push")
cmdFlags.StringVar(&atlasAddress, "atlas-address", "", "")
cmdFlags.StringVar(&c.Meta.statePath, "state", DefaultStateFilename, "path")

View File

@ -17,7 +17,10 @@ type RefreshCommand struct {
}
func (c *RefreshCommand) Run(args []string) int {
args = c.Meta.process(args, true)
args, err := c.Meta.process(args, true)
if err != nil {
return 1
}
cmdFlags := c.Meta.flagSet("refresh")
cmdFlags.StringVar(&c.Meta.statePath, "state", DefaultStateFilename, "path")

View File

@ -19,7 +19,10 @@ type ShowCommand struct {
func (c *ShowCommand) Run(args []string) int {
var moduleDepth int
args = c.Meta.process(args, false)
args, err := c.Meta.process(args, false)
if err != nil {
return 1
}
cmdFlags := flag.NewFlagSet("show", flag.ContinueOnError)
c.addModuleDepthFlag(cmdFlags, &moduleDepth)

View File

@ -16,7 +16,10 @@ type StateListCommand struct {
}
func (c *StateListCommand) Run(args []string) int {
args = c.Meta.process(args, true)
args, err := c.Meta.process(args, true)
if err != nil {
return 1
}
cmdFlags := c.Meta.flagSet("state list")
cmdFlags.StringVar(&c.Meta.statePath, "state", DefaultStateFilename, "path")

View File

@ -15,7 +15,10 @@ type StateMvCommand struct {
}
func (c *StateMvCommand) Run(args []string) int {
args = c.Meta.process(args, true)
args, err := c.Meta.process(args, true)
if err != nil {
return 1
}
// We create two metas to track the two states
var meta1, meta2 Meta

View File

@ -16,7 +16,10 @@ type StatePullCommand struct {
}
func (c *StatePullCommand) Run(args []string) int {
args = c.Meta.process(args, true)
args, err := c.Meta.process(args, true)
if err != nil {
return 1
}
cmdFlags := c.Meta.flagSet("state pull")
if err := cmdFlags.Parse(args); err != nil {

View File

@ -17,7 +17,10 @@ type StatePushCommand struct {
}
func (c *StatePushCommand) Run(args []string) int {
args = c.Meta.process(args, true)
args, err := c.Meta.process(args, true)
if err != nil {
return 1
}
var flagForce bool
cmdFlags := c.Meta.flagSet("state push")

View File

@ -14,7 +14,10 @@ type StateRmCommand struct {
}
func (c *StateRmCommand) Run(args []string) int {
args = c.Meta.process(args, true)
args, err := c.Meta.process(args, true)
if err != nil {
return 1
}
cmdFlags := c.Meta.flagSet("state show")
cmdFlags.StringVar(&c.Meta.backupPath, "backup", "-", "backup")

View File

@ -17,7 +17,10 @@ type StateShowCommand struct {
}
func (c *StateShowCommand) Run(args []string) int {
args = c.Meta.process(args, true)
args, err := c.Meta.process(args, true)
if err != nil {
return 1
}
cmdFlags := c.Meta.flagSet("state show")
cmdFlags.StringVar(&c.Meta.statePath, "state", DefaultStateFilename, "path")

View File

@ -18,7 +18,10 @@ type TaintCommand struct {
}
func (c *TaintCommand) Run(args []string) int {
args = c.Meta.process(args, false)
args, err := c.Meta.process(args, false)
if err != nil {
return 1
}
var allowMissing bool
var module string

View File

@ -16,7 +16,10 @@ type UnlockCommand struct {
}
func (c *UnlockCommand) Run(args []string) int {
args = c.Meta.process(args, false)
args, err := c.Meta.process(args, false)
if err != nil {
return 1
}
force := false
cmdFlags := c.Meta.flagSet("force-unlock")

View File

@ -17,7 +17,10 @@ type UntaintCommand struct {
}
func (c *UntaintCommand) Run(args []string) int {
args = c.Meta.process(args, false)
args, err := c.Meta.process(args, false)
if err != nil {
return 1
}
var allowMissing bool
var module string

View File

@ -17,7 +17,10 @@ type ValidateCommand struct {
const defaultPath = "."
func (c *ValidateCommand) Run(args []string) int {
args = c.Meta.process(args, true)
args, err := c.Meta.process(args, false)
if err != nil {
return 1
}
var checkVars bool
cmdFlags := c.Meta.flagSet("validate")

View File

@ -34,7 +34,10 @@ func (c *VersionCommand) Help() string {
func (c *VersionCommand) Run(args []string) int {
var versionString bytes.Buffer
args = c.Meta.process(args, false)
args, err := c.Meta.process(args, false)
if err != nil {
return 1
}
fmt.Fprintf(&versionString, "Terraform v%s", c.Version)
if c.VersionPrerelease != "" {

View File

@ -15,7 +15,10 @@ type WorkspaceCommand struct {
}
func (c *WorkspaceCommand) Run(args []string) int {
args = c.Meta.process(args, true)
args, err := c.Meta.process(args, true)
if err != nil {
return 1
}
envCommandShowWarning(c.Ui, c.LegacyName)

View File

@ -16,7 +16,10 @@ type WorkspaceDeleteCommand struct {
}
func (c *WorkspaceDeleteCommand) Run(args []string) int {
args = c.Meta.process(args, true)
args, err := c.Meta.process(args, true)
if err != nil {
return 1
}
envCommandShowWarning(c.Ui, c.LegacyName)

View File

@ -12,7 +12,10 @@ type WorkspaceListCommand struct {
}
func (c *WorkspaceListCommand) Run(args []string) int {
args = c.Meta.process(args, true)
args, err := c.Meta.process(args, true)
if err != nil {
return 1
}
envCommandShowWarning(c.Ui, c.LegacyName)

View File

@ -18,7 +18,10 @@ type WorkspaceNewCommand struct {
}
func (c *WorkspaceNewCommand) Run(args []string) int {
args = c.Meta.process(args, true)
args, err := c.Meta.process(args, true)
if err != nil {
return 1
}
envCommandShowWarning(c.Ui, c.LegacyName)

View File

@ -13,7 +13,10 @@ type WorkspaceSelectCommand struct {
}
func (c *WorkspaceSelectCommand) Run(args []string) int {
args = c.Meta.process(args, true)
args, err := c.Meta.process(args, true)
if err != nil {
return 1
}
envCommandShowWarning(c.Ui, c.LegacyName)

View File

@ -9,7 +9,10 @@ type WorkspaceShowCommand struct {
}
func (c *WorkspaceShowCommand) Run(args []string) int {
args = c.Meta.process(args, true)
args, err := c.Meta.process(args, true)
if err != nil {
return 1
}
cmdFlags := c.Meta.flagSet("workspace show")
cmdFlags.Usage = func() { c.Ui.Error(c.Help()) }

View File

@ -194,7 +194,7 @@ func dirFiles(dir string) ([]string, []string, error) {
// Only care about files that are valid to load
name := fi.Name()
extValue := ext(name)
if extValue == "" || isIgnoredFile(name) {
if extValue == "" || IsIgnoredFile(name) {
continue
}
@ -215,9 +215,9 @@ func dirFiles(dir string) ([]string, []string, error) {
return files, overrides, nil
}
// isIgnoredFile returns true or false depending on whether the
// IsIgnoredFile returns true or false depending on whether the
// provided file name is a file that should be ignored.
func isIgnoredFile(name string) bool {
func IsIgnoredFile(name string) bool {
return strings.HasPrefix(name, ".") || // Unix-like hidden files
strings.HasSuffix(name, "~") || // vim
strings.HasPrefix(name, "#") && strings.HasSuffix(name, "#") // emacs

View File

@ -68,6 +68,7 @@ The command-line flags are all optional. The list of available flags are:
* `-var-file=foo` - Set variables in the Terraform configuration from
a [variable file](/docs/configuration/variables.html#variable-files). If
"terraform.tfvars" is present, it will be automatically loaded first. Any
files specified by `-var-file` override any values in a "terraform.tfvars".
This flag can be used multiple times.
any files matching "*.tfvars" are present, they will be automatically loaded
in alphabetical order. Any files specified by `-var-file` override any values
set automatically from files in the working directory. This flag can be used
multiple times.

View File

@ -67,10 +67,10 @@ The command-line flags are all optional. The list of available flags are:
* `-var-file=foo` - Set variables in the Terraform configuration from
a [variable file](/docs/configuration/variables.html#variable-files). If
"terraform.tfvars" is present, it will be automatically loaded first. Any
files specified by `-var-file` override any values in a "terraform.tfvars".
This flag can be used multiple times. This is only useful with the `-config`
flag.
any file matching "*.tfvars" are present, they will be automatically loaded
in alphabetical order. Any files specified by `-var-file` override any values
set automatically from files in the working directory. This flag can be used
multiple times. This is only useful with the `-config` flag.
## Provider Configuration

View File

@ -73,9 +73,10 @@ The command-line flags are all optional. The list of available flags are:
* `-var-file=foo` - Set variables in the Terraform configuration from
a [variable file](/docs/configuration/variables.html#variable-files). If
"terraform.tfvars" is present, it will be automatically loaded first. Any
files specified by `-var-file` override any values in a "terraform.tfvars".
This flag can be used multiple times.
any files matching "*.tfvars" are present, they will be automatically loaded
in alphabetical order. Any files specified by `-var-file` override any values
set automatically from files in the working directory. This flag can be used
multiple times.
## Resource Targeting

View File

@ -56,6 +56,7 @@ The command-line flags are all optional. The list of available flags are:
* `-var-file=foo` - Set variables in the Terraform configuration from
a [variable file](/docs/configuration/variables.html#variable-files). If
"terraform.tfvars" is present, it will be automatically loaded first. Any
files specified by `-var-file` override any values in a "terraform.tfvars".
This flag can be used multiple times.
any files matching "*.tfvars" are present, they will be automatically loaded
in alphabetical order. Any files specified by `-var-file` override any values
set automatically from files in the working directory. This flag can be used
multiple times.

View File

@ -256,10 +256,9 @@ $ TF_VAR_somemap='{foo = "bar", baz = "qux"}' terraform plan
Variables can be collected in files and passed all at once using the
`-var-file=foo.tfvars` flag.
If a file named `terraform.tfvars` is present in the current directory,
Terraform automatically loads it to populate variables. If the file is named
something else, you can pass the path to the file using the `-var-file`
flag.
For all files which match `*.tfvars` present in the current directory,
Terraform automatically loads it to populate variables. If the file is located
somewhere else, you can pass the path to the file using the `-var-file` flag.
Variables files use HCL or JSON to define variable values. Strings, lists or
maps may be set in the same manner as the default value in a `variable` block
@ -338,11 +337,18 @@ _bar.tfvars_
baz = "bar"
```
When they are passed in the following order:
When they are read directly from the working directory, the files are evaluated
in alphabetical order. The result will be that baz contains the value `foo`
because `foo.tfvars` has the last definition loaded.
When they are passed manually in the following order:
```shell
$ terraform apply -var-file=foo.tfvars -var-file=bar.tfvars
$ terraform apply -var-file=path/to/foo.tfvars -var-file=path/to/bar.tfvars
```
The result will be that `baz` will contain the value `bar` because `bar.tfvars`
has the last definition loaded.
Definitions passed using the `-var-file` flag will always be evaluated after
those in the working directory.

View File

@ -233,4 +233,4 @@ This will give the map the effective value:
}
```
It's also possible to override the values in a variables file, either in `terraform.tfvars` or specified using the `-var-file` flag.
It's also possible to override the values in a variables file, either in any `*.tfvars` file or specified using the `-var-file` flag.