]> git.immae.eu Git - github/fretlink/terraform-provider-statuscake.git/blob - vendor/github.com/hashicorp/terraform/terraform/variables.go
Upgrade to 0.12
[github/fretlink/terraform-provider-statuscake.git] / vendor / github.com / hashicorp / terraform / terraform / variables.go
1 package terraform
2
3 import (
4 "fmt"
5
6 "github.com/hashicorp/hcl2/hcl"
7 "github.com/zclconf/go-cty/cty"
8 "github.com/zclconf/go-cty/cty/convert"
9
10 "github.com/hashicorp/terraform/configs"
11 "github.com/hashicorp/terraform/tfdiags"
12 )
13
14 // InputValue represents a value for a variable in the root module, provided
15 // as part of the definition of an operation.
16 type InputValue struct {
17 Value cty.Value
18 SourceType ValueSourceType
19
20 // SourceRange provides source location information for values whose
21 // SourceType is either ValueFromConfig or ValueFromFile. It is not
22 // populated for other source types, and so should not be used.
23 SourceRange tfdiags.SourceRange
24 }
25
26 // ValueSourceType describes what broad category of source location provided
27 // a particular value.
28 type ValueSourceType rune
29
30 const (
31 // ValueFromUnknown is the zero value of ValueSourceType and is not valid.
32 ValueFromUnknown ValueSourceType = 0
33
34 // ValueFromConfig indicates that a value came from a .tf or .tf.json file,
35 // e.g. the default value defined for a variable.
36 ValueFromConfig ValueSourceType = 'C'
37
38 // ValueFromAutoFile indicates that a value came from a "values file", like
39 // a .tfvars file, that was implicitly loaded by naming convention.
40 ValueFromAutoFile ValueSourceType = 'F'
41
42 // ValueFromNamedFile indicates that a value came from a named "values file",
43 // like a .tfvars file, that was passed explicitly on the command line (e.g.
44 // -var-file=foo.tfvars).
45 ValueFromNamedFile ValueSourceType = 'N'
46
47 // ValueFromCLIArg indicates that the value was provided directly in
48 // a CLI argument. The name of this argument is not recorded and so it must
49 // be inferred from context.
50 ValueFromCLIArg ValueSourceType = 'A'
51
52 // ValueFromEnvVar indicates that the value was provided via an environment
53 // variable. The name of the variable is not recorded and so it must be
54 // inferred from context.
55 ValueFromEnvVar ValueSourceType = 'E'
56
57 // ValueFromInput indicates that the value was provided at an interactive
58 // input prompt.
59 ValueFromInput ValueSourceType = 'I'
60
61 // ValueFromPlan indicates that the value was retrieved from a stored plan.
62 ValueFromPlan ValueSourceType = 'P'
63
64 // ValueFromCaller indicates that the value was explicitly overridden by
65 // a caller to Context.SetVariable after the context was constructed.
66 ValueFromCaller ValueSourceType = 'S'
67 )
68
69 func (v *InputValue) GoString() string {
70 if (v.SourceRange != tfdiags.SourceRange{}) {
71 return fmt.Sprintf("&terraform.InputValue{Value: %#v, SourceType: %#v, SourceRange: %#v}", v.Value, v.SourceType, v.SourceRange)
72 } else {
73 return fmt.Sprintf("&terraform.InputValue{Value: %#v, SourceType: %#v}", v.Value, v.SourceType)
74 }
75 }
76
77 func (v ValueSourceType) GoString() string {
78 return fmt.Sprintf("terraform.%s", v)
79 }
80
81 //go:generate stringer -type ValueSourceType
82
83 // InputValues is a map of InputValue instances.
84 type InputValues map[string]*InputValue
85
86 // InputValuesFromCaller turns the given map of naked values into an
87 // InputValues that attributes each value to "a caller", using the source
88 // type ValueFromCaller. This is primarily useful for testing purposes.
89 //
90 // This should not be used as a general way to convert map[string]cty.Value
91 // into InputValues, since in most real cases we want to set a suitable
92 // other SourceType and possibly SourceRange value.
93 func InputValuesFromCaller(vals map[string]cty.Value) InputValues {
94 ret := make(InputValues, len(vals))
95 for k, v := range vals {
96 ret[k] = &InputValue{
97 Value: v,
98 SourceType: ValueFromCaller,
99 }
100 }
101 return ret
102 }
103
104 // Override merges the given value maps with the receiver, overriding any
105 // conflicting keys so that the latest definition wins.
106 func (vv InputValues) Override(others ...InputValues) InputValues {
107 // FIXME: This should check to see if any of the values are maps and
108 // merge them if so, in order to preserve the behavior from prior to
109 // Terraform 0.12.
110 ret := make(InputValues)
111 for k, v := range vv {
112 ret[k] = v
113 }
114 for _, other := range others {
115 for k, v := range other {
116 ret[k] = v
117 }
118 }
119 return ret
120 }
121
122 // JustValues returns a map that just includes the values, discarding the
123 // source information.
124 func (vv InputValues) JustValues() map[string]cty.Value {
125 ret := make(map[string]cty.Value, len(vv))
126 for k, v := range vv {
127 ret[k] = v.Value
128 }
129 return ret
130 }
131
132 // DefaultVariableValues returns an InputValues map representing the default
133 // values specified for variables in the given configuration map.
134 func DefaultVariableValues(configs map[string]*configs.Variable) InputValues {
135 ret := make(InputValues)
136 for k, c := range configs {
137 if c.Default == cty.NilVal {
138 continue
139 }
140 ret[k] = &InputValue{
141 Value: c.Default,
142 SourceType: ValueFromConfig,
143 SourceRange: tfdiags.SourceRangeFromHCL(c.DeclRange),
144 }
145 }
146 return ret
147 }
148
149 // SameValues returns true if the given InputValues has the same values as
150 // the receiever, disregarding the source types and source ranges.
151 //
152 // Values are compared using the cty "RawEquals" method, which means that
153 // unknown values can be considered equal to one another if they are of the
154 // same type.
155 func (vv InputValues) SameValues(other InputValues) bool {
156 if len(vv) != len(other) {
157 return false
158 }
159
160 for k, v := range vv {
161 ov, exists := other[k]
162 if !exists {
163 return false
164 }
165 if !v.Value.RawEquals(ov.Value) {
166 return false
167 }
168 }
169
170 return true
171 }
172
173 // HasValues returns true if the reciever has the same values as in the given
174 // map, disregarding the source types and source ranges.
175 //
176 // Values are compared using the cty "RawEquals" method, which means that
177 // unknown values can be considered equal to one another if they are of the
178 // same type.
179 func (vv InputValues) HasValues(vals map[string]cty.Value) bool {
180 if len(vv) != len(vals) {
181 return false
182 }
183
184 for k, v := range vv {
185 oVal, exists := vals[k]
186 if !exists {
187 return false
188 }
189 if !v.Value.RawEquals(oVal) {
190 return false
191 }
192 }
193
194 return true
195 }
196
197 // Identical returns true if the given InputValues has the same values,
198 // source types, and source ranges as the receiver.
199 //
200 // Values are compared using the cty "RawEquals" method, which means that
201 // unknown values can be considered equal to one another if they are of the
202 // same type.
203 //
204 // This method is primarily for testing. For most practical purposes, it's
205 // better to use SameValues or HasValues.
206 func (vv InputValues) Identical(other InputValues) bool {
207 if len(vv) != len(other) {
208 return false
209 }
210
211 for k, v := range vv {
212 ov, exists := other[k]
213 if !exists {
214 return false
215 }
216 if !v.Value.RawEquals(ov.Value) {
217 return false
218 }
219 if v.SourceType != ov.SourceType {
220 return false
221 }
222 if v.SourceRange != ov.SourceRange {
223 return false
224 }
225 }
226
227 return true
228 }
229
230 // checkInputVariables ensures that variable values supplied at the UI conform
231 // to their corresponding declarations in configuration.
232 //
233 // The set of values is considered valid only if the returned diagnostics
234 // does not contain errors. A valid set of values may still produce warnings,
235 // which should be returned to the user.
236 func checkInputVariables(vcs map[string]*configs.Variable, vs InputValues) tfdiags.Diagnostics {
237 var diags tfdiags.Diagnostics
238
239 for name, vc := range vcs {
240 val, isSet := vs[name]
241 if !isSet {
242 // Always an error, since the caller should already have included
243 // default values from the configuration in the values map.
244 diags = diags.Append(tfdiags.Sourceless(
245 tfdiags.Error,
246 "Unassigned variable",
247 fmt.Sprintf("The input variable %q has not been assigned a value. This is a bug in Terraform; please report it in a GitHub issue.", name),
248 ))
249 continue
250 }
251
252 wantType := vc.Type
253
254 // A given value is valid if it can convert to the desired type.
255 _, err := convert.Convert(val.Value, wantType)
256 if err != nil {
257 switch val.SourceType {
258 case ValueFromConfig, ValueFromAutoFile, ValueFromNamedFile:
259 // We have source location information for these.
260 diags = diags.Append(&hcl.Diagnostic{
261 Severity: hcl.DiagError,
262 Summary: "Invalid value for input variable",
263 Detail: fmt.Sprintf("The given value is not valid for variable %q: %s.", name, err),
264 Subject: val.SourceRange.ToHCL().Ptr(),
265 })
266 case ValueFromEnvVar:
267 diags = diags.Append(tfdiags.Sourceless(
268 tfdiags.Error,
269 "Invalid value for input variable",
270 fmt.Sprintf("The environment variable TF_VAR_%s does not contain a valid value for variable %q: %s.", name, name, err),
271 ))
272 case ValueFromCLIArg:
273 diags = diags.Append(tfdiags.Sourceless(
274 tfdiags.Error,
275 "Invalid value for input variable",
276 fmt.Sprintf("The argument -var=\"%s=...\" does not contain a valid value for variable %q: %s.", name, name, err),
277 ))
278 case ValueFromInput:
279 diags = diags.Append(tfdiags.Sourceless(
280 tfdiags.Error,
281 "Invalid value for input variable",
282 fmt.Sprintf("The value entered for variable %q is not valid: %s.", name, err),
283 ))
284 default:
285 // The above gets us good coverage for the situations users
286 // are likely to encounter with their own inputs. The other
287 // cases are generally implementation bugs, so we'll just
288 // use a generic error for these.
289 diags = diags.Append(tfdiags.Sourceless(
290 tfdiags.Error,
291 "Invalid value for input variable",
292 fmt.Sprintf("The value provided for variable %q is not valid: %s.", name, err),
293 ))
294 }
295 }
296 }
297
298 // Check for any variables that are assigned without being configured.
299 // This is always an implementation error in the caller, because we
300 // expect undefined variables to be caught during context construction
301 // where there is better context to report it well.
302 for name := range vs {
303 if _, defined := vcs[name]; !defined {
304 diags = diags.Append(tfdiags.Sourceless(
305 tfdiags.Error,
306 "Value assigned to undeclared variable",
307 fmt.Sprintf("A value was assigned to an undeclared input variable %q.", name),
308 ))
309 }
310 }
311
312 return diags
313 }