]> git.immae.eu Git - github/fretlink/terraform-provider-statuscake.git/blob - vendor/github.com/hashicorp/terraform/terraform/resource.go
Upgrade to 0.12
[github/fretlink/terraform-provider-statuscake.git] / vendor / github.com / hashicorp / terraform / terraform / resource.go
1 package terraform
2
3 import (
4 "fmt"
5 "reflect"
6 "sort"
7 "strconv"
8 "strings"
9
10 "github.com/mitchellh/copystructure"
11 "github.com/mitchellh/reflectwalk"
12 "github.com/zclconf/go-cty/cty"
13
14 "github.com/hashicorp/terraform/addrs"
15 "github.com/hashicorp/terraform/config"
16 "github.com/hashicorp/terraform/config/hcl2shim"
17 "github.com/hashicorp/terraform/configs/configschema"
18 )
19
20 // ResourceProvisionerConfig is used to pair a provisioner
21 // with its provided configuration. This allows us to use singleton
22 // instances of each ResourceProvisioner and to keep the relevant
23 // configuration instead of instantiating a new Provisioner for each
24 // resource.
25 type ResourceProvisionerConfig struct {
26 Type string
27 Provisioner ResourceProvisioner
28 Config *ResourceConfig
29 RawConfig *config.RawConfig
30 ConnInfo *config.RawConfig
31 }
32
33 // Resource is a legacy way to identify a particular resource instance.
34 //
35 // New code should use addrs.ResourceInstance instead. This is still here
36 // only for codepaths that haven't been updated yet.
37 type Resource struct {
38 // These are all used by the new EvalNode stuff.
39 Name string
40 Type string
41 CountIndex int
42
43 // These aren't really used anymore anywhere, but we keep them around
44 // since we haven't done a proper cleanup yet.
45 Id string
46 Info *InstanceInfo
47 Config *ResourceConfig
48 Dependencies []string
49 Diff *InstanceDiff
50 Provider ResourceProvider
51 State *InstanceState
52 Provisioners []*ResourceProvisionerConfig
53 Flags ResourceFlag
54 }
55
56 // NewResource constructs a legacy Resource object from an
57 // addrs.ResourceInstance value.
58 //
59 // This is provided to shim to old codepaths that haven't been updated away
60 // from this type yet. Since this old type is not able to represent instances
61 // that have string keys, this function will panic if given a resource address
62 // that has a string key.
63 func NewResource(addr addrs.ResourceInstance) *Resource {
64 ret := &Resource{
65 Name: addr.Resource.Name,
66 Type: addr.Resource.Type,
67 }
68
69 if addr.Key != addrs.NoKey {
70 switch tk := addr.Key.(type) {
71 case addrs.IntKey:
72 ret.CountIndex = int(tk)
73 default:
74 panic(fmt.Errorf("resource instance with key %#v is not supported", addr.Key))
75 }
76 }
77
78 return ret
79 }
80
81 // ResourceKind specifies what kind of instance we're working with, whether
82 // its a primary instance, a tainted instance, or an orphan.
83 type ResourceFlag byte
84
85 // InstanceInfo is used to hold information about the instance and/or
86 // resource being modified.
87 type InstanceInfo struct {
88 // Id is a unique name to represent this instance. This is not related
89 // to InstanceState.ID in any way.
90 Id string
91
92 // ModulePath is the complete path of the module containing this
93 // instance.
94 ModulePath []string
95
96 // Type is the resource type of this instance
97 Type string
98
99 // uniqueExtra is an internal field that can be populated to supply
100 // extra metadata that is used to identify a unique instance in
101 // the graph walk. This will be appended to HumanID when uniqueId
102 // is called.
103 uniqueExtra string
104 }
105
106 // NewInstanceInfo constructs an InstanceInfo from an addrs.AbsResourceInstance.
107 //
108 // InstanceInfo is a legacy type, and uses of it should be gradually replaced
109 // by direct use of addrs.AbsResource or addrs.AbsResourceInstance as
110 // appropriate.
111 //
112 // The legacy InstanceInfo type cannot represent module instances with instance
113 // keys, so this function will panic if given such a path. Uses of this type
114 // should all be removed or replaced before implementing "count" and "for_each"
115 // arguments on modules in order to avoid such panics.
116 //
117 // This legacy type also cannot represent resource instances with string
118 // instance keys. It will panic if the given key is not either NoKey or an
119 // IntKey.
120 func NewInstanceInfo(addr addrs.AbsResourceInstance) *InstanceInfo {
121 // We need an old-style []string module path for InstanceInfo.
122 path := make([]string, len(addr.Module))
123 for i, step := range addr.Module {
124 if step.InstanceKey != addrs.NoKey {
125 panic("NewInstanceInfo cannot convert module instance with key")
126 }
127 path[i] = step.Name
128 }
129
130 // This is a funny old meaning of "id" that is no longer current. It should
131 // not be used for anything users might see. Note that it does not include
132 // a representation of the resource mode, and so it's impossible to
133 // determine from an InstanceInfo alone whether it is a managed or data
134 // resource that is being referred to.
135 id := fmt.Sprintf("%s.%s", addr.Resource.Resource.Type, addr.Resource.Resource.Name)
136 if addr.Resource.Resource.Mode == addrs.DataResourceMode {
137 id = "data." + id
138 }
139 if addr.Resource.Key != addrs.NoKey {
140 switch k := addr.Resource.Key.(type) {
141 case addrs.IntKey:
142 id = id + fmt.Sprintf(".%d", int(k))
143 default:
144 panic(fmt.Sprintf("NewInstanceInfo cannot convert resource instance with %T instance key", addr.Resource.Key))
145 }
146 }
147
148 return &InstanceInfo{
149 Id: id,
150 ModulePath: path,
151 Type: addr.Resource.Resource.Type,
152 }
153 }
154
155 // ResourceAddress returns the address of the resource that the receiver is describing.
156 func (i *InstanceInfo) ResourceAddress() *ResourceAddress {
157 // GROSS: for tainted and deposed instances, their status gets appended
158 // to i.Id to create a unique id for the graph node. Historically these
159 // ids were displayed to the user, so it's designed to be human-readable:
160 // "aws_instance.bar.0 (deposed #0)"
161 //
162 // So here we detect such suffixes and try to interpret them back to
163 // their original meaning so we can then produce a ResourceAddress
164 // with a suitable InstanceType.
165 id := i.Id
166 instanceType := TypeInvalid
167 if idx := strings.Index(id, " ("); idx != -1 {
168 remain := id[idx:]
169 id = id[:idx]
170
171 switch {
172 case strings.Contains(remain, "tainted"):
173 instanceType = TypeTainted
174 case strings.Contains(remain, "deposed"):
175 instanceType = TypeDeposed
176 }
177 }
178
179 addr, err := parseResourceAddressInternal(id)
180 if err != nil {
181 // should never happen, since that would indicate a bug in the
182 // code that constructed this InstanceInfo.
183 panic(fmt.Errorf("InstanceInfo has invalid Id %s", id))
184 }
185 if len(i.ModulePath) > 1 {
186 addr.Path = i.ModulePath[1:] // trim off "root" prefix, which is implied
187 }
188 if instanceType != TypeInvalid {
189 addr.InstanceTypeSet = true
190 addr.InstanceType = instanceType
191 }
192 return addr
193 }
194
195 // ResourceConfig is a legacy type that was formerly used to represent
196 // interpolatable configuration blocks. It is now only used to shim to old
197 // APIs that still use this type, via NewResourceConfigShimmed.
198 type ResourceConfig struct {
199 ComputedKeys []string
200 Raw map[string]interface{}
201 Config map[string]interface{}
202
203 raw *config.RawConfig
204 }
205
206 // NewResourceConfig creates a new ResourceConfig from a config.RawConfig.
207 func NewResourceConfig(c *config.RawConfig) *ResourceConfig {
208 result := &ResourceConfig{raw: c}
209 result.interpolateForce()
210 return result
211 }
212
213 // NewResourceConfigShimmed wraps a cty.Value of object type in a legacy
214 // ResourceConfig object, so that it can be passed to older APIs that expect
215 // this wrapping.
216 //
217 // The returned ResourceConfig is already interpolated and cannot be
218 // re-interpolated. It is, therefore, useful only to functions that expect
219 // an already-populated ResourceConfig which they then treat as read-only.
220 //
221 // If the given value is not of an object type that conforms to the given
222 // schema then this function will panic.
223 func NewResourceConfigShimmed(val cty.Value, schema *configschema.Block) *ResourceConfig {
224 if !val.Type().IsObjectType() {
225 panic(fmt.Errorf("NewResourceConfigShimmed given %#v; an object type is required", val.Type()))
226 }
227 ret := &ResourceConfig{}
228
229 legacyVal := hcl2shim.ConfigValueFromHCL2Block(val, schema)
230 if legacyVal != nil {
231 ret.Config = legacyVal
232
233 // Now we need to walk through our structure and find any unknown values,
234 // producing the separate list ComputedKeys to represent these. We use the
235 // schema here so that we can preserve the expected invariant
236 // that an attribute is always either wholly known or wholly unknown, while
237 // a child block can be partially unknown.
238 ret.ComputedKeys = newResourceConfigShimmedComputedKeys(val, "")
239 } else {
240 ret.Config = make(map[string]interface{})
241 }
242 ret.Raw = ret.Config
243
244 return ret
245 }
246
247 // Record the any config values in ComputedKeys. This field had been unused in
248 // helper/schema, but in the new protocol we're using this so that the SDK can
249 // now handle having an unknown collection. The legacy diff code doesn't
250 // properly handle the unknown, because it can't be expressed in the same way
251 // between the config and diff.
252 func newResourceConfigShimmedComputedKeys(val cty.Value, path string) []string {
253 var ret []string
254 ty := val.Type()
255
256 if val.IsNull() {
257 return ret
258 }
259
260 if !val.IsKnown() {
261 // we shouldn't have an entirely unknown resource, but prevent empty
262 // strings just in case
263 if len(path) > 0 {
264 ret = append(ret, path)
265 }
266 return ret
267 }
268
269 if path != "" {
270 path += "."
271 }
272 switch {
273 case ty.IsListType(), ty.IsTupleType(), ty.IsSetType():
274 i := 0
275 for it := val.ElementIterator(); it.Next(); i++ {
276 _, subVal := it.Element()
277 keys := newResourceConfigShimmedComputedKeys(subVal, fmt.Sprintf("%s%d", path, i))
278 ret = append(ret, keys...)
279 }
280
281 case ty.IsMapType(), ty.IsObjectType():
282 for it := val.ElementIterator(); it.Next(); {
283 subK, subVal := it.Element()
284 keys := newResourceConfigShimmedComputedKeys(subVal, fmt.Sprintf("%s%s", path, subK.AsString()))
285 ret = append(ret, keys...)
286 }
287 }
288
289 return ret
290 }
291
292 // DeepCopy performs a deep copy of the configuration. This makes it safe
293 // to modify any of the structures that are part of the resource config without
294 // affecting the original configuration.
295 func (c *ResourceConfig) DeepCopy() *ResourceConfig {
296 // DeepCopying a nil should return a nil to avoid panics
297 if c == nil {
298 return nil
299 }
300
301 // Copy, this will copy all the exported attributes
302 copy, err := copystructure.Config{Lock: true}.Copy(c)
303 if err != nil {
304 panic(err)
305 }
306
307 // Force the type
308 result := copy.(*ResourceConfig)
309
310 // For the raw configuration, we can just use its own copy method
311 result.raw = c.raw.Copy()
312
313 return result
314 }
315
316 // Equal checks the equality of two resource configs.
317 func (c *ResourceConfig) Equal(c2 *ResourceConfig) bool {
318 // If either are nil, then they're only equal if they're both nil
319 if c == nil || c2 == nil {
320 return c == c2
321 }
322
323 // Sort the computed keys so they're deterministic
324 sort.Strings(c.ComputedKeys)
325 sort.Strings(c2.ComputedKeys)
326
327 // Two resource configs if their exported properties are equal.
328 // We don't compare "raw" because it is never used again after
329 // initialization and for all intents and purposes they are equal
330 // if the exported properties are equal.
331 check := [][2]interface{}{
332 {c.ComputedKeys, c2.ComputedKeys},
333 {c.Raw, c2.Raw},
334 {c.Config, c2.Config},
335 }
336 for _, pair := range check {
337 if !reflect.DeepEqual(pair[0], pair[1]) {
338 return false
339 }
340 }
341
342 return true
343 }
344
345 // CheckSet checks that the given list of configuration keys is
346 // properly set. If not, errors are returned for each unset key.
347 //
348 // This is useful to be called in the Validate method of a ResourceProvider.
349 func (c *ResourceConfig) CheckSet(keys []string) []error {
350 var errs []error
351
352 for _, k := range keys {
353 if !c.IsSet(k) {
354 errs = append(errs, fmt.Errorf("%s must be set", k))
355 }
356 }
357
358 return errs
359 }
360
361 // Get looks up a configuration value by key and returns the value.
362 //
363 // The second return value is true if the get was successful. Get will
364 // return the raw value if the key is computed, so you should pair this
365 // with IsComputed.
366 func (c *ResourceConfig) Get(k string) (interface{}, bool) {
367 // We aim to get a value from the configuration. If it is computed,
368 // then we return the pure raw value.
369 source := c.Config
370 if c.IsComputed(k) {
371 source = c.Raw
372 }
373
374 return c.get(k, source)
375 }
376
377 // GetRaw looks up a configuration value by key and returns the value,
378 // from the raw, uninterpolated config.
379 //
380 // The second return value is true if the get was successful. Get will
381 // not succeed if the value is being computed.
382 func (c *ResourceConfig) GetRaw(k string) (interface{}, bool) {
383 return c.get(k, c.Raw)
384 }
385
386 // IsComputed returns whether the given key is computed or not.
387 func (c *ResourceConfig) IsComputed(k string) bool {
388 // The next thing we do is check the config if we get a computed
389 // value out of it.
390 v, ok := c.get(k, c.Config)
391 if !ok {
392 return false
393 }
394
395 // If value is nil, then it isn't computed
396 if v == nil {
397 return false
398 }
399
400 // Test if the value contains an unknown value
401 var w unknownCheckWalker
402 if err := reflectwalk.Walk(v, &w); err != nil {
403 panic(err)
404 }
405
406 return w.Unknown
407 }
408
409 // IsSet checks if the key in the configuration is set. A key is set if
410 // it has a value or the value is being computed (is unknown currently).
411 //
412 // This function should be used rather than checking the keys of the
413 // raw configuration itself, since a key may be omitted from the raw
414 // configuration if it is being computed.
415 func (c *ResourceConfig) IsSet(k string) bool {
416 if c == nil {
417 return false
418 }
419
420 if c.IsComputed(k) {
421 return true
422 }
423
424 if _, ok := c.Get(k); ok {
425 return true
426 }
427
428 return false
429 }
430
431 func (c *ResourceConfig) get(
432 k string, raw map[string]interface{}) (interface{}, bool) {
433 parts := strings.Split(k, ".")
434 if len(parts) == 1 && parts[0] == "" {
435 parts = nil
436 }
437
438 var current interface{} = raw
439 var previous interface{} = nil
440 for i, part := range parts {
441 if current == nil {
442 return nil, false
443 }
444
445 cv := reflect.ValueOf(current)
446 switch cv.Kind() {
447 case reflect.Map:
448 previous = current
449 v := cv.MapIndex(reflect.ValueOf(part))
450 if !v.IsValid() {
451 if i > 0 && i != (len(parts)-1) {
452 tryKey := strings.Join(parts[i:], ".")
453 v := cv.MapIndex(reflect.ValueOf(tryKey))
454 if !v.IsValid() {
455 return nil, false
456 }
457
458 return v.Interface(), true
459 }
460
461 return nil, false
462 }
463
464 current = v.Interface()
465 case reflect.Slice:
466 previous = current
467
468 if part == "#" {
469 // If any value in a list is computed, this whole thing
470 // is computed and we can't read any part of it.
471 for i := 0; i < cv.Len(); i++ {
472 if v := cv.Index(i).Interface(); v == unknownValue() {
473 return v, true
474 }
475 }
476
477 current = cv.Len()
478 } else {
479 i, err := strconv.ParseInt(part, 0, 0)
480 if err != nil {
481 return nil, false
482 }
483 if int(i) < 0 || int(i) >= cv.Len() {
484 return nil, false
485 }
486 current = cv.Index(int(i)).Interface()
487 }
488 case reflect.String:
489 // This happens when map keys contain "." and have a common
490 // prefix so were split as path components above.
491 actualKey := strings.Join(parts[i-1:], ".")
492 if prevMap, ok := previous.(map[string]interface{}); ok {
493 v, ok := prevMap[actualKey]
494 return v, ok
495 }
496
497 return nil, false
498 default:
499 panic(fmt.Sprintf("Unknown kind: %s", cv.Kind()))
500 }
501 }
502
503 return current, true
504 }
505
506 // interpolateForce is a temporary thing. We want to get rid of interpolate
507 // above and likewise this, but it can only be done after the f-ast-graph
508 // refactor is complete.
509 func (c *ResourceConfig) interpolateForce() {
510 if c.raw == nil {
511 // If we don't have a lowercase "raw" but we _do_ have the uppercase
512 // Raw populated then this indicates that we're recieving a shim
513 // ResourceConfig created by NewResourceConfigShimmed, which is already
514 // fully evaluated and thus this function doesn't need to do anything.
515 if c.Raw != nil {
516 return
517 }
518
519 var err error
520 c.raw, err = config.NewRawConfig(make(map[string]interface{}))
521 if err != nil {
522 panic(err)
523 }
524 }
525
526 c.ComputedKeys = c.raw.UnknownKeys()
527 c.Raw = c.raw.RawMap()
528 c.Config = c.raw.Config()
529 }
530
531 // unknownCheckWalker
532 type unknownCheckWalker struct {
533 Unknown bool
534 }
535
536 func (w *unknownCheckWalker) Primitive(v reflect.Value) error {
537 if v.Interface() == unknownValue() {
538 w.Unknown = true
539 }
540
541 return nil
542 }