Skip to content
Draft

test #4950

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 46 additions & 6 deletions cai2hcl/common/hcl_write.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@ package common

import (
"fmt"
"sort"
"strings"

"github.com/hashicorp/hcl/hcl/printer"
"github.com/hashicorp/hcl/v2"
"github.com/hashicorp/hcl/v2/hclwrite"
"github.com/zclconf/go-cty/cty"
)
Expand All @@ -13,17 +15,36 @@ func HclWriteBlocks(blocks []*HCLResourceBlock) ([]byte, error) {
f := hclwrite.NewFile()
rootBody := f.Body()

variableMap := make(map[string]struct{})

for _, resourceBlock := range blocks {
hclBlock := rootBody.AppendNewBlock("resource", resourceBlock.Labels)
if err := hclWriteBlock(resourceBlock.Value, hclBlock.Body()); err != nil {
if err := hclWriteBlock(resourceBlock.Value, hclBlock.Body(), variableMap); err != nil {
return nil, err
}
}

return printer.Format(f.Bytes())
varNames := make([]string, 0, len(variableMap))
for name := range variableMap {
varNames = append(varNames, name)
}
sort.Strings(varNames)

for _, name := range varNames {
rootBody.AppendNewline()
varBlock := rootBody.AppendNewBlock("variable", []string{name})

// type = string
t := hcl.Traversal{
hcl.TraverseRoot{Name: "string"},
}
varBlock.Body().SetAttributeTraversal("type", t)
}

return f.Bytes(), nil
}

func hclWriteBlock(val cty.Value, body *hclwrite.Body) error {
func hclWriteBlock(val cty.Value, body *hclwrite.Body, variableMap map[string]struct{}) error {
if val.IsNull() {
return nil
}
Expand All @@ -40,7 +61,7 @@ func hclWriteBlock(val cty.Value, body *hclwrite.Body) error {
switch {
case objValType.IsObjectType():
newBlock := body.AppendNewBlock(objKey.AsString(), nil)
if err := hclWriteBlock(objVal, newBlock.Body()); err != nil {
if err := hclWriteBlock(objVal, newBlock.Body(), variableMap); err != nil {
return err
}
case objValType.IsCollectionType():
Expand All @@ -53,14 +74,33 @@ func hclWriteBlock(val cty.Value, body *hclwrite.Body) error {
for listIterator.Next() {
_, listVal := listIterator.Element()
subBlock := body.AppendNewBlock(objKey.AsString(), nil)
if err := hclWriteBlock(listVal, subBlock.Body()); err != nil {
if err := hclWriteBlock(listVal, subBlock.Body(), variableMap); err != nil {
return err
}
}
continue
}
fallthrough
default:
if objValType == cty.String {
strVal := objVal.AsString()
if strVal == "" {
continue
}

if strings.HasPrefix(strVal, "unknown.") {
varName := strings.TrimPrefix(strVal, "unknown.")
variableMap[varName] = struct{}{}

t := hcl.Traversal{
hcl.TraverseRoot{Name: "var"},
hcl.TraverseAttr{Name: varName},
}
body.SetAttributeTraversal(objKey.AsString(), t)
continue
}
}

if objValType.FriendlyName() == "string" && objVal.AsString() == "" {
continue
}
Expand Down
44 changes: 44 additions & 0 deletions cai2hcl/common/hcl_write_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package common

import (
"strings"
"testing"

"github.com/stretchr/testify/assert"
"github.com/zclconf/go-cty/cty"
)

func TestHclWriteBlocks_VariableReplacement(t *testing.T) {
// Create a test block with a string value starting with "unknown."
blocks := []*HCLResourceBlock{
{
Labels: []string{"test_resource", "test_name"},
Value: cty.ObjectVal(map[string]cty.Value{
"attr1": cty.StringVal("unknown.my_variable"),
"attr2": cty.StringVal("normal_value"),
"attr3": cty.ObjectVal(map[string]cty.Value{
"nested_attr": cty.StringVal("unknown.another_variable"),
}),
}),
},
}

output, err := HclWriteBlocks(blocks)
assert.Nil(t, err)

hclString := string(output)

// Check if the variable references are correct
// Note: hclwrite output format might vary slightly, but we expect var.my_variable
// We expect "attr1 = var.my_variable" (no quotes around var.my_variable)
assert.Contains(t, hclString, "attr1 = var.my_variable")
assert.Contains(t, hclString, "nested_attr = var.another_variable")

// Check if normal strings are still quoted
assert.Contains(t, hclString, `attr2 = "normal_value"`)

// Check if variable blocks are generated
assert.True(t, strings.Contains(hclString, `variable "my_variable" {`), "missing variable block for my_variable")
assert.True(t, strings.Contains(hclString, `type = string`), "missing type = string")
assert.True(t, strings.Contains(hclString, `variable "another_variable" {`), "missing variable block for another_variable")
}