]> git.immae.eu Git - github/fretlink/terraform-provider-statuscake.git/blame - 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
CommitLineData
bae9f6d2
JC
1package terraform
2
3import (
4 "fmt"
bae9f6d2 5
107c1cdb
ND
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.
16type 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.
28type ValueSourceType rune
29
30const (
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'
bae9f6d2
JC
67)
68
107c1cdb
ND
69func (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
77func (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.
84type 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.
bae9f6d2 89//
107c1cdb
ND
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.
93func 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,
bae9f6d2 99 }
107c1cdb
ND
100 }
101 return ret
102}
bae9f6d2 103
107c1cdb
ND
104// Override merges the given value maps with the receiver, overriding any
105// conflicting keys so that the latest definition wins.
106func (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
bae9f6d2 117 }
107c1cdb
ND
118 }
119 return ret
120}
bae9f6d2 121
107c1cdb
ND
122// JustValues returns a map that just includes the values, discarding the
123// source information.
124func (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
bae9f6d2 128 }
107c1cdb
ND
129 return ret
130}
bae9f6d2 131
107c1cdb
ND
132// DefaultVariableValues returns an InputValues map representing the default
133// values specified for variables in the given configuration map.
134func DefaultVariableValues(configs map[string]*configs.Variable) InputValues {
135 ret := make(InputValues)
136 for k, c := range configs {
137 if c.Default == cty.NilVal {
bae9f6d2
JC
138 continue
139 }
107c1cdb
ND
140 ret[k] = &InputValue{
141 Value: c.Default,
142 SourceType: ValueFromConfig,
143 SourceRange: tfdiags.SourceRangeFromHCL(c.DeclRange),
144 }
145 }
146 return ret
147}
bae9f6d2 148
107c1cdb
ND
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.
155func (vv InputValues) SameValues(other InputValues) bool {
156 if len(vv) != len(other) {
157 return false
158 }
bae9f6d2 159
107c1cdb
ND
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
bae9f6d2
JC
167 }
168 }
169
107c1cdb
ND
170 return true
171}
bae9f6d2 172
107c1cdb
ND
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.
179func (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
bae9f6d2
JC
191 }
192 }
193
107c1cdb 194 return true
bae9f6d2
JC
195}
196
107c1cdb
ND
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.
206func (vv InputValues) Identical(other InputValues) bool {
207 if len(vv) != len(other) {
208 return false
bae9f6d2
JC
209 }
210
107c1cdb
ND
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 }
bae9f6d2
JC
225 }
226
107c1cdb
ND
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.
236func 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
bae9f6d2 250 }
107c1cdb
ND
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 }
bae9f6d2 295 }
bae9f6d2 296 }
107c1cdb
ND
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
bae9f6d2 313}