]> git.immae.eu Git - github/fretlink/terraform-provider-statuscake.git/blob - vendor/github.com/hashicorp/hcl2/ext/dynblock/README.md
update vendor and go.mod
[github/fretlink/terraform-provider-statuscake.git] / vendor / github.com / hashicorp / hcl2 / ext / dynblock / README.md
1 # HCL Dynamic Blocks Extension
2
3 This HCL extension implements a special block type named "dynamic" that can
4 be used to dynamically generate blocks of other types by iterating over
5 collection values.
6
7 Normally the block structure in an HCL configuration file is rigid, even
8 though dynamic expressions can be used within attribute values. This is
9 convenient for most applications since it allows the overall structure of
10 the document to be decoded easily, but in some applications it is desirable
11 to allow dynamic block generation within certain portions of the configuration.
12
13 Dynamic block generation is performed using the `dynamic` block type:
14
15 ```hcl
16 toplevel {
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
35 The above is interpreted as if it were written as follows:
36
37 ```hcl
38 toplevel {
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
61 Since HCL block syntax is not normally exposed to the possibility of unknown
62 values, this extension must make some compromises when asked to iterate over
63 an unknown collection. If the length of the collection cannot be statically
64 recognized (because it is an unknown value of list, map, or set type) then
65 the `dynamic` construct will generate a _single_ dynamic block whose iterator
66 key and value are both unknown values of the dynamic pseudo-type, thus causing
67 any attribute values derived from iteration to appear as unknown values. There
68 is no explicit representation of the fact that the length of the collection may
69 eventually be different than one.
70
71 ## Usage
72
73 Pass a body to function `Expand` to obtain a new body that will, on access
74 to its content, evaluate and expand any nested `dynamic` blocks.
75 Dynamic block processing is also automatically propagated into any nested
76 blocks that are returned, allowing users to nest dynamic blocks inside
77 one another and to nest dynamic blocks inside other static blocks.
78
79 HCL structural decoding does not normally have access to an `EvalContext`, so
80 any variables and functions that should be available to the `for_each`
81 and `labels` expressions must be passed in when calling `Expand`. Expressions
82 within the `content` block are evaluated separately and so can be passed a
83 separate `EvalContext` if desired, during normal attribute expression
84 evaluation.
85
86 ## Detecting Variables
87
88 Some applications dynamically generate an `EvalContext` by analyzing which
89 variables are referenced by an expression before evaluating it.
90
91 This unfortunately requires some extra effort when this analysis is required
92 for the context passed to `Expand`: the HCL API requires a schema to be
93 provided in order to do any analysis of the blocks in a body, but the low-level
94 schema model provides a description of only one level of nested blocks at
95 a time, and thus a new schema must be provided for each additional level of
96 nesting.
97
98 To make this arduous process as convenient as possible, this package provides
99 a helper function `WalkForEachVariables`, which returns a `WalkVariablesNode`
100 instance that can be used to find variables directly in a given body and also
101 determine which nested blocks require recursive calls. Using this mechanism
102 requires that the caller be able to look up a schema given a nested block type.
103 For _simple_ formats where a specific block type name always has the same schema
104 regardless of context, a walk can be implemented as follows:
105
106 ```go
107 func 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
144 For applications that use the higher-level `hcldec` package to decode nested
145 configuration structures into `cty` values, the same specification can be used
146 to automatically drive the recursive variable-detection walk described above.
147
148 The helper function `ForEachVariablesHCLDec` allows an entire recursive
149 configuration structure to be analyzed in a single call given a `hcldec.Spec`
150 that describes the nested block structure. This means a `hcldec`-based
151 application can support dynamic blocks with only a little additional effort:
152
153 ```go
154 func 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
173 func buildEvalContext(needed []hcl.Traversal) *hcl.EvalContext {
174 // (to be implemented by your application)
175 }
176 ```
177
178 # Performance
179
180 This extension is going quite harshly against the grain of the HCL API, and
181 so it uses lots of wrapping objects and temporary data structures to get its
182 work done. HCL in general is not suitable for use in high-performance situations
183 or situations sensitive to memory pressure, but that is _especially_ true for
184 this extension.