lang/eval: Apply attr-as-nested-block fixup in EvalBlock

For any block content we evaluate dynamically via this API, we'll make a
special allowance for users to optionally write members of a list
attribute instead as a sequence of nested blocks, thus allowing some
existing provider features that were assuming this capability to continue
to support it after v0.12.

This should not be used for any new provider features, and should ideally
be eventually phased out so that there aren't two
similar-but-slightly-different syntaxes for saying the same thing.
This commit is contained in:
Martin Atkins 2019-03-26 17:27:14 -07:00
parent 6dcf8195b8
commit 87786484ea
2 changed files with 45 additions and 2 deletions

View File

@ -43,6 +43,41 @@ resource "test_resource_config_mode" "foo" {
},
resource.TestStep{
Config: strings.TrimSpace(`
resource "test_resource_config_mode" "foo" {
# Due to a preprocessing fixup we do in lang.EvalBlock, it's allowed
# to specify resource_as_attr members using one or more nested blocks
# instead of attribute syntax, if desired. This should be equivalent
# to the previous config.
#
# This allowance is made for backward-compatibility with existing providers
# before Terraform v0.12 that were expecting nested block types to also
# support attribute syntax; it should not be used for any new use-cases.
resource_as_attr {
foo = "resource_as_attr 0"
}
resource_as_attr {
foo = "resource_as_attr 1"
}
resource_as_attr_dynamic = [
{
foo = "resource_as_attr_dynamic 0"
},
{
},
]
}
`),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr("test_resource_config_mode.foo", "resource_as_attr.#", "2"),
resource.TestCheckResourceAttr("test_resource_config_mode.foo", "resource_as_attr.0.foo", "resource_as_attr 0"),
resource.TestCheckResourceAttr("test_resource_config_mode.foo", "resource_as_attr.1.foo", "resource_as_attr 1"),
resource.TestCheckResourceAttr("test_resource_config_mode.foo", "resource_as_attr_dynamic.#", "2"),
resource.TestCheckResourceAttr("test_resource_config_mode.foo", "resource_as_attr_dynamic.0.foo", "resource_as_attr_dynamic 0"),
resource.TestCheckResourceAttr("test_resource_config_mode.foo", "resource_as_attr_dynamic.1.foo", "default"),
),
},
resource.TestStep{
Config: strings.TrimSpace(`
resource "test_resource_config_mode" "foo" {
resource_as_attr = [
{

View File

@ -5,12 +5,12 @@ import (
"log"
"strconv"
"github.com/hashicorp/terraform/addrs"
"github.com/hashicorp/hcl2/ext/dynblock"
"github.com/hashicorp/hcl2/hcl"
"github.com/hashicorp/hcl2/hcldec"
"github.com/hashicorp/terraform/addrs"
"github.com/hashicorp/terraform/configs/configschema"
"github.com/hashicorp/terraform/lang/blocktoattr"
"github.com/hashicorp/terraform/tfdiags"
"github.com/zclconf/go-cty/cty"
"github.com/zclconf/go-cty/cty/convert"
@ -58,6 +58,14 @@ func (s *Scope) EvalBlock(body hcl.Body, schema *configschema.Block) (cty.Value,
return cty.UnknownVal(schema.ImpliedType()), diags
}
// HACK: In order to remain compatible with some assumptions made in
// Terraform v0.11 and earlier about the approximate equivalence of
// attribute vs. block syntax, we do a just-in-time fixup here to allow
// any attribute in the schema that has a list-of-objects or set-of-objects
// kind to potentially be populated instead by one or more nested blocks
// whose type is the attribute name.
body = blocktoattr.FixUpBlockAttrs(body, schema)
val, evalDiags := hcldec.Decode(body, spec, ctx)
diags = diags.Append(evalDiags)