genconfig: do not generate null NestingSingle blocks (#33213)

* genconfig: fix nil nested block panic

* always InternalValidate test schemas

* genconfig: null NestingSingle blocks should be absent

A NestingSingle block that is null in state should be completely absent from config.
This commit is contained in:
kmoe 2023-05-19 11:32:28 -07:00 committed by GitHub
parent 048cc65787
commit 4015f1aa30
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 71 additions and 2 deletions

View File

@ -17,7 +17,7 @@ import (
// GenerateResourceContents generates HCL configuration code for the provided
// resource and state value.
//
// If you want tot generate actual valid Terraform code you should follow this
// If you want to generate actual valid Terraform code you should follow this
// call up with a call to WrapResourceContents, which will place a Terraform
// resource header around the attributes and blocks returned by this function.
func GenerateResourceContents(addr addrs.AbsResourceInstance,
@ -140,7 +140,7 @@ func writeConfigAttributesFromExisting(addr addrs.AbsResourceInstance, buf *stri
buf.WriteString(fmt.Sprintf("%s = ", name))
var val cty.Value
if stateVal.Type().HasAttribute(name) {
if !stateVal.IsNull() && stateVal.Type().HasAttribute(name) {
val = stateVal.GetAttr(name)
} else {
val = attrS.EmptyValue()
@ -422,6 +422,9 @@ func writeConfigNestedBlockFromExisting(addr addrs.AbsResourceInstance, buf *str
switch schema.Nesting {
case configschema.NestingSingle, configschema.NestingGroup:
if stateVal.IsNull() {
return diags
}
buf.WriteString(strings.Repeat(" ", indent))
buf.WriteString(fmt.Sprintf("%s {", name))

View File

@ -302,6 +302,7 @@ resource "tfcoremock_simple_resource" "empty" {
Attributes: map[string]*configschema.Attribute{},
Nesting: configschema.NestingSingle,
},
Required: true,
},
"list": {
NestedType: &configschema.Object{
@ -313,6 +314,7 @@ resource "tfcoremock_simple_resource" "empty" {
},
Nesting: configschema.NestingList,
},
Required: true,
},
"map": {
NestedType: &configschema.Object{
@ -324,6 +326,54 @@ resource "tfcoremock_simple_resource" "empty" {
},
Nesting: configschema.NestingMap,
},
Required: true,
},
},
BlockTypes: map[string]*configschema.NestedBlock{
"nested_single": {
Nesting: configschema.NestingSingle,
Block: configschema.Block{
Attributes: map[string]*configschema.Attribute{
"nested_id": {
Type: cty.String,
Optional: true,
},
},
},
},
// No configschema.NestingGroup example for this test, because this block type can never be null in state.
"nested_list": {
Nesting: configschema.NestingList,
Block: configschema.Block{
Attributes: map[string]*configschema.Attribute{
"nested_id": {
Type: cty.String,
Optional: true,
},
},
},
},
"nested_set": {
Nesting: configschema.NestingSet,
Block: configschema.Block{
Attributes: map[string]*configschema.Attribute{
"nested_id": {
Type: cty.String,
Optional: true,
},
},
},
},
"nested_map": {
Nesting: configschema.NestingMap,
Block: configschema.Block{
Attributes: map[string]*configschema.Attribute{
"nested_id": {
Type: cty.String,
Optional: true,
},
},
},
},
},
},
@ -350,6 +400,18 @@ resource "tfcoremock_simple_resource" "empty" {
"map": cty.NullVal(cty.Map(cty.Object(map[string]cty.Type{
"nested_id": cty.String,
}))),
"nested_single": cty.NullVal(cty.Object(map[string]cty.Type{
"nested_id": cty.String,
})),
"nested_list": cty.ListValEmpty(cty.Object(map[string]cty.Type{
"nested_id": cty.String,
})),
"nested_set": cty.SetValEmpty(cty.Object(map[string]cty.Type{
"nested_id": cty.String,
})),
"nested_map": cty.MapValEmpty(cty.Object(map[string]cty.Type{
"nested_id": cty.String,
})),
}),
expected: `
resource "tfcoremock_simple_resource" "empty" {
@ -361,6 +423,10 @@ resource "tfcoremock_simple_resource" "empty" {
}
for name, tc := range tcs {
t.Run(name, func(t *testing.T) {
err := tc.schema.InternalValidate()
if err != nil {
t.Fatalf("schema failed InternalValidate: %s", err)
}
contents, diags := GenerateResourceContents(tc.addr, tc.schema, tc.provider, tc.value)
if len(diags) > 0 {
t.Errorf("expected no diagnostics but found %s", diags)