diff options
Diffstat (limited to 'vendor/github.com/hashicorp/hcl2/ext/dynblock/variables.go')
-rw-r--r-- | vendor/github.com/hashicorp/hcl2/ext/dynblock/variables.go | 209 |
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 @@ | |||
1 | package dynblock | ||
2 | |||
3 | import ( | ||
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. | ||
19 | func 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. | ||
32 | func WalkExpandVariables(body hcl.Body) WalkVariablesNode { | ||
33 | return WalkVariablesNode{ | ||
34 | body: body, | ||
35 | } | ||
36 | } | ||
37 | |||
38 | type WalkVariablesNode struct { | ||
39 | body hcl.Body | ||
40 | it *iteration | ||
41 | |||
42 | includeContent bool | ||
43 | } | ||
44 | |||
45 | type 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. | ||
58 | func (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. | ||
70 | func (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 | |||
172 | func (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. | ||
189 | var 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 | } | ||