1package terraform
2
3import (
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.
25type 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.
37type 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.
63func 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.
83type ResourceFlag byte
84
85// InstanceInfo is used to hold information about the instance and/or
86// resource being modified.
87type 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.
120func 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.
156func (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.
198type 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.
207func 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.
223func 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.
252func 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.
295func (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.
317func (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.
349func (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.
366func (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.
382func (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.
387func (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.
415func (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
431func (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.
509func (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
532type unknownCheckWalker struct {
533	Unknown bool
534}
535
536func (w *unknownCheckWalker) Primitive(v reflect.Value) error {
537	if v.Interface() == unknownValue() {
538		w.Unknown = true
539	}
540
541	return nil
542}
543