configs/configupgrade: Upgrade the bodies of "provisioner" blocks

Aside from the two special meta-arguments "connection" and "provisioner"
this is just our standard mapping from schema to conversion rules, using
the provisioner's configuration schema.
This commit is contained in:
Martin Atkins 2019-02-21 17:51:37 -08:00
parent cdca8fbfe8
commit e2ef51800a
8 changed files with 108 additions and 21 deletions

View File

@ -3,7 +3,7 @@ resource "test_instance" "example" {
connection {
host = "127.0.0.1"
}
provisioner "local-exec" {
provisioner "test" {
connection {
host = "127.0.0.2"
}

View File

@ -3,7 +3,7 @@ resource "test_instance" "example" {
connection {
host = "127.0.0.1"
}
provisioner "local-exec" {
provisioner "test" {
connection {
host = "127.0.0.2"
}

View File

@ -0,0 +1,8 @@
resource "test_instance" "foo" {
provisioner "test" {
commands = "${list("a", "b", "c")}"
when = "create"
on_failure = "fail"
}
}

View File

@ -0,0 +1,8 @@
resource "test_instance" "foo" {
provisioner "test" {
commands = ["a", "b", "c"]
when = create
on_failure = fail
}
}

View File

@ -0,0 +1,3 @@
terraform {
required_version = ">= 0.12"
}

View File

@ -7,6 +7,7 @@ import (
"strings"
hcl1ast "github.com/hashicorp/hcl/hcl/ast"
hcl1printer "github.com/hashicorp/hcl/hcl/printer"
hcl1token "github.com/hashicorp/hcl/hcl/token"
hcl2 "github.com/hashicorp/hcl2/hcl"
hcl2syntax "github.com/hashicorp/hcl2/hcl/hclsyntax"
@ -554,3 +555,69 @@ func lifecycleBlockBodyRules(filename string, an *analysis) bodyContentRules {
},
}
}
func provisionerBlockRule(filename string, an *analysis, adhocComments *commentQueue) bodyItemRule {
// Unlike some other examples above, this is a rule for the entire
// provisioner block, rather than just for its contents. Therefore it must
// also produce the block header and body delimiters.
return func(buf *bytes.Buffer, blockAddr string, item *hcl1ast.ObjectItem) tfdiags.Diagnostics {
var diags tfdiags.Diagnostics
body := item.Val.(*hcl1ast.ObjectType)
declRange := hcl1PosRange(filename, item.Keys[0].Pos())
if len(item.Keys) < 2 {
diags = diags.Append(&hcl2.Diagnostic{
Severity: hcl2.DiagError,
Summary: "Invalid provisioner block",
Detail: "A provisioner block must have one label: the provisioner type.",
Subject: &declRange,
})
return diags
}
typeName := item.Keys[1].Token.Value().(string)
schema := an.ProvisionerSchemas[typeName]
if schema == nil {
// This message is assuming that if the user _is_ using a third-party
// provisioner plugin they already know how to install it for normal
// use and so we don't need to spell out those instructions in detail
// here.
diags = diags.Append(&hcl2.Diagnostic{
Severity: hcl2.DiagError,
Summary: "Unknown provisioner type",
Detail: fmt.Sprintf("The provisioner type %q is not supported. If this is a third-party plugin, make sure its plugin executable is available in one of the usual plugin search paths.", typeName),
Subject: &declRange,
})
return diags
}
rules := schemaDefaultBodyRules(filename, schema, an, adhocComments)
rules["when"] = maybeBareTraversalAttributeRule(filename, an)
rules["on_failure"] = maybeBareTraversalAttributeRule(filename, an)
rules["connection"] = connectionBlockRule(filename, an, adhocComments)
printComments(buf, item.LeadComment)
printBlockOpen(buf, "provisioner", []string{typeName}, item.LineComment)
bodyDiags := upgradeBlockBody(filename, fmt.Sprintf("%s.provisioner[%q]", blockAddr, typeName), buf, body.List.Items, body.Rbrace, rules, adhocComments)
diags = diags.Append(bodyDiags)
buf.WriteString("}\n")
return diags
}
}
func connectionBlockRule(filename string, an *analysis, adhocComments *commentQueue) bodyItemRule {
// Unlike some other examples above, this is a rule for the entire
// connection block, rather than just for its contents. Therefore it must
// also produce the block header and body delimiters.
return func(buf *bytes.Buffer, blockAddr string, item *hcl1ast.ObjectItem) tfdiags.Diagnostics {
// TODO: For the few resource types that were setting ConnInfo in
// state after create/update in prior versions, generate the additional
// explicit connection settings that are now required if and only if
// there's at least one provisioner block.
// For now, we just pass this through as-is.
hcl1printer.Fprint(buf, item)
buf.WriteByte('\n')
return nil
}
}

View File

@ -329,24 +329,8 @@ func (u *Upgrader) upgradeNativeSyntaxResource(filename string, buf *bytes.Buffe
rules["depends_on"] = dependsOnAttributeRule(filename, an)
rules["provider"] = maybeBareTraversalAttributeRule(filename, an)
rules["lifecycle"] = nestedBlockRule(filename, lifecycleBlockBodyRules(filename, an), an, adhocComments)
rules["connection"] = func(buf *bytes.Buffer, blockAddr string, item *hcl1ast.ObjectItem) tfdiags.Diagnostics {
// TODO: For the few resource types that were setting ConnInfo in
// state after create/update in prior versions, generate the additional
// explicit connection settings that are now required if and only if
// there's at least one provisioner block.
// For now, we just pass this through as-is.
hcl1printer.Fprint(buf, item)
buf.WriteByte('\n')
return nil
}
rules["provisioner"] = func(buf *bytes.Buffer, blockAddr string, item *hcl1ast.ObjectItem) tfdiags.Diagnostics {
// TODO: Look up the provisioner schema and map this properly to ensure
// any references get properly updated.
// For now, we just pass this through as-is.
hcl1printer.Fprint(buf, item)
buf.WriteByte('\n')
return nil
}
rules["connection"] = connectionBlockRule(filename, an, adhocComments)
rules["provisioner"] = provisionerBlockRule(filename, an, adhocComments)
printComments(buf, item.LeadComment)
printBlockOpen(buf, blockType, labels, item.LineComment)

View File

@ -17,6 +17,7 @@ import (
"github.com/hashicorp/terraform/configs/configschema"
"github.com/hashicorp/terraform/helper/logging"
"github.com/hashicorp/terraform/providers"
"github.com/hashicorp/terraform/provisioners"
"github.com/hashicorp/terraform/terraform"
)
@ -40,6 +41,7 @@ func TestUpgradeValid(t *testing.T) {
wantDir := filepath.Join(fixtureDir, entry.Name(), "want")
u := &Upgrader{
Providers: providers.ResolverFixed(testProviders),
Provisioners: testProvisioners,
}
inputSrc, err := LoadModule(inputDir)
@ -250,6 +252,21 @@ var testProviders = map[string]providers.Factory{
}),
}
var testProvisioners = map[string]provisioners.Factory{
"test": provisioners.Factory(func() (provisioners.Interface, error) {
p := &terraform.MockProvisioner{}
p.GetSchemaResponse = provisioners.GetSchemaResponse{
Provisioner: &configschema.Block{
Attributes: map[string]*configschema.Attribute{
"commands": {Type: cty.List(cty.String), Optional: true},
"interpreter": {Type: cty.String, Optional: true},
},
},
}
return p, nil
}),
}
func init() {
// Initialize the backends
backendinit.Init(nil)