Merge pull request #25960 from hashicorp/alisdair/backend-config-override-fix

command: Fix backend config override validation
This commit is contained in:
Alisdair McDiarmid 2020-08-24 10:14:16 -04:00 committed by GitHub
commit c5d9935c0e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 88 additions and 14 deletions

View File

@ -861,16 +861,31 @@ 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 if fileDiags.HasErrors() {
// full backend config block. JustAttributes() will return an error continue
// if blocks are found }
_, attrDiags := newBody.JustAttributes() // Generate an HCL body schema for the backend block.
if attrDiags.HasErrors() { var bodySchema hcl.BodySchema
diags = diags.Append(tfdiags.Sourceless( for name, attr := range schema.Attributes {
tfdiags.Error, bodySchema.Attributes = append(bodySchema.Attributes, hcl.AttributeSchema{
"Invalid backend configuration file", Name: name,
fmt.Sprintf("The backend configuration file %q given on the command line must contain key-value pairs only, and not configuration blocks.", item.Value), Required: attr.Required,
)) })
}
for name, block := range schema.BlockTypes {
var labelNames []string
if block.Nesting == configschema.NestingMap {
labelNames = append(labelNames, "key")
}
bodySchema.Blocks = append(bodySchema.Blocks, hcl.BlockHeaderSchema{
Type: name,
LabelNames: labelNames,
})
}
// Verify that the file body matches the expected backend schema.
_, schemaDiags := newBody.Content(&bodySchema)
diags = diags.Append(schemaDiags)
if schemaDiags.HasErrors() {
continue continue
} }
flushVals() // deal with any accumulated individual values first flushVals() // deal with any accumulated individual values first

View File

@ -355,8 +355,8 @@ func TestInit_backendConfigFile(t *testing.T) {
} }
}) })
// the backend config file must be a set of key-value pairs and not a full backend {} block // the backend config file must not be a full terraform block
t.Run("invalid-config-file", func(t *testing.T) { t.Run("full-backend-config-file", func(t *testing.T) {
ui := new(cli.MockUi) ui := new(cli.MockUi)
c := &InitCommand{ c := &InitCommand{
Meta: Meta{ Meta: Meta{
@ -368,10 +368,67 @@ func TestInit_backendConfigFile(t *testing.T) {
if code := c.Run(args); code != 1 { if code := c.Run(args); code != 1 {
t.Fatalf("expected error, got success\n") t.Fatalf("expected error, got success\n")
} }
if !strings.Contains(ui.ErrorWriter.String(), "Invalid backend configuration file") { if !strings.Contains(ui.ErrorWriter.String(), "Unsupported block type") {
t.Fatalf("wrong error: %s", ui.ErrorWriter) t.Fatalf("wrong error: %s", ui.ErrorWriter)
} }
}) })
// the backend config file must match the schema for the backend
t.Run("invalid-config-file", func(t *testing.T) {
ui := new(cli.MockUi)
c := &InitCommand{
Meta: Meta{
testingOverrides: metaOverridesForProvider(testProvider()),
Ui: ui,
},
}
args := []string{"-backend-config", "invalid.config"}
if code := c.Run(args); code != 1 {
t.Fatalf("expected error, got success\n")
}
if !strings.Contains(ui.ErrorWriter.String(), "Unsupported argument") {
t.Fatalf("wrong error: %s", ui.ErrorWriter)
}
})
// missing file is an error
t.Run("missing-config-file", func(t *testing.T) {
ui := new(cli.MockUi)
c := &InitCommand{
Meta: Meta{
testingOverrides: metaOverridesForProvider(testProvider()),
Ui: ui,
},
}
args := []string{"-backend-config", "missing.config"}
if code := c.Run(args); code != 1 {
t.Fatalf("expected error, got success\n")
}
if !strings.Contains(ui.ErrorWriter.String(), "Failed to read file") {
t.Fatalf("wrong error: %s", ui.ErrorWriter)
}
})
// blank filename clears the backend config
t.Run("blank-config-file", func(t *testing.T) {
ui := new(cli.MockUi)
c := &InitCommand{
Meta: Meta{
testingOverrides: metaOverridesForProvider(testProvider()),
Ui: ui,
},
}
args := []string{"-backend-config="}
if code := c.Run(args); code != 0 {
t.Fatalf("bad: \n%s", ui.ErrorWriter.String())
}
// Read our saved backend config and verify the backend config is empty
state := testDataStateRead(t, filepath.Join(DefaultDataDir, DefaultStateFilename))
if got, want := normalizeJSON(t, state.Backend.ConfigRaw), `{"path":null,"workspace_dir":null}`; got != want {
t.Errorf("wrong config\ngot: %s\nwant: %s", got, want)
}
})
} }
func TestInit_backendConfigFilePowershellConfusion(t *testing.T) { func TestInit_backendConfigFilePowershellConfusion(t *testing.T) {

View File

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

View File

@ -0,0 +1,2 @@
path = "hello"
foo = "bar"