in import-generated code represent JSON values in HCL instead of as strings

This makes the value easier to read and is mostly an aesthetic change.

Closes #34444
pull/34630/head
Daniel Schmidt 2 years ago
parent 64cd0da758
commit ba043a04d5
No known key found for this signature in database
GPG Key ID: 377C3A4D62FBBBE2

@ -4,6 +4,7 @@
package genconfig
import (
"encoding/json"
"fmt"
"sort"
"strings"
@ -11,6 +12,7 @@ import (
"github.com/hashicorp/hcl/v2"
"github.com/hashicorp/hcl/v2/hclwrite"
"github.com/zclconf/go-cty/cty"
ctyjson "github.com/zclconf/go-cty/cty/json"
"github.com/hashicorp/terraform/internal/addrs"
"github.com/hashicorp/terraform/internal/configs/configschema"
@ -158,15 +160,46 @@ func writeConfigAttributesFromExisting(addr addrs.AbsResourceInstance, buf *stri
if attrS.Sensitive || val.IsMarked() {
buf.WriteString("null # sensitive")
} else {
tok := hclwrite.TokensForValue(val)
if _, err := tok.WriteTo(buf); err != nil {
diags = diags.Append(&hcl.Diagnostic{
Severity: hcl.DiagWarning,
Summary: "Skipped part of config generation",
Detail: fmt.Sprintf("Could not create attribute %s in %s when generating import configuration. The plan will likely report the missing attribute as being deleted.", name, addr),
Extra: err,
})
continue
// If the value is a string storing a JSON value we want to represent it in a terraform native way
// and encapsulate it in `jsonencode` as it is the idiomatic representation
if val.IsKnown() && !val.IsNull() && val.Type() == cty.String && json.Valid([]byte(val.AsString())) {
buf.WriteString("jsonencode(")
var ctyValue ctyjson.SimpleJSONValue
err := ctyValue.UnmarshalJSON([]byte(val.AsString()))
if err != nil {
diags = diags.Append(&hcl.Diagnostic{
Severity: hcl.DiagWarning,
Summary: "Failed to parse JSON",
Detail: fmt.Sprintf("Could not parse JSON value of attribute %s in %s when generating import configuration. The plan will likely report the missing attribute as being deleted. This is most likely a bug in Terraform, please report it.", name, addr),
Extra: err,
})
continue
}
tok := hclwrite.TokensForValue(ctyValue.Value)
if _, err := tok.WriteTo(buf); err != nil {
diags = diags.Append(&hcl.Diagnostic{
Severity: hcl.DiagWarning,
Summary: "Skipped part of config generation",
Detail: fmt.Sprintf("Could not create attribute %s in %s when generating import configuration. The plan will likely report the missing attribute as being deleted.", name, addr),
Extra: err,
})
continue
}
buf.WriteString(")")
} else {
tok := hclwrite.TokensForValue(val)
if _, err := tok.WriteTo(buf); err != nil {
diags = diags.Append(&hcl.Diagnostic{
Severity: hcl.DiagWarning,
Summary: "Skipped part of config generation",
Detail: fmt.Sprintf("Could not create attribute %s in %s when generating import configuration. The plan will likely report the missing attribute as being deleted.", name, addr),
Extra: err,
})
continue
}
}
}

@ -421,6 +421,121 @@ resource "tfcoremock_simple_resource" "empty" {
list = null
map = null
single = null
}`,
},
"simple_resource_with_stringified_json_object": {
schema: &configschema.Block{
// BlockTypes: map[string]*configschema.NestedBlock{},
Attributes: map[string]*configschema.Attribute{
"id": {
Type: cty.String,
Computed: true,
},
"value": {
Type: cty.String,
Optional: true,
},
},
},
addr: addrs.AbsResourceInstance{
Module: nil,
Resource: addrs.ResourceInstance{
Resource: addrs.Resource{
Mode: addrs.ManagedResourceMode,
Type: "tfcoremock_simple_resource",
Name: "empty",
},
Key: nil,
},
},
provider: addrs.LocalProviderConfig{
LocalName: "tfcoremock",
},
value: cty.ObjectVal(map[string]cty.Value{
"id": cty.StringVal("D2320658"),
"value": cty.StringVal(`{ "0Hello": "World", "And": ["Solar", "System"], "ready": true }`),
}),
expected: `
resource "tfcoremock_simple_resource" "empty" {
value = jsonencode({
"0Hello" = "World"
And = ["Solar", "System"]
ready = true
})
}`,
},
"simple_resource_with_stringified_json_array": {
schema: &configschema.Block{
// BlockTypes: map[string]*configschema.NestedBlock{},
Attributes: map[string]*configschema.Attribute{
"id": {
Type: cty.String,
Computed: true,
},
"value": {
Type: cty.String,
Optional: true,
},
},
},
addr: addrs.AbsResourceInstance{
Module: nil,
Resource: addrs.ResourceInstance{
Resource: addrs.Resource{
Mode: addrs.ManagedResourceMode,
Type: "tfcoremock_simple_resource",
Name: "empty",
},
Key: nil,
},
},
provider: addrs.LocalProviderConfig{
LocalName: "tfcoremock",
},
value: cty.ObjectVal(map[string]cty.Value{
"id": cty.StringVal("D2320658"),
"value": cty.StringVal(`["Hello", "World"]`),
}),
expected: `
resource "tfcoremock_simple_resource" "empty" {
value = jsonencode(["Hello", "World"])
}`,
},
"simple_resource_with_malformed_json": {
schema: &configschema.Block{
// BlockTypes: map[string]*configschema.NestedBlock{},
Attributes: map[string]*configschema.Attribute{
"id": {
Type: cty.String,
Computed: true,
},
"value": {
Type: cty.String,
Optional: true,
},
},
},
addr: addrs.AbsResourceInstance{
Module: nil,
Resource: addrs.ResourceInstance{
Resource: addrs.Resource{
Mode: addrs.ManagedResourceMode,
Type: "tfcoremock_simple_resource",
Name: "empty",
},
Key: nil,
},
},
provider: addrs.LocalProviderConfig{
LocalName: "tfcoremock",
},
value: cty.ObjectVal(map[string]cty.Value{
"id": cty.StringVal("D2320658"),
"value": cty.StringVal(`["Hello", "World"`),
}),
expected: `
resource "tfcoremock_simple_resource" "empty" {
value = "[\"Hello\", \"World\""
}`,
},
}

Loading…
Cancel
Save