command/init: return an error with invalid -backend-config files (#25411)

* command/init: return an error with invalid -backend-config files

The -backend-config flag expects a set of key-value pairs or a file
containing key-value pairs. If the file instead contains a full backend
configuration block, it was silently ignored. This commit adds a check
for blocks in the file and returns an error if they are encountered.

Fixes #24845

* emphasize backend configuration file in docs
This commit is contained in:
Kristin Laemmert 2020-06-26 12:49:31 -04:00 committed by GitHub
parent 0b3c0f64c2
commit df244b87c2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 61 additions and 22 deletions

View File

@ -803,6 +803,18 @@ func (c *InitCommand) backendConfigOverrideBody(flags rawFlags, schema *configsc
// The value is interpreted as a filename. // The value is interpreted as a filename.
newBody, fileDiags := c.loadHCLFile(item.Value) newBody, fileDiags := c.loadHCLFile(item.Value)
diags = diags.Append(fileDiags) diags = diags.Append(fileDiags)
// Verify that the file contains only key-values pairs, and not a
// full backend config block. JustAttributes() will return an error
// if blocks are found
_, attrDiags := newBody.JustAttributes()
if attrDiags.HasErrors() {
diags = diags.Append(tfdiags.Sourceless(
tfdiags.Error,
"Invalid backend configuration file",
fmt.Sprintf("The backend configuration file %q given on the command line must contain key-value pairs only, and not configuration blocks.", item.Value),
))
continue
}
flushVals() // deal with any accumulated individual values first flushVals() // deal with any accumulated individual values first
mergeBody(newBody) mergeBody(newBody)
} else { } else {

View File

@ -337,24 +337,43 @@ func TestInit_backendConfigFile(t *testing.T) {
defer os.RemoveAll(td) defer os.RemoveAll(td)
defer testChdir(t, td)() defer testChdir(t, td)()
ui := new(cli.MockUi) t.Run("good-config-file", func(t *testing.T) {
c := &InitCommand{ ui := new(cli.MockUi)
Meta: Meta{ c := &InitCommand{
testingOverrides: metaOverridesForProvider(testProvider()), Meta: Meta{
Ui: ui, testingOverrides: metaOverridesForProvider(testProvider()),
}, Ui: ui,
} },
}
args := []string{"-backend-config", "input.config"}
if code := c.Run(args); code != 0 {
t.Fatalf("bad: \n%s", ui.ErrorWriter.String())
}
args := []string{"-backend-config", "input.config"} // Read our saved backend config and verify we have our settings
if code := c.Run(args); code != 0 { state := testDataStateRead(t, filepath.Join(DefaultDataDir, DefaultStateFilename))
t.Fatalf("bad: \n%s", ui.ErrorWriter.String()) if got, want := normalizeJSON(t, state.Backend.ConfigRaw), `{"path":"hello","workspace_dir":null}`; got != want {
} t.Errorf("wrong config\ngot: %s\nwant: %s", got, want)
}
})
// Read our saved backend config and verify we have our settings // the backend config file must be a set of key-value pairs and not a full backend {} block
state := testDataStateRead(t, filepath.Join(DefaultDataDir, DefaultStateFilename)) t.Run("invalid-config-file", func(t *testing.T) {
if got, want := normalizeJSON(t, state.Backend.ConfigRaw), `{"path":"hello","workspace_dir":null}`; got != want { ui := new(cli.MockUi)
t.Errorf("wrong config\ngot: %s\nwant: %s", got, want) c := &InitCommand{
} Meta: Meta{
testingOverrides: metaOverridesForProvider(testProvider()),
Ui: ui,
},
}
args := []string{"-backend-config", "backend.config"}
if code := c.Run(args); code != 1 {
t.Fatalf("expected error, got success\n")
}
if !strings.Contains(ui.ErrorWriter.String(), "Invalid backend configuration file") {
t.Fatalf("wrong error: %s", ui.ErrorWriter)
}
})
} }
func TestInit_backendConfigFilePowershellConfusion(t *testing.T) { func TestInit_backendConfigFilePowershellConfusion(t *testing.T) {

View File

@ -0,0 +1,7 @@
// the -backend-config flag on init cannot be used to point to a "full" backend
// block, only key-value pairs (like terraform.tfvars)
terraform {
backend "local" {
path = "hello"
}
}

View File

@ -63,12 +63,12 @@ There are several ways to supply the remaining arguments:
values, unless interactive input is disabled. Terraform will not prompt for values, unless interactive input is disabled. Terraform will not prompt for
optional values. optional values.
* **File**: A configuration file may be specified via the `init` command line. * **File**: A [backend configuration file](#backend-configuration-file) may be specified via the
To specify a file, use the `-backend-config=PATH` option when running `init` command line. To specify a file, use the `-backend-config=PATH`
`terraform init`. If the file contains secrets it may be kept in option when running `terraform init`. If the file contains secrets it may be
a secure data store, such as kept in a secure data store, such as [Vault](https://www.vaultproject.io/),
[Vault](https://www.vaultproject.io/), in which case it must be downloaded in which case it must be downloaded to the local disk before running
to the local disk before running Terraform. Terraform.
* **Command-line key/value pairs**: Key/value pairs can be specified via the * **Command-line key/value pairs**: Key/value pairs can be specified via the
`init` command line. Note that many shells retain command-line flags in a `init` command line. Note that many shells retain command-line flags in a
@ -96,6 +96,7 @@ terraform {
} }
``` ```
## Backend Configuration File
A backend configuration file has the contents of the `backend` block as A backend configuration file has the contents of the `backend` block as
top-level attributes, without the need to wrap it in another `terraform` top-level attributes, without the need to wrap it in another `terraform`
or `backend` block: or `backend` block: