From 7454e5ae49b90e9d0b73fcc1430e236e58006957 Mon Sep 17 00:00:00 2001 From: Liam Cervante Date: Fri, 11 Oct 2024 07:59:30 +0000 Subject: [PATCH] backport of commit d7f1d5b5953ef6afcac00e669f22fd1718b25180 --- internal/genconfig/generate_config.go | 10 +-- internal/genconfig/generate_config_test.go | 88 ++++++++++++++++++++++ 2 files changed, 90 insertions(+), 8 deletions(-) diff --git a/internal/genconfig/generate_config.go b/internal/genconfig/generate_config.go index 44387e19c72a..5843c88b3e37 100644 --- a/internal/genconfig/generate_config.go +++ b/internal/genconfig/generate_config.go @@ -6,11 +6,11 @@ package genconfig import ( "encoding/json" "fmt" - "regexp" "sort" "strings" "github.com/hashicorp/hcl/v2" + "github.com/hashicorp/hcl/v2/hclsyntax" "github.com/hashicorp/hcl/v2/hclwrite" "github.com/zclconf/go-cty/cty" ctyjson "github.com/zclconf/go-cty/cty/json" @@ -20,12 +20,6 @@ import ( "github.com/hashicorp/terraform/internal/tfdiags" ) -var ( - // whitespace is a regular expression that matches one or more whitespace - // characters. - whitespace = regexp.MustCompile(`\s+`) -) - // GenerateResourceContents generates HCL configuration code for the provided // resource and state value. // @@ -592,7 +586,7 @@ func ctyCollectionValues(val cty.Value) []cty.Value { func hclEscapeString(str string) string { // TODO: Replace this with more complete HCL logic instead of the simple // go workaround. - if whitespace.MatchString(str) { + if !hclsyntax.ValidIdentifier(str) { return fmt.Sprintf("%q", str) } return str diff --git a/internal/genconfig/generate_config_test.go b/internal/genconfig/generate_config_test.go index 12e3c9642fc4..a1392326773a 100644 --- a/internal/genconfig/generate_config_test.go +++ b/internal/genconfig/generate_config_test.go @@ -686,6 +686,94 @@ resource "tfcoremock_sensitive_values" "values" { value = "underscores" } } +}`, + }, + "simple_map_with_periods_in_keys": { + schema: &configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "map": { + Type: cty.Map(cty.String), + Optional: true, + }, + }, + }, + addr: addrs.AbsResourceInstance{ + Module: addrs.RootModuleInstance, + Resource: addrs.ResourceInstance{ + Resource: addrs.Resource{ + Mode: addrs.ManagedResourceMode, + Type: "testing_resource", + Name: "resource", + }, + Key: addrs.NoKey, + }, + }, + provider: addrs.LocalProviderConfig{ + LocalName: "testing", + }, + value: cty.ObjectVal(map[string]cty.Value{ + "map": cty.MapVal(map[string]cty.Value{ + "key.with.periods": cty.StringVal("periods"), + "key_with_underscores": cty.StringVal("underscores"), + }), + }), + expected: `resource "testing_resource" "resource" { + map = { + "key.with.periods" = "periods" + key_with_underscores = "underscores" + } +}`, + }, + "nested_map_with_periods_in_keys": { + schema: &configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "map": { + NestedType: &configschema.Object{ + Attributes: map[string]*configschema.Attribute{ + "value": { + Type: cty.String, + Optional: true, + }, + }, + Nesting: configschema.NestingMap, + }, + Optional: true, + }, + }, + }, + addr: addrs.AbsResourceInstance{ + Module: addrs.RootModuleInstance, + Resource: addrs.ResourceInstance{ + Resource: addrs.Resource{ + Mode: addrs.ManagedResourceMode, + Type: "testing_resource", + Name: "resource", + }, + Key: addrs.NoKey, + }, + }, + provider: addrs.LocalProviderConfig{ + LocalName: "testing", + }, + value: cty.ObjectVal(map[string]cty.Value{ + "map": cty.MapVal(map[string]cty.Value{ + "key.with.periods": cty.ObjectVal(map[string]cty.Value{ + "value": cty.StringVal("periods"), + }), + "key_with_underscores": cty.ObjectVal(map[string]cty.Value{ + "value": cty.StringVal("underscores"), + }), + }), + }), + expected: `resource "testing_resource" "resource" { + map = { + "key.with.periods" = { + value = "periods" + } + key_with_underscores = { + value = "underscores" + } + } }`, }, }