aboutsummaryrefslogtreecommitdiffhomepage
path: root/vendor/github.com/hashicorp/hcl2/ext/dynblock/README.md
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/hashicorp/hcl2/ext/dynblock/README.md')
-rw-r--r--vendor/github.com/hashicorp/hcl2/ext/dynblock/README.md184
1 files changed, 184 insertions, 0 deletions
diff --git a/vendor/github.com/hashicorp/hcl2/ext/dynblock/README.md b/vendor/github.com/hashicorp/hcl2/ext/dynblock/README.md
new file mode 100644
index 0000000..2b24fdb
--- /dev/null
+++ b/vendor/github.com/hashicorp/hcl2/ext/dynblock/README.md
@@ -0,0 +1,184 @@
1# HCL Dynamic Blocks Extension
2
3This HCL extension implements a special block type named "dynamic" that can
4be used to dynamically generate blocks of other types by iterating over
5collection values.
6
7Normally the block structure in an HCL configuration file is rigid, even
8though dynamic expressions can be used within attribute values. This is
9convenient for most applications since it allows the overall structure of
10the document to be decoded easily, but in some applications it is desirable
11to allow dynamic block generation within certain portions of the configuration.
12
13Dynamic block generation is performed using the `dynamic` block type:
14
15```hcl
16toplevel {
17 nested {
18 foo = "static block 1"
19 }
20
21 dynamic "nested" {
22 for_each = ["a", "b", "c"]
23 iterator = nested
24 content {
25 foo = "dynamic block ${nested.value}"
26 }
27 }
28
29 nested {
30 foo = "static block 2"
31 }
32}
33```
34
35The above is interpreted as if it were written as follows:
36
37```hcl
38toplevel {
39 nested {
40 foo = "static block 1"
41 }
42
43 nested {
44 foo = "dynamic block a"
45 }
46
47 nested {
48 foo = "dynamic block b"
49 }
50
51 nested {
52 foo = "dynamic block c"
53 }
54
55 nested {
56 foo = "static block 2"
57 }
58}
59```
60
61Since HCL block syntax is not normally exposed to the possibility of unknown
62values, this extension must make some compromises when asked to iterate over
63an unknown collection. If the length of the collection cannot be statically
64recognized (because it is an unknown value of list, map, or set type) then
65the `dynamic` construct will generate a _single_ dynamic block whose iterator
66key and value are both unknown values of the dynamic pseudo-type, thus causing
67any attribute values derived from iteration to appear as unknown values. There
68is no explicit representation of the fact that the length of the collection may
69eventually be different than one.
70
71## Usage
72
73Pass a body to function `Expand` to obtain a new body that will, on access
74to its content, evaluate and expand any nested `dynamic` blocks.
75Dynamic block processing is also automatically propagated into any nested
76blocks that are returned, allowing users to nest dynamic blocks inside
77one another and to nest dynamic blocks inside other static blocks.
78
79HCL structural decoding does not normally have access to an `EvalContext`, so
80any variables and functions that should be available to the `for_each`
81and `labels` expressions must be passed in when calling `Expand`. Expressions
82within the `content` block are evaluated separately and so can be passed a
83separate `EvalContext` if desired, during normal attribute expression
84evaluation.
85
86## Detecting Variables
87
88Some applications dynamically generate an `EvalContext` by analyzing which
89variables are referenced by an expression before evaluating it.
90
91This unfortunately requires some extra effort when this analysis is required
92for the context passed to `Expand`: the HCL API requires a schema to be
93provided in order to do any analysis of the blocks in a body, but the low-level
94schema model provides a description of only one level of nested blocks at
95a time, and thus a new schema must be provided for each additional level of
96nesting.
97
98To make this arduous process as convenient as possbile, this package provides
99a helper function `WalkForEachVariables`, which returns a `WalkVariablesNode`
100instance that can be used to find variables directly in a given body and also
101determine which nested blocks require recursive calls. Using this mechanism
102requires that the caller be able to look up a schema given a nested block type.
103For _simple_ formats where a specific block type name always has the same schema
104regardless of context, a walk can be implemented as follows:
105
106```go
107func walkVariables(node dynblock.WalkVariablesNode, schema *hcl.BodySchema) []hcl.Traversal {
108 vars, children := node.Visit(schema)
109
110 for _, child := range children {
111 var childSchema *hcl.BodySchema
112 switch child.BlockTypeName {
113 case "a":
114 childSchema = &hcl.BodySchema{
115 Blocks: []hcl.BlockHeaderSchema{
116 {
117 Type: "b",
118 LabelNames: []string{"key"},
119 },
120 },
121 }
122 case "b":
123 childSchema = &hcl.BodySchema{
124 Attributes: []hcl.AttributeSchema{
125 {
126 Name: "val",
127 Required: true,
128 },
129 },
130 }
131 default:
132 // Should never happen, because the above cases should be exhaustive
133 // for the application's configuration format.
134 panic(fmt.Errorf("can't find schema for unknown block type %q", child.BlockTypeName))
135 }
136
137 vars = append(vars, testWalkAndAccumVars(child.Node, childSchema)...)
138 }
139}
140```
141
142### Detecting Variables with `hcldec` Specifications
143
144For applications that use the higher-level `hcldec` package to decode nested
145configuration structures into `cty` values, the same specification can be used
146to automatically drive the recursive variable-detection walk described above.
147
148The helper function `ForEachVariablesHCLDec` allows an entire recursive
149configuration structure to be analyzed in a single call given a `hcldec.Spec`
150that describes the nested block structure. This means a `hcldec`-based
151application can support dynamic blocks with only a little additional effort:
152
153```go
154func decodeBody(body hcl.Body, spec hcldec.Spec) (cty.Value, hcl.Diagnostics) {
155 // Determine which variables are needed to expand dynamic blocks
156 neededForDynamic := dynblock.ForEachVariablesHCLDec(body, spec)
157
158 // Build a suitable EvalContext and expand dynamic blocks
159 dynCtx := buildEvalContext(neededForDynamic)
160 dynBody := dynblock.Expand(body, dynCtx)
161
162 // Determine which variables are needed to fully decode the expanded body
163 // This will analyze expressions that came both from static blocks in the
164 // original body and from blocks that were dynamically added by Expand.
165 neededForDecode := hcldec.Variables(dynBody, spec)
166
167 // Build a suitable EvalContext and then fully decode the body as per the
168 // hcldec specification.
169 decCtx := buildEvalContext(neededForDecode)
170 return hcldec.Decode(dynBody, spec, decCtx)
171}
172
173func buildEvalContext(needed []hcl.Traversal) *hcl.EvalContext {
174 // (to be implemented by your application)
175}
176```
177
178# Performance
179
180This extension is going quite harshly against the grain of the HCL API, and
181so it uses lots of wrapping objects and temporary data structures to get its
182work done. HCL in general is not suitable for use in high-performance situations
183or situations sensitive to memory pressure, but that is _especially_ true for
184this extension.