Skip to content

Commit a2b4473

Browse files
authored
apply opentofu patches (#3)
2 parents b0ec608 + 5a7bd01 commit a2b4473

21 files changed

+1694
-11
lines changed

ext/dynblock/expr_wrap.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,13 @@ func (e exprWrap) Variables() []hcl.Traversal {
4646
return ret
4747
}
4848

49+
func (e exprWrap) Functions() []hcl.Traversal {
50+
if fexpr, ok := e.Expression.(hcl.ExpressionWithFunctions); ok {
51+
return fexpr.Functions()
52+
}
53+
return nil
54+
}
55+
4956
func (e exprWrap) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics) {
5057
if e.i == nil {
5158
// If we don't have an active iteration then we can just use the

ext/dynblock/functions.go

Lines changed: 228 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,228 @@
1+
// Copyright (c) HashiCorp, Inc.
2+
// SPDX-License-Identifier: MPL-2.0
3+
4+
package dynblock
5+
6+
import (
7+
"github.com/terramate-io/hcl/v2"
8+
"github.com/terramate-io/hcl/v2/hclsyntax"
9+
"github.com/zclconf/go-cty/cty"
10+
)
11+
12+
// This is duplicated from ext/dynblock/variables.go and modified to suit functions
13+
14+
// WalkFunctions begins the recursive process of walking all expressions and
15+
// nested blocks in the given body and its child bodies while taking into
16+
// account any "dynamic" blocks.
17+
//
18+
// This function requires that the caller walk through the nested block
19+
// structure in the given body level-by-level so that an appropriate schema
20+
// can be provided at each level to inform further processing. This workflow
21+
// is thus easiest to use for calling applications that have some higher-level
22+
// schema representation available with which to drive this multi-step
23+
// process. If your application uses the hcldec package, you may be able to
24+
// use FunctionsHCLDec instead for a more automatic approach.
25+
func WalkFunctions(body hcl.Body) WalkFunctionsNode {
26+
return WalkFunctionsNode{
27+
body: body,
28+
includeContent: true,
29+
}
30+
}
31+
32+
// WalkExpandFunctions is like Functions but it includes only the functions
33+
// required for successful block expansion, ignoring any functions referenced
34+
// inside block contents. The result is the minimal set of all functions
35+
// required for a call to Expand, excluding functions that would only be
36+
// needed to subsequently call Content or PartialContent on the expanded
37+
// body.
38+
func WalkExpandFunctions(body hcl.Body) WalkFunctionsNode {
39+
return WalkFunctionsNode{
40+
body: body,
41+
}
42+
}
43+
44+
type WalkFunctionsNode struct {
45+
body hcl.Body
46+
it *iteration
47+
48+
includeContent bool
49+
}
50+
51+
type WalkFunctionsChild struct {
52+
BlockTypeName string
53+
Node WalkFunctionsNode
54+
}
55+
56+
// Body returns the HCL Body associated with the child node, in case the caller
57+
// wants to do some sort of inspection of it in order to decide what schema
58+
// to pass to Visit.
59+
//
60+
// Most implementations should just fetch a fixed schema based on the
61+
// BlockTypeName field and not access this. Deciding on a schema dynamically
62+
// based on the body is a strange thing to do and generally necessary only if
63+
// your caller is already doing other bizarre things with HCL bodies.
64+
func (c WalkFunctionsChild) Body() hcl.Body {
65+
return c.Node.body
66+
}
67+
68+
// exprFunctions handles the
69+
func exprFunctions(expr hcl.Expression) []hcl.Traversal {
70+
if ef, ok := expr.(hcl.ExpressionWithFunctions); ok {
71+
return ef.Functions()
72+
}
73+
// hclsyntax Fallback
74+
if hsexpr, ok := expr.(hclsyntax.Expression); ok {
75+
return hclsyntax.Functions(hsexpr)
76+
}
77+
// Not exposed
78+
return nil
79+
}
80+
81+
// Visit returns the function traversals required for any "dynamic" blocks
82+
// directly in the body associated with this node, and also returns any child
83+
// nodes that must be visited in order to continue the walk.
84+
//
85+
// Each child node has its associated block type name given in its BlockTypeName
86+
// field, which the calling application should use to determine the appropriate
87+
// schema for the content of each child node and pass it to the child node's
88+
// own Visit method to continue the walk recursively.
89+
func (n WalkFunctionsNode) Visit(schema *hcl.BodySchema) (vars []hcl.Traversal, children []WalkFunctionsChild) {
90+
extSchema := n.extendSchema(schema)
91+
container, _, _ := n.body.PartialContent(extSchema)
92+
if container == nil {
93+
return vars, children
94+
}
95+
96+
children = make([]WalkFunctionsChild, 0, len(container.Blocks))
97+
98+
if n.includeContent {
99+
for _, attr := range container.Attributes {
100+
for _, traversal := range exprFunctions(attr.Expr) {
101+
var ours, inherited bool
102+
if n.it != nil {
103+
ours = traversal.RootName() == n.it.IteratorName
104+
_, inherited = n.it.Inherited[traversal.RootName()]
105+
}
106+
107+
if !(ours || inherited) {
108+
vars = append(vars, traversal)
109+
}
110+
}
111+
}
112+
}
113+
114+
for _, block := range container.Blocks {
115+
switch block.Type {
116+
117+
case "dynamic":
118+
blockTypeName := block.Labels[0]
119+
inner, _, _ := block.Body.PartialContent(functionDetectionInnerSchema)
120+
if inner == nil {
121+
continue
122+
}
123+
124+
iteratorName := blockTypeName
125+
if attr, exists := inner.Attributes["iterator"]; exists {
126+
iterTraversal, _ := hcl.AbsTraversalForExpr(attr.Expr)
127+
if len(iterTraversal) == 0 {
128+
// Ignore this invalid dynamic block, since it'll produce
129+
// an error if someone tries to extract content from it
130+
// later anyway.
131+
continue
132+
}
133+
iteratorName = iterTraversal.RootName()
134+
}
135+
blockIt := n.it.MakeChild(iteratorName, cty.DynamicVal, cty.DynamicVal)
136+
137+
if attr, exists := inner.Attributes["for_each"]; exists {
138+
// Filter out iterator names inherited from parent blocks
139+
for _, traversal := range exprFunctions(attr.Expr) {
140+
if _, inherited := blockIt.Inherited[traversal.RootName()]; !inherited {
141+
vars = append(vars, traversal)
142+
}
143+
}
144+
}
145+
if attr, exists := inner.Attributes["labels"]; exists {
146+
// Filter out both our own iterator name _and_ those inherited
147+
// from parent blocks, since we provide _both_ of these to the
148+
// label expressions.
149+
for _, traversal := range exprFunctions(attr.Expr) {
150+
ours := traversal.RootName() == iteratorName
151+
_, inherited := blockIt.Inherited[traversal.RootName()]
152+
153+
if !(ours || inherited) {
154+
vars = append(vars, traversal)
155+
}
156+
}
157+
}
158+
159+
for _, contentBlock := range inner.Blocks {
160+
// We only request "content" blocks in our schema, so we know
161+
// any blocks we find here will be content blocks. We require
162+
// exactly one content block for actual expansion, but we'll
163+
// be more liberal here so that callers can still collect
164+
// functions from erroneous "dynamic" blocks.
165+
children = append(children, WalkFunctionsChild{
166+
BlockTypeName: blockTypeName,
167+
Node: WalkFunctionsNode{
168+
body: contentBlock.Body,
169+
it: blockIt,
170+
includeContent: n.includeContent,
171+
},
172+
})
173+
}
174+
175+
default:
176+
children = append(children, WalkFunctionsChild{
177+
BlockTypeName: block.Type,
178+
Node: WalkFunctionsNode{
179+
body: block.Body,
180+
it: n.it,
181+
includeContent: n.includeContent,
182+
},
183+
})
184+
185+
}
186+
}
187+
188+
return vars, children
189+
}
190+
191+
func (n WalkFunctionsNode) extendSchema(schema *hcl.BodySchema) *hcl.BodySchema {
192+
// We augment the requested schema to also include our special "dynamic"
193+
// block type, since then we'll get instances of it interleaved with
194+
// all of the literal child blocks we must also include.
195+
extSchema := &hcl.BodySchema{
196+
Attributes: schema.Attributes,
197+
Blocks: make([]hcl.BlockHeaderSchema, len(schema.Blocks), len(schema.Blocks)+1),
198+
}
199+
copy(extSchema.Blocks, schema.Blocks)
200+
extSchema.Blocks = append(extSchema.Blocks, dynamicBlockHeaderSchema)
201+
202+
return extSchema
203+
}
204+
205+
// This is a more relaxed schema than what's in schema.go, since we
206+
// want to maximize the amount of functions we can find even if there
207+
// are erroneous blocks.
208+
var functionDetectionInnerSchema = &hcl.BodySchema{
209+
Attributes: []hcl.AttributeSchema{
210+
{
211+
Name: "for_each",
212+
Required: false,
213+
},
214+
{
215+
Name: "labels",
216+
Required: false,
217+
},
218+
{
219+
Name: "iterator",
220+
Required: false,
221+
},
222+
},
223+
Blocks: []hcl.BlockHeaderSchema{
224+
{
225+
Type: "content",
226+
},
227+
},
228+
}

ext/dynblock/functions_hcldec.go

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
// Copyright (c) HashiCorp, Inc.
2+
// SPDX-License-Identifier: MPL-2.0
3+
4+
package dynblock
5+
6+
import (
7+
"github.com/terramate-io/hcl/v2"
8+
"github.com/terramate-io/hcl/v2/hcldec"
9+
)
10+
11+
// This is duplicated from ext/dynblock/variables_hcldec.go and modified to suit functions
12+
13+
// FunctionsHCLDec is a wrapper around WalkFunctions that uses the given hcldec
14+
// specification to automatically drive the recursive walk through nested
15+
// blocks in the given body.
16+
//
17+
// This is a drop-in replacement for hcldec.Functions which is able to treat
18+
// blocks of type "dynamic" in the same special way that dynblock.Expand would,
19+
// exposing both the functions referenced in the "for_each" and "labels"
20+
// arguments and functions used in the nested "content" block.
21+
func FunctionsHCLDec(body hcl.Body, spec hcldec.Spec) []hcl.Traversal {
22+
rootNode := WalkFunctions(body)
23+
return walkFunctionsWithHCLDec(rootNode, spec)
24+
}
25+
26+
// ExpandFunctionsHCLDec is like FunctionsHCLDec but it includes only the
27+
// minimal set of functions required to call Expand, ignoring functions that
28+
// are referenced only inside normal block contents. See WalkExpandFunctions
29+
// for more information.
30+
func ExpandFunctionsHCLDec(body hcl.Body, spec hcldec.Spec) []hcl.Traversal {
31+
rootNode := WalkExpandFunctions(body)
32+
return walkFunctionsWithHCLDec(rootNode, spec)
33+
}
34+
35+
func walkFunctionsWithHCLDec(node WalkFunctionsNode, spec hcldec.Spec) []hcl.Traversal {
36+
vars, children := node.Visit(hcldec.ImpliedSchema(spec))
37+
38+
if len(children) > 0 {
39+
childSpecs := hcldec.ChildBlockTypes(spec)
40+
for _, child := range children {
41+
if childSpec, exists := childSpecs[child.BlockTypeName]; exists {
42+
vars = append(vars, walkFunctionsWithHCLDec(child.Node, childSpec)...)
43+
}
44+
}
45+
}
46+
47+
return vars
48+
}

0 commit comments

Comments
 (0)