Replies: 1 comment 7 replies
-
A good idea! You'll probably have to drop down to the https://pkg.go.dev/github.com/hashicorp/hcl2/hcl/hclsyntax At the point where anything is represented as a Go The lower-level https://pkg.go.dev/github.com/hashicorp/hcl2/hcl/hclsyntax#ObjectConsExpr Here's a starting point for you, partially GPT-written but edited to get some of the finer points of type handling right: package main
import (
"fmt"
"github.com/hashicorp/hcl/v2"
"github.com/hashicorp/hcl/v2/hclsyntax"
"github.com/zclconf/go-cty/cty"
)
func main() {
hclContent := `
resource "null_resource" "test" {
triggers = {
a = "b"
a = "c"
dynamic = "d"
(dynamic) = "e"
(unknown) = "f"
}
}
`
file, diagnostics := hclsyntax.ParseConfig([]byte(hclContent), "main.tf", hcl.InitialPos)
if diagnostics.HasErrors() {
panic(diagnostics.Error())
}
findDuplicateKeys(file.Body)
}
func findDuplicateKeys(body hcl.Body) {
content, _, diagnostics := body.PartialContent(&hcl.BodySchema{
Blocks: []hcl.BlockHeaderSchema{{Type: "resource", LabelNames: []string{"type", "name"}}},
})
if diagnostics.HasErrors() {
panic(diagnostics.Error())
}
for _, block := range content.Blocks {
processBlock(block)
}
}
func processBlock(block *hcl.Block) {
content, _, diags := block.Body.PartialContent(&hcl.BodySchema{
Attributes: []hcl.AttributeSchema{{Name: "triggers"}},
})
if diags.HasErrors() {
panic(diags.Error())
}
if triggers, exists := content.Attributes["triggers"]; exists {
expr := triggers.Expr
exprMap, ok := expr.(*hclsyntax.ObjectConsExpr)
if !ok {
panic("Expected object expression")
}
keys := make(map[string]struct{})
for _, item := range exprMap.Items {
expr := item.KeyExpr.(*hclsyntax.ObjectConsKeyExpr)
kv, diags := expr.Value(&hcl.EvalContext{
Variables: map[string]cty.Value{
"dynamic": cty.StringVal("dynamic"),
"unknown": cty.UnknownVal(cty.String),
},
})
if diags.HasErrors() {
panic(diags.Error())
}
if !kv.IsKnown() {
fmt.Printf("Unknown key, continuing")
continue
}
if _, exists := keys[kv.AsString()]; exists {
fmt.Printf("Duplicate key: %s\n", kv.AsString())
} else {
keys[kv.AsString()] = struct{}{}
}
}
}
} This prints:
The Hope this helps as a starting point! |
Beta Was this translation helpful? Give feedback.
-
Hi there,
I ran into this issue yesterday in production and was looking to add some tests to make sure it doesn't happen again.
Would it be possible to implement this check in tflint? I'm willing to do so but unfamiliar with the codebase. Will need some guidance.
Beta Was this translation helpful? Give feedback.
All reactions