From 8e594f32aa109c6ce52f4e368d62ba5d74668349 Mon Sep 17 00:00:00 2001 From: Martin Atkins Date: Fri, 30 Nov 2018 15:56:03 -0800 Subject: [PATCH] configs/configupgrade: Upgrade rules for the "terraform" block type This includes the backend configuration, so we need to load the requested backend and migrate the included configuration per that schema. --- configs/configupgrade/upgrade_body.go | 24 +++++++++ configs/configupgrade/upgrade_native.go | 71 +++++++++++++++++++++++++ configs/configupgrade/upgrade_test.go | 6 +++ 3 files changed, 101 insertions(+) diff --git a/configs/configupgrade/upgrade_body.go b/configs/configupgrade/upgrade_body.go index bd4b81c5b..b175a32da 100644 --- a/configs/configupgrade/upgrade_body.go +++ b/configs/configupgrade/upgrade_body.go @@ -158,3 +158,27 @@ func schemaDefaultBodyRules(filename string, schema *configschema.Block, an *ana return ret } + +// schemaNoInterpBodyRules constructs standard body content rules for the given +// schema. Each call is guaranteed to produce a distinct object so that +// callers can safely mutate the result in order to impose custom rules +// in addition to or instead of those created by default, for situations +// where schema-based and predefined items mix in a single body. +func schemaNoInterpBodyRules(filename string, schema *configschema.Block, an *analysis) bodyContentRules { + ret := make(bodyContentRules) + if schema == nil { + // Shouldn't happen in any real case, but often crops up in tests + // where the mock schemas tend to be incomplete. + return ret + } + + for name, attrS := range schema.Attributes { + ret[name] = noInterpAttributeRule(filename, attrS.Type, an) + } + for name, blockS := range schema.BlockTypes { + nestedRules := schemaDefaultBodyRules(filename, &blockS.Block, an) + ret[name] = nestedBlockRule(filename, nestedRules, an) + } + + return ret +} diff --git a/configs/configupgrade/upgrade_native.go b/configs/configupgrade/upgrade_native.go index c82355713..21a088493 100644 --- a/configs/configupgrade/upgrade_native.go +++ b/configs/configupgrade/upgrade_native.go @@ -18,6 +18,7 @@ import ( "github.com/zclconf/go-cty/cty" "github.com/hashicorp/terraform/addrs" + backendinit "github.com/hashicorp/terraform/backend/init" "github.com/hashicorp/terraform/tfdiags" ) @@ -117,6 +118,19 @@ func (u *Upgrader) upgradeNativeSyntaxFile(filename string, src []byte, an *anal moreDiags := u.upgradeNativeSyntaxProvider(filename, &buf, pType, item, an, adhocComments) diags = diags.Append(moreDiags) + case "terraform": + if len(labels) != 0 { + diags = diags.Append(&hcl2.Diagnostic{ + Severity: hcl2.DiagError, + Summary: fmt.Sprintf("Invalid %s block", blockType), + Detail: fmt.Sprintf("A %s block must not have any labels.", blockType), + Subject: &declRange, + }) + continue + } + moreDiags := u.upgradeNativeSyntaxTerraformBlock(filename, &buf, item, an, adhocComments) + diags = diags.Append(moreDiags) + case "variable": if len(labels) != 1 { diags = diags.Append(&hcl2.Diagnostic{ @@ -319,6 +333,63 @@ func (u *Upgrader) upgradeNativeSyntaxProvider(filename string, buf *bytes.Buffe return diags } +func (u *Upgrader) upgradeNativeSyntaxTerraformBlock(filename string, buf *bytes.Buffer, item *hcl1ast.ObjectItem, an *analysis, adhocComments *commentQueue) tfdiags.Diagnostics { + var diags tfdiags.Diagnostics + + body := item.Val.(*hcl1ast.ObjectType) + + rules := bodyContentRules{ + "required_version": noInterpAttributeRule(filename, cty.String, an), + "backend": func(buf *bytes.Buffer, blockAddr string, item *hcl1ast.ObjectItem) tfdiags.Diagnostics { + var diags tfdiags.Diagnostics + + declRange := hcl1PosRange(filename, item.Keys[0].Pos()) + if len(item.Keys) != 2 { + diags = diags.Append(&hcl2.Diagnostic{ + Severity: hcl2.DiagError, + Summary: `Invalid backend block`, + Detail: `A backend block must have one label: the backend type name.`, + Subject: &declRange, + }) + return diags + } + + typeName := item.Keys[1].Token.Value().(string) + beFn := backendinit.Backend(typeName) + if beFn == nil { + diags = diags.Append(&hcl2.Diagnostic{ + Severity: hcl2.DiagError, + Summary: "Unsupported backend type", + Detail: fmt.Sprintf("Terraform does not support a backend type named %q.", typeName), + Subject: &declRange, + }) + return diags + } + be := beFn() + schema := be.ConfigSchema() + rules := schemaNoInterpBodyRules(filename, schema, an) + + body := item.Val.(*hcl1ast.ObjectType) + + printComments(buf, item.LeadComment) + printBlockOpen(buf, "backend", []string{typeName}, item.LineComment) + bodyDiags := u.upgradeBlockBody(filename, fmt.Sprintf("terraform.backend.%s", typeName), buf, body.List.Items, rules, adhocComments) + diags = diags.Append(bodyDiags) + buf.WriteString("}\n") + + return diags + }, + } + + printComments(buf, item.LeadComment) + printBlockOpen(buf, "terraform", nil, item.LineComment) + bodyDiags := u.upgradeBlockBody(filename, "terraform", buf, body.List.Items, rules, adhocComments) + diags = diags.Append(bodyDiags) + buf.WriteString("}\n\n") + + return diags +} + func (u *Upgrader) upgradeBlockBody(filename string, blockAddr string, buf *bytes.Buffer, args []*hcl1ast.ObjectItem, rules bodyContentRules, adhocComments *commentQueue) tfdiags.Diagnostics { var diags tfdiags.Diagnostics diff --git a/configs/configupgrade/upgrade_test.go b/configs/configupgrade/upgrade_test.go index b94bda4a9..d5b85d162 100644 --- a/configs/configupgrade/upgrade_test.go +++ b/configs/configupgrade/upgrade_test.go @@ -9,6 +9,7 @@ import ( "path/filepath" "testing" + backendinit "github.com/hashicorp/terraform/backend/init" "github.com/hashicorp/terraform/configs/configschema" "github.com/hashicorp/terraform/providers" "github.com/hashicorp/terraform/terraform" @@ -188,3 +189,8 @@ var testProviders = map[string]providers.Factory{ return p, nil }), } + +func init() { + // Initialize the backends + backendinit.Init(nil) +}