configs/configupgrade: Upgrading of simple nested blocks
This commit is contained in:
parent
39c3e7112f
commit
f96d702d4f
|
@ -0,0 +1,10 @@
|
||||||
|
resource "test_instance" "foo" {
|
||||||
|
network = [
|
||||||
|
{
|
||||||
|
cidr_block = "10.1.0.0/16"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
cidr_block = "10.2.0.0/16"
|
||||||
|
},
|
||||||
|
]
|
||||||
|
}
|
|
@ -0,0 +1,8 @@
|
||||||
|
resource "test_instance" "foo" {
|
||||||
|
network {
|
||||||
|
cidr_block = "10.1.0.0/16"
|
||||||
|
}
|
||||||
|
network {
|
||||||
|
cidr_block = "10.2.0.0/16"
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,3 @@
|
||||||
|
terraform {
|
||||||
|
required_version = ">= 0.12"
|
||||||
|
}
|
|
@ -0,0 +1,5 @@
|
||||||
|
resource "test_instance" "foo" {
|
||||||
|
network = {
|
||||||
|
cidr_block = "10.1.0.0/16"
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,5 @@
|
||||||
|
resource "test_instance" "foo" {
|
||||||
|
network {
|
||||||
|
cidr_block = "10.1.0.0/16"
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,3 @@
|
||||||
|
terraform {
|
||||||
|
required_version = ">= 0.12"
|
||||||
|
}
|
|
@ -123,12 +123,102 @@ func attributeRule(filename string, wantTy cty.Type, an *analysis, upgradeExpr f
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func nestedBlockRule(filename string, nestedRules bodyContentRules, an *analysis) bodyItemRule {
|
func nestedBlockRule(filename string, nestedRules bodyContentRules, an *analysis, adhocComments *commentQueue) bodyItemRule {
|
||||||
return func(buf *bytes.Buffer, blockAddr string, item *hcl1ast.ObjectItem) tfdiags.Diagnostics {
|
return func(buf *bytes.Buffer, blockAddr string, item *hcl1ast.ObjectItem) tfdiags.Diagnostics {
|
||||||
// TODO: Deal with this.
|
// This simpler nestedBlockRule is for contexts where the special
|
||||||
// In particular we need to handle the tricky case where
|
// "dynamic" block type is not accepted and so only HCL1 object
|
||||||
// a user attempts to treat a block type name like it's
|
// constructs can be accepted. Attempts to assign arbitrary HIL
|
||||||
// an attribute, by producing a "dynamic" block.
|
// expressions will be rejected as errors.
|
||||||
|
|
||||||
|
var diags tfdiags.Diagnostics
|
||||||
|
declRange := hcl1PosRange(filename, item.Keys[0].Pos())
|
||||||
|
blockType := item.Keys[0].Token.Value().(string)
|
||||||
|
labels := make([]string, len(item.Keys)-1)
|
||||||
|
for i, key := range item.Keys[1:] {
|
||||||
|
labels[i] = key.Token.Value().(string)
|
||||||
|
}
|
||||||
|
|
||||||
|
var blockItems []*hcl1ast.ObjectType
|
||||||
|
|
||||||
|
switch val := item.Val.(type) {
|
||||||
|
|
||||||
|
case *hcl1ast.ObjectType:
|
||||||
|
blockItems = []*hcl1ast.ObjectType{val}
|
||||||
|
|
||||||
|
case *hcl1ast.ListType:
|
||||||
|
for _, node := range val.List {
|
||||||
|
switch listItem := node.(type) {
|
||||||
|
case *hcl1ast.ObjectType:
|
||||||
|
blockItems = append(blockItems, listItem)
|
||||||
|
default:
|
||||||
|
diags = diags.Append(&hcl2.Diagnostic{
|
||||||
|
Severity: hcl2.DiagError,
|
||||||
|
Summary: "Invalid value for nested block",
|
||||||
|
Detail: fmt.Sprintf("In %s the name %q is a nested block type, so any value assigned to it must be an object.", blockAddr, blockType),
|
||||||
|
Subject: hcl1PosRange(filename, node.Pos()).Ptr(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
diags = diags.Append(&hcl2.Diagnostic{
|
||||||
|
Severity: hcl2.DiagError,
|
||||||
|
Summary: "Invalid value for nested block",
|
||||||
|
Detail: fmt.Sprintf("In %s the name %q is a nested block type, so any value assigned to it must be an object.", blockAddr, blockType),
|
||||||
|
Subject: &declRange,
|
||||||
|
})
|
||||||
|
return diags
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, blockItem := range blockItems {
|
||||||
|
printBlockOpen(buf, blockType, labels, item.LineComment)
|
||||||
|
bodyDiags := upgradeBlockBody(
|
||||||
|
filename, fmt.Sprintf("%s.%s", blockAddr, blockType), buf,
|
||||||
|
blockItem.List.Items, nestedRules, adhocComments,
|
||||||
|
)
|
||||||
|
diags = diags.Append(bodyDiags)
|
||||||
|
buf.WriteString("}\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
return diags
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func nestedBlockRuleWithDynamic(filename string, nestedRules bodyContentRules, an *analysis, adhocComments *commentQueue) bodyItemRule {
|
||||||
|
return func(buf *bytes.Buffer, blockAddr string, item *hcl1ast.ObjectItem) tfdiags.Diagnostics {
|
||||||
|
// In Terraform v0.11 it was possible in some cases to trick Terraform
|
||||||
|
// and providers into accepting HCL's attribute syntax and some HIL
|
||||||
|
// expressions in places where blocks or sequences of blocks were
|
||||||
|
// expected, since the information about the heritage of the values
|
||||||
|
// was lost during decoding and interpolation.
|
||||||
|
//
|
||||||
|
// In order to avoid all of the weird rough edges that resulted from
|
||||||
|
// those misinterpretations, Terraform v0.12 is stricter and requires
|
||||||
|
// the use of block syntax for blocks in all cases. However, because
|
||||||
|
// various abuses of attribute syntax _did_ work (with some caveats)
|
||||||
|
// in v0.11 we will upgrade them as best we can to use proper block
|
||||||
|
// syntax.
|
||||||
|
//
|
||||||
|
// There are a few different permutations supported by this code:
|
||||||
|
//
|
||||||
|
// - Assigning a single HCL1 "object" using attribute syntax. This is
|
||||||
|
// straightforward to migrate just by dropping the equals sign.
|
||||||
|
//
|
||||||
|
// - Assigning a HCL1 list of objects using attribute syntax. Each
|
||||||
|
// object in that list can be translated to a separate block.
|
||||||
|
//
|
||||||
|
// - Assigning a HCL1 list containing HIL expressions that evaluate
|
||||||
|
// to maps. This is a hard case because we can't know the internal
|
||||||
|
// structure of those maps during static analysis, and so we must
|
||||||
|
// generate a worst-case dynamic block structure for it.
|
||||||
|
//
|
||||||
|
// - Assigning a single HIL expression that evaluates to a list of
|
||||||
|
// maps. This is just like the previous case except additionally
|
||||||
|
// we cannot even predict the number of generated blocks, so we must
|
||||||
|
// generate a single "dynamic" block to iterate over the list at
|
||||||
|
// runtime.
|
||||||
|
|
||||||
|
// TODO: Implement this
|
||||||
hcl1printer.Fprint(buf, item)
|
hcl1printer.Fprint(buf, item)
|
||||||
buf.WriteByte('\n')
|
buf.WriteByte('\n')
|
||||||
return nil
|
return nil
|
||||||
|
@ -140,7 +230,7 @@ func nestedBlockRule(filename string, nestedRules bodyContentRules, an *analysis
|
||||||
// callers can safely mutate the result in order to impose custom rules
|
// callers can safely mutate the result in order to impose custom rules
|
||||||
// in addition to or instead of those created by default, for situations
|
// in addition to or instead of those created by default, for situations
|
||||||
// where schema-based and predefined items mix in a single body.
|
// where schema-based and predefined items mix in a single body.
|
||||||
func schemaDefaultBodyRules(filename string, schema *configschema.Block, an *analysis) bodyContentRules {
|
func schemaDefaultBodyRules(filename string, schema *configschema.Block, an *analysis, adhocComments *commentQueue) bodyContentRules {
|
||||||
ret := make(bodyContentRules)
|
ret := make(bodyContentRules)
|
||||||
if schema == nil {
|
if schema == nil {
|
||||||
// Shouldn't happen in any real case, but often crops up in tests
|
// Shouldn't happen in any real case, but often crops up in tests
|
||||||
|
@ -152,8 +242,8 @@ func schemaDefaultBodyRules(filename string, schema *configschema.Block, an *ana
|
||||||
ret[name] = normalAttributeRule(filename, attrS.Type, an)
|
ret[name] = normalAttributeRule(filename, attrS.Type, an)
|
||||||
}
|
}
|
||||||
for name, blockS := range schema.BlockTypes {
|
for name, blockS := range schema.BlockTypes {
|
||||||
nestedRules := schemaDefaultBodyRules(filename, &blockS.Block, an)
|
nestedRules := schemaDefaultBodyRules(filename, &blockS.Block, an, adhocComments)
|
||||||
ret[name] = nestedBlockRule(filename, nestedRules, an)
|
ret[name] = nestedBlockRule(filename, nestedRules, an, adhocComments)
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret
|
return ret
|
||||||
|
@ -164,7 +254,7 @@ func schemaDefaultBodyRules(filename string, schema *configschema.Block, an *ana
|
||||||
// callers can safely mutate the result in order to impose custom rules
|
// callers can safely mutate the result in order to impose custom rules
|
||||||
// in addition to or instead of those created by default, for situations
|
// in addition to or instead of those created by default, for situations
|
||||||
// where schema-based and predefined items mix in a single body.
|
// where schema-based and predefined items mix in a single body.
|
||||||
func schemaNoInterpBodyRules(filename string, schema *configschema.Block, an *analysis) bodyContentRules {
|
func schemaNoInterpBodyRules(filename string, schema *configschema.Block, an *analysis, adhocComments *commentQueue) bodyContentRules {
|
||||||
ret := make(bodyContentRules)
|
ret := make(bodyContentRules)
|
||||||
if schema == nil {
|
if schema == nil {
|
||||||
// Shouldn't happen in any real case, but often crops up in tests
|
// Shouldn't happen in any real case, but often crops up in tests
|
||||||
|
@ -176,8 +266,8 @@ func schemaNoInterpBodyRules(filename string, schema *configschema.Block, an *an
|
||||||
ret[name] = noInterpAttributeRule(filename, attrS.Type, an)
|
ret[name] = noInterpAttributeRule(filename, attrS.Type, an)
|
||||||
}
|
}
|
||||||
for name, blockS := range schema.BlockTypes {
|
for name, blockS := range schema.BlockTypes {
|
||||||
nestedRules := schemaDefaultBodyRules(filename, &blockS.Block, an)
|
nestedRules := schemaDefaultBodyRules(filename, &blockS.Block, an, adhocComments)
|
||||||
ret[name] = nestedBlockRule(filename, nestedRules, an)
|
ret[name] = nestedBlockRule(filename, nestedRules, an, adhocComments)
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret
|
return ret
|
||||||
|
|
|
@ -161,7 +161,7 @@ func (u *Upgrader) upgradeNativeSyntaxFile(filename string, src []byte, an *anal
|
||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
log.Printf("[TRACE] configupgrade: Upgrading var.%s at %s", labels[0], declRange)
|
log.Printf("[TRACE] configupgrade: Upgrading var.%s at %s", labels[0], declRange)
|
||||||
bodyDiags := u.upgradeBlockBody(filename, fmt.Sprintf("var.%s", labels[0]), &buf, body.List.Items, rules, adhocComments)
|
bodyDiags := upgradeBlockBody(filename, fmt.Sprintf("var.%s", labels[0]), &buf, body.List.Items, rules, adhocComments)
|
||||||
diags = diags.Append(bodyDiags)
|
diags = diags.Append(bodyDiags)
|
||||||
buf.WriteString("}\n\n")
|
buf.WriteString("}\n\n")
|
||||||
|
|
||||||
|
@ -186,7 +186,7 @@ func (u *Upgrader) upgradeNativeSyntaxFile(filename string, src []byte, an *anal
|
||||||
"depends_on": dependsOnAttributeRule(filename, an),
|
"depends_on": dependsOnAttributeRule(filename, an),
|
||||||
}
|
}
|
||||||
log.Printf("[TRACE] configupgrade: Upgrading output.%s at %s", labels[0], declRange)
|
log.Printf("[TRACE] configupgrade: Upgrading output.%s at %s", labels[0], declRange)
|
||||||
bodyDiags := u.upgradeBlockBody(filename, fmt.Sprintf("output.%s", labels[0]), &buf, body.List.Items, rules, adhocComments)
|
bodyDiags := upgradeBlockBody(filename, fmt.Sprintf("output.%s", labels[0]), &buf, body.List.Items, rules, adhocComments)
|
||||||
diags = diags.Append(bodyDiags)
|
diags = diags.Append(bodyDiags)
|
||||||
buf.WriteString("}\n\n")
|
buf.WriteString("}\n\n")
|
||||||
|
|
||||||
|
@ -307,11 +307,11 @@ func (u *Upgrader) upgradeNativeSyntaxResource(filename string, buf *bytes.Buffe
|
||||||
}
|
}
|
||||||
labels := []string{addr.Type, addr.Name}
|
labels := []string{addr.Type, addr.Name}
|
||||||
|
|
||||||
rules := schemaDefaultBodyRules(filename, schema, an)
|
rules := schemaDefaultBodyRules(filename, schema, an, adhocComments)
|
||||||
|
|
||||||
printComments(buf, item.LeadComment)
|
printComments(buf, item.LeadComment)
|
||||||
printBlockOpen(buf, blockType, labels, item.LineComment)
|
printBlockOpen(buf, blockType, labels, item.LineComment)
|
||||||
bodyDiags := u.upgradeBlockBody(filename, addr.String(), buf, body.List.Items, rules, adhocComments)
|
bodyDiags := upgradeBlockBody(filename, addr.String(), buf, body.List.Items, rules, adhocComments)
|
||||||
diags = diags.Append(bodyDiags)
|
diags = diags.Append(bodyDiags)
|
||||||
buf.WriteString("}\n\n")
|
buf.WriteString("}\n\n")
|
||||||
|
|
||||||
|
@ -330,11 +330,11 @@ func (u *Upgrader) upgradeNativeSyntaxProvider(filename string, buf *bytes.Buffe
|
||||||
panic(fmt.Sprintf("missing schema for provider type %q", typeName))
|
panic(fmt.Sprintf("missing schema for provider type %q", typeName))
|
||||||
}
|
}
|
||||||
schema := providerSchema.Provider
|
schema := providerSchema.Provider
|
||||||
rules := schemaDefaultBodyRules(filename, schema, an)
|
rules := schemaDefaultBodyRules(filename, schema, an, adhocComments)
|
||||||
|
|
||||||
printComments(buf, item.LeadComment)
|
printComments(buf, item.LeadComment)
|
||||||
printBlockOpen(buf, "provider", []string{typeName}, item.LineComment)
|
printBlockOpen(buf, "provider", []string{typeName}, item.LineComment)
|
||||||
bodyDiags := u.upgradeBlockBody(filename, fmt.Sprintf("provider.%s", typeName), buf, body.List.Items, rules, adhocComments)
|
bodyDiags := upgradeBlockBody(filename, fmt.Sprintf("provider.%s", typeName), buf, body.List.Items, rules, adhocComments)
|
||||||
diags = diags.Append(bodyDiags)
|
diags = diags.Append(bodyDiags)
|
||||||
buf.WriteString("}\n\n")
|
buf.WriteString("}\n\n")
|
||||||
|
|
||||||
|
@ -375,13 +375,13 @@ func (u *Upgrader) upgradeNativeSyntaxTerraformBlock(filename string, buf *bytes
|
||||||
}
|
}
|
||||||
be := beFn()
|
be := beFn()
|
||||||
schema := be.ConfigSchema()
|
schema := be.ConfigSchema()
|
||||||
rules := schemaNoInterpBodyRules(filename, schema, an)
|
rules := schemaNoInterpBodyRules(filename, schema, an, adhocComments)
|
||||||
|
|
||||||
body := item.Val.(*hcl1ast.ObjectType)
|
body := item.Val.(*hcl1ast.ObjectType)
|
||||||
|
|
||||||
printComments(buf, item.LeadComment)
|
printComments(buf, item.LeadComment)
|
||||||
printBlockOpen(buf, "backend", []string{typeName}, item.LineComment)
|
printBlockOpen(buf, "backend", []string{typeName}, item.LineComment)
|
||||||
bodyDiags := u.upgradeBlockBody(filename, fmt.Sprintf("terraform.backend.%s", typeName), buf, body.List.Items, rules, adhocComments)
|
bodyDiags := upgradeBlockBody(filename, fmt.Sprintf("terraform.backend.%s", typeName), buf, body.List.Items, rules, adhocComments)
|
||||||
diags = diags.Append(bodyDiags)
|
diags = diags.Append(bodyDiags)
|
||||||
buf.WriteString("}\n")
|
buf.WriteString("}\n")
|
||||||
|
|
||||||
|
@ -391,14 +391,14 @@ func (u *Upgrader) upgradeNativeSyntaxTerraformBlock(filename string, buf *bytes
|
||||||
|
|
||||||
printComments(buf, item.LeadComment)
|
printComments(buf, item.LeadComment)
|
||||||
printBlockOpen(buf, "terraform", nil, item.LineComment)
|
printBlockOpen(buf, "terraform", nil, item.LineComment)
|
||||||
bodyDiags := u.upgradeBlockBody(filename, "terraform", buf, body.List.Items, rules, adhocComments)
|
bodyDiags := upgradeBlockBody(filename, "terraform", buf, body.List.Items, rules, adhocComments)
|
||||||
diags = diags.Append(bodyDiags)
|
diags = diags.Append(bodyDiags)
|
||||||
buf.WriteString("}\n\n")
|
buf.WriteString("}\n\n")
|
||||||
|
|
||||||
return diags
|
return diags
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *Upgrader) upgradeBlockBody(filename string, blockAddr string, buf *bytes.Buffer, args []*hcl1ast.ObjectItem, rules bodyContentRules, adhocComments *commentQueue) tfdiags.Diagnostics {
|
func upgradeBlockBody(filename string, blockAddr string, buf *bytes.Buffer, args []*hcl1ast.ObjectItem, rules bodyContentRules, adhocComments *commentQueue) tfdiags.Diagnostics {
|
||||||
var diags tfdiags.Diagnostics
|
var diags tfdiags.Diagnostics
|
||||||
|
|
||||||
for i, arg := range args {
|
for i, arg := range args {
|
||||||
|
|
Loading…
Reference in New Issue