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