1package terraform
2
3import (
4	"bufio"
5	"bytes"
6	"fmt"
7	"log"
8	"reflect"
9	"regexp"
10	"sort"
11	"strconv"
12	"strings"
13	"sync"
14
15	"github.com/hashicorp/terraform/internal/addrs"
16	"github.com/hashicorp/terraform/internal/configs/configschema"
17	"github.com/hashicorp/terraform/internal/configs/hcl2shim"
18	"github.com/zclconf/go-cty/cty"
19
20	"github.com/mitchellh/copystructure"
21)
22
23// DiffChangeType is an enum with the kind of changes a diff has planned.
24type DiffChangeType byte
25
26const (
27	DiffInvalid DiffChangeType = iota
28	DiffNone
29	DiffCreate
30	DiffUpdate
31	DiffDestroy
32	DiffDestroyCreate
33
34	// DiffRefresh is only used in the UI for displaying diffs.
35	// Managed resource reads never appear in plan, and when data source
36	// reads appear they are represented as DiffCreate in core before
37	// transforming to DiffRefresh in the UI layer.
38	DiffRefresh // TODO: Actually use DiffRefresh in core too, for less confusion
39)
40
41// multiVal matches the index key to a flatmapped set, list or map
42var multiVal = regexp.MustCompile(`\.(#|%)$`)
43
44// Diff tracks the changes that are necessary to apply a configuration
45// to an existing infrastructure.
46type Diff struct {
47	// Modules contains all the modules that have a diff
48	Modules []*ModuleDiff
49}
50
51// Prune cleans out unused structures in the diff without affecting
52// the behavior of the diff at all.
53//
54// This is not safe to call concurrently. This is safe to call on a
55// nil Diff.
56func (d *Diff) Prune() {
57	if d == nil {
58		return
59	}
60
61	// Prune all empty modules
62	newModules := make([]*ModuleDiff, 0, len(d.Modules))
63	for _, m := range d.Modules {
64		// If the module isn't empty, we keep it
65		if !m.Empty() {
66			newModules = append(newModules, m)
67		}
68	}
69	if len(newModules) == 0 {
70		newModules = nil
71	}
72	d.Modules = newModules
73}
74
75// AddModule adds the module with the given path to the diff.
76//
77// This should be the preferred method to add module diffs since it
78// allows us to optimize lookups later as well as control sorting.
79func (d *Diff) AddModule(path addrs.ModuleInstance) *ModuleDiff {
80	// Lower the new-style address into a legacy-style address.
81	// This requires that none of the steps have instance keys, which is
82	// true for all addresses at the time of implementing this because
83	// "count" and "for_each" are not yet implemented for modules.
84	legacyPath := make([]string, len(path))
85	for i, step := range path {
86		if step.InstanceKey != addrs.NoKey {
87			// FIXME: Once the rest of Terraform is ready to use count and
88			// for_each, remove all of this and just write the addrs.ModuleInstance
89			// value itself into the ModuleState.
90			panic("diff cannot represent modules with count or for_each keys")
91		}
92
93		legacyPath[i] = step.Name
94	}
95
96	m := &ModuleDiff{Path: legacyPath}
97	m.init()
98	d.Modules = append(d.Modules, m)
99	return m
100}
101
102// ModuleByPath is used to lookup the module diff for the given path.
103// This should be the preferred lookup mechanism as it allows for future
104// lookup optimizations.
105func (d *Diff) ModuleByPath(path addrs.ModuleInstance) *ModuleDiff {
106	if d == nil {
107		return nil
108	}
109	for _, mod := range d.Modules {
110		if mod.Path == nil {
111			panic("missing module path")
112		}
113		modPath := normalizeModulePath(mod.Path)
114		if modPath.String() == path.String() {
115			return mod
116		}
117	}
118	return nil
119}
120
121// RootModule returns the ModuleState for the root module
122func (d *Diff) RootModule() *ModuleDiff {
123	root := d.ModuleByPath(addrs.RootModuleInstance)
124	if root == nil {
125		panic("missing root module")
126	}
127	return root
128}
129
130// Empty returns true if the diff has no changes.
131func (d *Diff) Empty() bool {
132	if d == nil {
133		return true
134	}
135
136	for _, m := range d.Modules {
137		if !m.Empty() {
138			return false
139		}
140	}
141
142	return true
143}
144
145// Equal compares two diffs for exact equality.
146//
147// This is different from the Same comparison that is supported which
148// checks for operation equality taking into account computed values. Equal
149// instead checks for exact equality.
150func (d *Diff) Equal(d2 *Diff) bool {
151	// If one is nil, they must both be nil
152	if d == nil || d2 == nil {
153		return d == d2
154	}
155
156	// Sort the modules
157	sort.Sort(moduleDiffSort(d.Modules))
158	sort.Sort(moduleDiffSort(d2.Modules))
159
160	// Copy since we have to modify the module destroy flag to false so
161	// we don't compare that. TODO: delete this when we get rid of the
162	// destroy flag on modules.
163	dCopy := d.DeepCopy()
164	d2Copy := d2.DeepCopy()
165	for _, m := range dCopy.Modules {
166		m.Destroy = false
167	}
168	for _, m := range d2Copy.Modules {
169		m.Destroy = false
170	}
171
172	// Use DeepEqual
173	return reflect.DeepEqual(dCopy, d2Copy)
174}
175
176// DeepCopy performs a deep copy of all parts of the Diff, making the
177// resulting Diff safe to use without modifying this one.
178func (d *Diff) DeepCopy() *Diff {
179	copy, err := copystructure.Config{Lock: true}.Copy(d)
180	if err != nil {
181		panic(err)
182	}
183
184	return copy.(*Diff)
185}
186
187func (d *Diff) String() string {
188	var buf bytes.Buffer
189
190	keys := make([]string, 0, len(d.Modules))
191	lookup := make(map[string]*ModuleDiff)
192	for _, m := range d.Modules {
193		addr := normalizeModulePath(m.Path)
194		key := addr.String()
195		keys = append(keys, key)
196		lookup[key] = m
197	}
198	sort.Strings(keys)
199
200	for _, key := range keys {
201		m := lookup[key]
202		mStr := m.String()
203
204		// If we're the root module, we just write the output directly.
205		if reflect.DeepEqual(m.Path, rootModulePath) {
206			buf.WriteString(mStr + "\n")
207			continue
208		}
209
210		buf.WriteString(fmt.Sprintf("%s:\n", key))
211
212		s := bufio.NewScanner(strings.NewReader(mStr))
213		for s.Scan() {
214			buf.WriteString(fmt.Sprintf("  %s\n", s.Text()))
215		}
216	}
217
218	return strings.TrimSpace(buf.String())
219}
220
221func (d *Diff) init() {
222	if d.Modules == nil {
223		rootDiff := &ModuleDiff{Path: rootModulePath}
224		d.Modules = []*ModuleDiff{rootDiff}
225	}
226	for _, m := range d.Modules {
227		m.init()
228	}
229}
230
231// ModuleDiff tracks the differences between resources to apply within
232// a single module.
233type ModuleDiff struct {
234	Path      []string
235	Resources map[string]*InstanceDiff
236	Destroy   bool // Set only by the destroy plan
237}
238
239func (d *ModuleDiff) init() {
240	if d.Resources == nil {
241		d.Resources = make(map[string]*InstanceDiff)
242	}
243	for _, r := range d.Resources {
244		r.init()
245	}
246}
247
248// ChangeType returns the type of changes that the diff for this
249// module includes.
250//
251// At a module level, this will only be DiffNone, DiffUpdate, DiffDestroy, or
252// DiffCreate. If an instance within the module has a DiffDestroyCreate
253// then this will register as a DiffCreate for a module.
254func (d *ModuleDiff) ChangeType() DiffChangeType {
255	result := DiffNone
256	for _, r := range d.Resources {
257		change := r.ChangeType()
258		switch change {
259		case DiffCreate, DiffDestroy:
260			if result == DiffNone {
261				result = change
262			}
263		case DiffDestroyCreate, DiffUpdate:
264			result = DiffUpdate
265		}
266	}
267
268	return result
269}
270
271// Empty returns true if the diff has no changes within this module.
272func (d *ModuleDiff) Empty() bool {
273	if d.Destroy {
274		return false
275	}
276
277	if len(d.Resources) == 0 {
278		return true
279	}
280
281	for _, rd := range d.Resources {
282		if !rd.Empty() {
283			return false
284		}
285	}
286
287	return true
288}
289
290// Instances returns the instance diffs for the id given. This can return
291// multiple instance diffs if there are counts within the resource.
292func (d *ModuleDiff) Instances(id string) []*InstanceDiff {
293	var result []*InstanceDiff
294	for k, diff := range d.Resources {
295		if k == id || strings.HasPrefix(k, id+".") {
296			if !diff.Empty() {
297				result = append(result, diff)
298			}
299		}
300	}
301
302	return result
303}
304
305// IsRoot says whether or not this module diff is for the root module.
306func (d *ModuleDiff) IsRoot() bool {
307	return reflect.DeepEqual(d.Path, rootModulePath)
308}
309
310// String outputs the diff in a long but command-line friendly output
311// format that users can read to quickly inspect a diff.
312func (d *ModuleDiff) String() string {
313	var buf bytes.Buffer
314
315	names := make([]string, 0, len(d.Resources))
316	for name, _ := range d.Resources {
317		names = append(names, name)
318	}
319	sort.Strings(names)
320
321	for _, name := range names {
322		rdiff := d.Resources[name]
323
324		crud := "UPDATE"
325		switch {
326		case rdiff.RequiresNew() && (rdiff.GetDestroy() || rdiff.GetDestroyTainted()):
327			crud = "DESTROY/CREATE"
328		case rdiff.GetDestroy() || rdiff.GetDestroyDeposed():
329			crud = "DESTROY"
330		case rdiff.RequiresNew():
331			crud = "CREATE"
332		}
333
334		extra := ""
335		if !rdiff.GetDestroy() && rdiff.GetDestroyDeposed() {
336			extra = " (deposed only)"
337		}
338
339		buf.WriteString(fmt.Sprintf(
340			"%s: %s%s\n",
341			crud,
342			name,
343			extra))
344
345		keyLen := 0
346		rdiffAttrs := rdiff.CopyAttributes()
347		keys := make([]string, 0, len(rdiffAttrs))
348		for key, _ := range rdiffAttrs {
349			if key == "id" {
350				continue
351			}
352
353			keys = append(keys, key)
354			if len(key) > keyLen {
355				keyLen = len(key)
356			}
357		}
358		sort.Strings(keys)
359
360		for _, attrK := range keys {
361			attrDiff, _ := rdiff.GetAttribute(attrK)
362
363			v := attrDiff.New
364			u := attrDiff.Old
365			if attrDiff.NewComputed {
366				v = "<computed>"
367			}
368
369			if attrDiff.Sensitive {
370				u = "<sensitive>"
371				v = "<sensitive>"
372			}
373
374			updateMsg := ""
375			if attrDiff.RequiresNew {
376				updateMsg = " (forces new resource)"
377			} else if attrDiff.Sensitive {
378				updateMsg = " (attribute changed)"
379			}
380
381			buf.WriteString(fmt.Sprintf(
382				"  %s:%s %#v => %#v%s\n",
383				attrK,
384				strings.Repeat(" ", keyLen-len(attrK)),
385				u,
386				v,
387				updateMsg))
388		}
389	}
390
391	return buf.String()
392}
393
394// InstanceDiff is the diff of a resource from some state to another.
395type InstanceDiff struct {
396	mu             sync.Mutex
397	Attributes     map[string]*ResourceAttrDiff
398	Destroy        bool
399	DestroyDeposed bool
400	DestroyTainted bool
401
402	// Meta is a simple K/V map that is stored in a diff and persisted to
403	// plans but otherwise is completely ignored by Terraform core. It is
404	// meant to be used for additional data a resource may want to pass through.
405	// The value here must only contain Go primitives and collections.
406	Meta map[string]interface{}
407}
408
409func (d *InstanceDiff) Lock()   { d.mu.Lock() }
410func (d *InstanceDiff) Unlock() { d.mu.Unlock() }
411
412// ApplyToValue merges the receiver into the given base value, returning a
413// new value that incorporates the planned changes. The given value must
414// conform to the given schema, or this method will panic.
415//
416// This method is intended for shimming old subsystems that still use this
417// legacy diff type to work with the new-style types.
418func (d *InstanceDiff) ApplyToValue(base cty.Value, schema *configschema.Block) (cty.Value, error) {
419	// Create an InstanceState attributes from our existing state.
420	// We can use this to more easily apply the diff changes.
421	attrs := hcl2shim.FlatmapValueFromHCL2(base)
422	applied, err := d.Apply(attrs, schema)
423	if err != nil {
424		return base, err
425	}
426
427	val, err := hcl2shim.HCL2ValueFromFlatmap(applied, schema.ImpliedType())
428	if err != nil {
429		return base, err
430	}
431
432	return schema.CoerceValue(val)
433}
434
435// Apply applies the diff to the provided flatmapped attributes,
436// returning the new instance attributes.
437//
438// This method is intended for shimming old subsystems that still use this
439// legacy diff type to work with the new-style types.
440func (d *InstanceDiff) Apply(attrs map[string]string, schema *configschema.Block) (map[string]string, error) {
441	// We always build a new value here, even if the given diff is "empty",
442	// because we might be planning to create a new instance that happens
443	// to have no attributes set, and so we want to produce an empty object
444	// rather than just echoing back the null old value.
445	if attrs == nil {
446		attrs = map[string]string{}
447	}
448
449	// Rather applying the diff to mutate the attrs, we'll copy new values into
450	// here to avoid the possibility of leaving stale values.
451	result := map[string]string{}
452
453	if d.Destroy || d.DestroyDeposed || d.DestroyTainted {
454		return result, nil
455	}
456
457	return d.applyBlockDiff(nil, attrs, schema)
458}
459
460func (d *InstanceDiff) applyBlockDiff(path []string, attrs map[string]string, schema *configschema.Block) (map[string]string, error) {
461	result := map[string]string{}
462	name := ""
463	if len(path) > 0 {
464		name = path[len(path)-1]
465	}
466
467	// localPrefix is used to build the local result map
468	localPrefix := ""
469	if name != "" {
470		localPrefix = name + "."
471	}
472
473	// iterate over the schema rather than the attributes, so we can handle
474	// different block types separately from plain attributes
475	for n, attrSchema := range schema.Attributes {
476		var err error
477		newAttrs, err := d.applyAttrDiff(append(path, n), attrs, attrSchema)
478
479		if err != nil {
480			return result, err
481		}
482
483		for k, v := range newAttrs {
484			result[localPrefix+k] = v
485		}
486	}
487
488	blockPrefix := strings.Join(path, ".")
489	if blockPrefix != "" {
490		blockPrefix += "."
491	}
492	for n, block := range schema.BlockTypes {
493		// we need to find the set of all keys that traverse this block
494		candidateKeys := map[string]bool{}
495		blockKey := blockPrefix + n + "."
496		localBlockPrefix := localPrefix + n + "."
497
498		// we can only trust the diff for sets, since the path changes, so don't
499		// count existing values as candidate keys. If it turns out we're
500		// keeping the attributes, we will catch it down below with "keepBlock"
501		// after we check the set count.
502		if block.Nesting != configschema.NestingSet {
503			for k := range attrs {
504				if strings.HasPrefix(k, blockKey) {
505					nextDot := strings.Index(k[len(blockKey):], ".")
506					if nextDot < 0 {
507						continue
508					}
509					nextDot += len(blockKey)
510					candidateKeys[k[len(blockKey):nextDot]] = true
511				}
512			}
513		}
514
515		for k, diff := range d.Attributes {
516			// helper/schema should not insert nil diff values, but don't panic
517			// if it does.
518			if diff == nil {
519				continue
520			}
521
522			if strings.HasPrefix(k, blockKey) {
523				nextDot := strings.Index(k[len(blockKey):], ".")
524				if nextDot < 0 {
525					continue
526				}
527
528				if diff.NewRemoved {
529					continue
530				}
531
532				nextDot += len(blockKey)
533				candidateKeys[k[len(blockKey):nextDot]] = true
534			}
535		}
536
537		// check each set candidate to see if it was removed.
538		// we need to do this, because when entire sets are removed, they may
539		// have the wrong key, and ony show diffs going to ""
540		if block.Nesting == configschema.NestingSet {
541			for k := range candidateKeys {
542				indexPrefix := strings.Join(append(path, n, k), ".") + "."
543				keep := false
544				// now check each set element to see if it's a new diff, or one
545				// that we're dropping. Since we're only applying the "New"
546				// portion of the set, we can ignore diffs that only contain "Old"
547				for attr, diff := range d.Attributes {
548					// helper/schema should not insert nil diff values, but don't panic
549					// if it does.
550					if diff == nil {
551						continue
552					}
553
554					if !strings.HasPrefix(attr, indexPrefix) {
555						continue
556					}
557
558					// check for empty "count" keys
559					if (strings.HasSuffix(attr, ".#") || strings.HasSuffix(attr, ".%")) && diff.New == "0" {
560						continue
561					}
562
563					// removed items don't count either
564					if diff.NewRemoved {
565						continue
566					}
567
568					// this must be a diff to keep
569					keep = true
570					break
571				}
572				if !keep {
573					delete(candidateKeys, k)
574				}
575			}
576		}
577
578		for k := range candidateKeys {
579			newAttrs, err := d.applyBlockDiff(append(path, n, k), attrs, &block.Block)
580			if err != nil {
581				return result, err
582			}
583
584			for attr, v := range newAttrs {
585				result[localBlockPrefix+attr] = v
586			}
587		}
588
589		keepBlock := true
590		// check this block's count diff directly first, since we may not
591		// have candidates because it was removed and only set to "0"
592		if diff, ok := d.Attributes[blockKey+"#"]; ok {
593			if diff.New == "0" || diff.NewRemoved {
594				keepBlock = false
595			}
596		}
597
598		// if there was no diff at all, then we need to keep the block attributes
599		if len(candidateKeys) == 0 && keepBlock {
600			for k, v := range attrs {
601				if strings.HasPrefix(k, blockKey) {
602					// we need the key relative to this block, so remove the
603					// entire prefix, then re-insert the block name.
604					localKey := localBlockPrefix + k[len(blockKey):]
605					result[localKey] = v
606				}
607			}
608		}
609
610		countAddr := strings.Join(append(path, n, "#"), ".")
611		if countDiff, ok := d.Attributes[countAddr]; ok {
612			if countDiff.NewComputed {
613				result[localBlockPrefix+"#"] = hcl2shim.UnknownVariableValue
614			} else {
615				result[localBlockPrefix+"#"] = countDiff.New
616
617				// While sets are complete, list are not, and we may not have all the
618				// information to track removals. If the list was truncated, we need to
619				// remove the extra items from the result.
620				if block.Nesting == configschema.NestingList &&
621					countDiff.New != "" && countDiff.New != hcl2shim.UnknownVariableValue {
622					length, _ := strconv.Atoi(countDiff.New)
623					for k := range result {
624						if !strings.HasPrefix(k, localBlockPrefix) {
625							continue
626						}
627
628						index := k[len(localBlockPrefix):]
629						nextDot := strings.Index(index, ".")
630						if nextDot < 1 {
631							continue
632						}
633						index = index[:nextDot]
634						i, err := strconv.Atoi(index)
635						if err != nil {
636							// this shouldn't happen since we added these
637							// ourself, but make note of it just in case.
638							log.Printf("[ERROR] bad list index in %q: %s", k, err)
639							continue
640						}
641						if i >= length {
642							delete(result, k)
643						}
644					}
645				}
646			}
647		} else if origCount, ok := attrs[countAddr]; ok && keepBlock {
648			result[localBlockPrefix+"#"] = origCount
649		} else {
650			result[localBlockPrefix+"#"] = countFlatmapContainerValues(localBlockPrefix+"#", result)
651		}
652	}
653
654	return result, nil
655}
656
657func (d *InstanceDiff) applyAttrDiff(path []string, attrs map[string]string, attrSchema *configschema.Attribute) (map[string]string, error) {
658	ty := attrSchema.Type
659	switch {
660	case ty.IsListType(), ty.IsTupleType(), ty.IsMapType():
661		return d.applyCollectionDiff(path, attrs, attrSchema)
662	case ty.IsSetType():
663		return d.applySetDiff(path, attrs, attrSchema)
664	default:
665		return d.applySingleAttrDiff(path, attrs, attrSchema)
666	}
667}
668
669func (d *InstanceDiff) applySingleAttrDiff(path []string, attrs map[string]string, attrSchema *configschema.Attribute) (map[string]string, error) {
670	currentKey := strings.Join(path, ".")
671
672	attr := path[len(path)-1]
673
674	result := map[string]string{}
675	diff := d.Attributes[currentKey]
676	old, exists := attrs[currentKey]
677
678	if diff != nil && diff.NewComputed {
679		result[attr] = hcl2shim.UnknownVariableValue
680		return result, nil
681	}
682
683	// "id" must exist and not be an empty string, or it must be unknown.
684	// This only applied to top-level "id" fields.
685	if attr == "id" && len(path) == 1 {
686		if old == "" {
687			result[attr] = hcl2shim.UnknownVariableValue
688		} else {
689			result[attr] = old
690		}
691		return result, nil
692	}
693
694	// attribute diffs are sometimes missed, so assume no diff means keep the
695	// old value
696	if diff == nil {
697		if exists {
698			result[attr] = old
699		} else {
700			// We need required values, so set those with an empty value. It
701			// must be set in the config, since if it were missing it would have
702			// failed validation.
703			if attrSchema.Required {
704				// we only set a missing string here, since bool or number types
705				// would have distinct zero value which shouldn't have been
706				// lost.
707				if attrSchema.Type == cty.String {
708					result[attr] = ""
709				}
710			}
711		}
712		return result, nil
713	}
714
715	// check for missmatched diff values
716	if exists &&
717		old != diff.Old &&
718		old != hcl2shim.UnknownVariableValue &&
719		diff.Old != hcl2shim.UnknownVariableValue {
720		return result, fmt.Errorf("diff apply conflict for %s: diff expects %q, but prior value has %q", attr, diff.Old, old)
721	}
722
723	if diff.NewRemoved {
724		// don't set anything in the new value
725		return map[string]string{}, nil
726	}
727
728	if diff.Old == diff.New && diff.New == "" {
729		// this can only be a valid empty string
730		if attrSchema.Type == cty.String {
731			result[attr] = ""
732		}
733		return result, nil
734	}
735
736	if attrSchema.Computed && diff.NewComputed {
737		result[attr] = hcl2shim.UnknownVariableValue
738		return result, nil
739	}
740
741	result[attr] = diff.New
742
743	return result, nil
744}
745
746func (d *InstanceDiff) applyCollectionDiff(path []string, attrs map[string]string, attrSchema *configschema.Attribute) (map[string]string, error) {
747	result := map[string]string{}
748
749	prefix := ""
750	if len(path) > 1 {
751		prefix = strings.Join(path[:len(path)-1], ".") + "."
752	}
753
754	name := ""
755	if len(path) > 0 {
756		name = path[len(path)-1]
757	}
758
759	currentKey := prefix + name
760
761	// check the index first for special handling
762	for k, diff := range d.Attributes {
763		// check the index value, which can be set, and 0
764		if k == currentKey+".#" || k == currentKey+".%" || k == currentKey {
765			if diff.NewRemoved {
766				return result, nil
767			}
768
769			if diff.NewComputed {
770				result[k[len(prefix):]] = hcl2shim.UnknownVariableValue
771				return result, nil
772			}
773
774			// do what the diff tells us to here, so that it's consistent with applies
775			if diff.New == "0" {
776				result[k[len(prefix):]] = "0"
777				return result, nil
778			}
779		}
780	}
781
782	// collect all the keys from the diff and the old state
783	noDiff := true
784	keys := map[string]bool{}
785	for k := range d.Attributes {
786		if !strings.HasPrefix(k, currentKey+".") {
787			continue
788		}
789		noDiff = false
790		keys[k] = true
791	}
792
793	noAttrs := true
794	for k := range attrs {
795		if !strings.HasPrefix(k, currentKey+".") {
796			continue
797		}
798		noAttrs = false
799		keys[k] = true
800	}
801
802	// If there's no diff and no attrs, then there's no value at all.
803	// This prevents an unexpected zero-count attribute in the attributes.
804	if noDiff && noAttrs {
805		return result, nil
806	}
807
808	idx := "#"
809	if attrSchema.Type.IsMapType() {
810		idx = "%"
811	}
812
813	for k := range keys {
814		// generate an schema placeholder for the values
815		elSchema := &configschema.Attribute{
816			Type: attrSchema.Type.ElementType(),
817		}
818
819		res, err := d.applySingleAttrDiff(append(path, k[len(currentKey)+1:]), attrs, elSchema)
820		if err != nil {
821			return result, err
822		}
823
824		for k, v := range res {
825			result[name+"."+k] = v
826		}
827	}
828
829	// Just like in nested list blocks, for simple lists we may need to fill in
830	// missing empty strings.
831	countKey := name + "." + idx
832	count := result[countKey]
833	length, _ := strconv.Atoi(count)
834
835	if count != "" && count != hcl2shim.UnknownVariableValue &&
836		attrSchema.Type.Equals(cty.List(cty.String)) {
837		// insert empty strings into missing indexes
838		for i := 0; i < length; i++ {
839			key := fmt.Sprintf("%s.%d", name, i)
840			if _, ok := result[key]; !ok {
841				result[key] = ""
842			}
843		}
844	}
845
846	// now check for truncation in any type of list
847	if attrSchema.Type.IsListType() {
848		for key := range result {
849			if key == countKey {
850				continue
851			}
852
853			if len(key) <= len(name)+1 {
854				// not sure what this is, but don't panic
855				continue
856			}
857
858			index := key[len(name)+1:]
859
860			// It is possible to have nested sets or maps, so look for another dot
861			dot := strings.Index(index, ".")
862			if dot > 0 {
863				index = index[:dot]
864			}
865
866			// This shouldn't have any more dots, since the element type is only string.
867			num, err := strconv.Atoi(index)
868			if err != nil {
869				log.Printf("[ERROR] bad list index in %q: %s", currentKey, err)
870				continue
871			}
872
873			if num >= length {
874				delete(result, key)
875			}
876		}
877	}
878
879	// Fill in the count value if it wasn't present in the diff for some reason,
880	// or if there is no count at all.
881	_, countDiff := d.Attributes[countKey]
882	if result[countKey] == "" || (!countDiff && len(keys) != len(result)) {
883		result[countKey] = countFlatmapContainerValues(countKey, result)
884	}
885
886	return result, nil
887}
888
889func (d *InstanceDiff) applySetDiff(path []string, attrs map[string]string, attrSchema *configschema.Attribute) (map[string]string, error) {
890	// We only need this special behavior for sets of object.
891	if !attrSchema.Type.ElementType().IsObjectType() {
892		// The normal collection apply behavior will work okay for this one, then.
893		return d.applyCollectionDiff(path, attrs, attrSchema)
894	}
895
896	// When we're dealing with a set of an object type we actually want to
897	// use our normal _block type_ apply behaviors, so we'll construct ourselves
898	// a synthetic schema that treats the object type as a block type and
899	// then delegate to our block apply method.
900	synthSchema := &configschema.Block{
901		Attributes: make(map[string]*configschema.Attribute),
902	}
903
904	for name, ty := range attrSchema.Type.ElementType().AttributeTypes() {
905		// We can safely make everything into an attribute here because in the
906		// event that there are nested set attributes we'll end up back in
907		// here again recursively and can then deal with the next level of
908		// expansion.
909		synthSchema.Attributes[name] = &configschema.Attribute{
910			Type:     ty,
911			Optional: true,
912		}
913	}
914
915	parentPath := path[:len(path)-1]
916	childName := path[len(path)-1]
917	containerSchema := &configschema.Block{
918		BlockTypes: map[string]*configschema.NestedBlock{
919			childName: {
920				Nesting: configschema.NestingSet,
921				Block:   *synthSchema,
922			},
923		},
924	}
925
926	return d.applyBlockDiff(parentPath, attrs, containerSchema)
927}
928
929// countFlatmapContainerValues returns the number of values in the flatmapped container
930// (set, map, list) indexed by key. The key argument is expected to include the
931// trailing ".#", or ".%".
932func countFlatmapContainerValues(key string, attrs map[string]string) string {
933	if len(key) < 3 || !(strings.HasSuffix(key, ".#") || strings.HasSuffix(key, ".%")) {
934		panic(fmt.Sprintf("invalid index value %q", key))
935	}
936
937	prefix := key[:len(key)-1]
938	items := map[string]int{}
939
940	for k := range attrs {
941		if k == key {
942			continue
943		}
944		if !strings.HasPrefix(k, prefix) {
945			continue
946		}
947
948		suffix := k[len(prefix):]
949		dot := strings.Index(suffix, ".")
950		if dot > 0 {
951			suffix = suffix[:dot]
952		}
953
954		items[suffix]++
955	}
956	return strconv.Itoa(len(items))
957}
958
959// ResourceAttrDiff is the diff of a single attribute of a resource.
960type ResourceAttrDiff struct {
961	Old         string      // Old Value
962	New         string      // New Value
963	NewComputed bool        // True if new value is computed (unknown currently)
964	NewRemoved  bool        // True if this attribute is being removed
965	NewExtra    interface{} // Extra information for the provider
966	RequiresNew bool        // True if change requires new resource
967	Sensitive   bool        // True if the data should not be displayed in UI output
968	Type        DiffAttrType
969}
970
971// Empty returns true if the diff for this attr is neutral
972func (d *ResourceAttrDiff) Empty() bool {
973	return d.Old == d.New && !d.NewComputed && !d.NewRemoved
974}
975
976func (d *ResourceAttrDiff) GoString() string {
977	return fmt.Sprintf("*%#v", *d)
978}
979
980// DiffAttrType is an enum type that says whether a resource attribute
981// diff is an input attribute (comes from the configuration) or an
982// output attribute (comes as a result of applying the configuration). An
983// example input would be "ami" for AWS and an example output would be
984// "private_ip".
985type DiffAttrType byte
986
987const (
988	DiffAttrUnknown DiffAttrType = iota
989	DiffAttrInput
990	DiffAttrOutput
991)
992
993func (d *InstanceDiff) init() {
994	if d.Attributes == nil {
995		d.Attributes = make(map[string]*ResourceAttrDiff)
996	}
997}
998
999func NewInstanceDiff() *InstanceDiff {
1000	return &InstanceDiff{Attributes: make(map[string]*ResourceAttrDiff)}
1001}
1002
1003func (d *InstanceDiff) Copy() (*InstanceDiff, error) {
1004	if d == nil {
1005		return nil, nil
1006	}
1007
1008	dCopy, err := copystructure.Config{Lock: true}.Copy(d)
1009	if err != nil {
1010		return nil, err
1011	}
1012
1013	return dCopy.(*InstanceDiff), nil
1014}
1015
1016// ChangeType returns the DiffChangeType represented by the diff
1017// for this single instance.
1018func (d *InstanceDiff) ChangeType() DiffChangeType {
1019	if d.Empty() {
1020		return DiffNone
1021	}
1022
1023	if d.RequiresNew() && (d.GetDestroy() || d.GetDestroyTainted()) {
1024		return DiffDestroyCreate
1025	}
1026
1027	if d.GetDestroy() || d.GetDestroyDeposed() {
1028		return DiffDestroy
1029	}
1030
1031	if d.RequiresNew() {
1032		return DiffCreate
1033	}
1034
1035	return DiffUpdate
1036}
1037
1038// Empty returns true if this diff encapsulates no changes.
1039func (d *InstanceDiff) Empty() bool {
1040	if d == nil {
1041		return true
1042	}
1043
1044	d.mu.Lock()
1045	defer d.mu.Unlock()
1046	return !d.Destroy &&
1047		!d.DestroyTainted &&
1048		!d.DestroyDeposed &&
1049		len(d.Attributes) == 0
1050}
1051
1052// Equal compares two diffs for exact equality.
1053//
1054// This is different from the Same comparison that is supported which
1055// checks for operation equality taking into account computed values. Equal
1056// instead checks for exact equality.
1057func (d *InstanceDiff) Equal(d2 *InstanceDiff) bool {
1058	// If one is nil, they must both be nil
1059	if d == nil || d2 == nil {
1060		return d == d2
1061	}
1062
1063	// Use DeepEqual
1064	return reflect.DeepEqual(d, d2)
1065}
1066
1067// DeepCopy performs a deep copy of all parts of the InstanceDiff
1068func (d *InstanceDiff) DeepCopy() *InstanceDiff {
1069	copy, err := copystructure.Config{Lock: true}.Copy(d)
1070	if err != nil {
1071		panic(err)
1072	}
1073
1074	return copy.(*InstanceDiff)
1075}
1076
1077func (d *InstanceDiff) GoString() string {
1078	return fmt.Sprintf("*%#v", InstanceDiff{
1079		Attributes:     d.Attributes,
1080		Destroy:        d.Destroy,
1081		DestroyTainted: d.DestroyTainted,
1082		DestroyDeposed: d.DestroyDeposed,
1083	})
1084}
1085
1086// RequiresNew returns true if the diff requires the creation of a new
1087// resource (implying the destruction of the old).
1088func (d *InstanceDiff) RequiresNew() bool {
1089	if d == nil {
1090		return false
1091	}
1092
1093	d.mu.Lock()
1094	defer d.mu.Unlock()
1095
1096	return d.requiresNew()
1097}
1098
1099func (d *InstanceDiff) requiresNew() bool {
1100	if d == nil {
1101		return false
1102	}
1103
1104	if d.DestroyTainted {
1105		return true
1106	}
1107
1108	for _, rd := range d.Attributes {
1109		if rd != nil && rd.RequiresNew {
1110			return true
1111		}
1112	}
1113
1114	return false
1115}
1116
1117func (d *InstanceDiff) GetDestroyDeposed() bool {
1118	d.mu.Lock()
1119	defer d.mu.Unlock()
1120
1121	return d.DestroyDeposed
1122}
1123
1124func (d *InstanceDiff) SetDestroyDeposed(b bool) {
1125	d.mu.Lock()
1126	defer d.mu.Unlock()
1127
1128	d.DestroyDeposed = b
1129}
1130
1131// These methods are properly locked, for use outside other InstanceDiff
1132// methods but everywhere else within the terraform package.
1133// TODO refactor the locking scheme
1134func (d *InstanceDiff) SetTainted(b bool) {
1135	d.mu.Lock()
1136	defer d.mu.Unlock()
1137
1138	d.DestroyTainted = b
1139}
1140
1141func (d *InstanceDiff) GetDestroyTainted() bool {
1142	d.mu.Lock()
1143	defer d.mu.Unlock()
1144
1145	return d.DestroyTainted
1146}
1147
1148func (d *InstanceDiff) SetDestroy(b bool) {
1149	d.mu.Lock()
1150	defer d.mu.Unlock()
1151
1152	d.Destroy = b
1153}
1154
1155func (d *InstanceDiff) GetDestroy() bool {
1156	d.mu.Lock()
1157	defer d.mu.Unlock()
1158
1159	return d.Destroy
1160}
1161
1162func (d *InstanceDiff) SetAttribute(key string, attr *ResourceAttrDiff) {
1163	d.mu.Lock()
1164	defer d.mu.Unlock()
1165
1166	d.Attributes[key] = attr
1167}
1168
1169func (d *InstanceDiff) DelAttribute(key string) {
1170	d.mu.Lock()
1171	defer d.mu.Unlock()
1172
1173	delete(d.Attributes, key)
1174}
1175
1176func (d *InstanceDiff) GetAttribute(key string) (*ResourceAttrDiff, bool) {
1177	d.mu.Lock()
1178	defer d.mu.Unlock()
1179
1180	attr, ok := d.Attributes[key]
1181	return attr, ok
1182}
1183func (d *InstanceDiff) GetAttributesLen() int {
1184	d.mu.Lock()
1185	defer d.mu.Unlock()
1186
1187	return len(d.Attributes)
1188}
1189
1190// Safely copies the Attributes map
1191func (d *InstanceDiff) CopyAttributes() map[string]*ResourceAttrDiff {
1192	d.mu.Lock()
1193	defer d.mu.Unlock()
1194
1195	attrs := make(map[string]*ResourceAttrDiff)
1196	for k, v := range d.Attributes {
1197		attrs[k] = v
1198	}
1199
1200	return attrs
1201}
1202
1203// Same checks whether or not two InstanceDiff's are the "same". When
1204// we say "same", it is not necessarily exactly equal. Instead, it is
1205// just checking that the same attributes are changing, a destroy
1206// isn't suddenly happening, etc.
1207func (d *InstanceDiff) Same(d2 *InstanceDiff) (bool, string) {
1208	// we can safely compare the pointers without a lock
1209	switch {
1210	case d == nil && d2 == nil:
1211		return true, ""
1212	case d == nil || d2 == nil:
1213		return false, "one nil"
1214	case d == d2:
1215		return true, ""
1216	}
1217
1218	d.mu.Lock()
1219	defer d.mu.Unlock()
1220
1221	// If we're going from requiring new to NOT requiring new, then we have
1222	// to see if all required news were computed. If so, it is allowed since
1223	// computed may also mean "same value and therefore not new".
1224	oldNew := d.requiresNew()
1225	newNew := d2.RequiresNew()
1226	if oldNew && !newNew {
1227		oldNew = false
1228
1229		// This section builds a list of ignorable attributes for requiresNew
1230		// by removing off any elements of collections going to zero elements.
1231		// For collections going to zero, they may not exist at all in the
1232		// new diff (and hence RequiresNew == false).
1233		ignoreAttrs := make(map[string]struct{})
1234		for k, diffOld := range d.Attributes {
1235			if !strings.HasSuffix(k, ".%") && !strings.HasSuffix(k, ".#") {
1236				continue
1237			}
1238
1239			// This case is in here as a protection measure. The bug that this
1240			// code originally fixed (GH-11349) didn't have to deal with computed
1241			// so I'm not 100% sure what the correct behavior is. Best to leave
1242			// the old behavior.
1243			if diffOld.NewComputed {
1244				continue
1245			}
1246
1247			// We're looking for the case a map goes to exactly 0.
1248			if diffOld.New != "0" {
1249				continue
1250			}
1251
1252			// Found it! Ignore all of these. The prefix here is stripping
1253			// off the "%" so it is just "k."
1254			prefix := k[:len(k)-1]
1255			for k2, _ := range d.Attributes {
1256				if strings.HasPrefix(k2, prefix) {
1257					ignoreAttrs[k2] = struct{}{}
1258				}
1259			}
1260		}
1261
1262		for k, rd := range d.Attributes {
1263			if _, ok := ignoreAttrs[k]; ok {
1264				continue
1265			}
1266
1267			// If the field is requires new and NOT computed, then what
1268			// we have is a diff mismatch for sure. We set that the old
1269			// diff does REQUIRE a ForceNew.
1270			if rd != nil && rd.RequiresNew && !rd.NewComputed {
1271				oldNew = true
1272				break
1273			}
1274		}
1275	}
1276
1277	if oldNew != newNew {
1278		return false, fmt.Sprintf(
1279			"diff RequiresNew; old: %t, new: %t", oldNew, newNew)
1280	}
1281
1282	// Verify that destroy matches. The second boolean here allows us to
1283	// have mismatching Destroy if we're moving from RequiresNew true
1284	// to false above. Therefore, the second boolean will only pass if
1285	// we're moving from Destroy: true to false as well.
1286	if d.Destroy != d2.GetDestroy() && d.requiresNew() == oldNew {
1287		return false, fmt.Sprintf(
1288			"diff: Destroy; old: %t, new: %t", d.Destroy, d2.GetDestroy())
1289	}
1290
1291	// Go through the old diff and make sure the new diff has all the
1292	// same attributes. To start, build up the check map to be all the keys.
1293	checkOld := make(map[string]struct{})
1294	checkNew := make(map[string]struct{})
1295	for k, _ := range d.Attributes {
1296		checkOld[k] = struct{}{}
1297	}
1298	for k, _ := range d2.CopyAttributes() {
1299		checkNew[k] = struct{}{}
1300	}
1301
1302	// Make an ordered list so we are sure the approximated hashes are left
1303	// to process at the end of the loop
1304	keys := make([]string, 0, len(d.Attributes))
1305	for k, _ := range d.Attributes {
1306		keys = append(keys, k)
1307	}
1308	sort.StringSlice(keys).Sort()
1309
1310	for _, k := range keys {
1311		diffOld := d.Attributes[k]
1312
1313		if _, ok := checkOld[k]; !ok {
1314			// We're not checking this key for whatever reason (see where
1315			// check is modified).
1316			continue
1317		}
1318
1319		// Remove this key since we'll never hit it again
1320		delete(checkOld, k)
1321		delete(checkNew, k)
1322
1323		_, ok := d2.GetAttribute(k)
1324		if !ok {
1325			// If there's no new attribute, and the old diff expected the attribute
1326			// to be removed, that's just fine.
1327			if diffOld.NewRemoved {
1328				continue
1329			}
1330
1331			// If the last diff was a computed value then the absense of
1332			// that value is allowed since it may mean the value ended up
1333			// being the same.
1334			if diffOld.NewComputed {
1335				ok = true
1336			}
1337
1338			// No exact match, but maybe this is a set containing computed
1339			// values. So check if there is an approximate hash in the key
1340			// and if so, try to match the key.
1341			if strings.Contains(k, "~") {
1342				parts := strings.Split(k, ".")
1343				parts2 := append([]string(nil), parts...)
1344
1345				re := regexp.MustCompile(`^~\d+$`)
1346				for i, part := range parts {
1347					if re.MatchString(part) {
1348						// we're going to consider this the base of a
1349						// computed hash, and remove all longer matching fields
1350						ok = true
1351
1352						parts2[i] = `\d+`
1353						parts2 = parts2[:i+1]
1354						break
1355					}
1356				}
1357
1358				re, err := regexp.Compile("^" + strings.Join(parts2, `\.`))
1359				if err != nil {
1360					return false, fmt.Sprintf("regexp failed to compile; err: %#v", err)
1361				}
1362
1363				for k2, _ := range checkNew {
1364					if re.MatchString(k2) {
1365						delete(checkNew, k2)
1366					}
1367				}
1368			}
1369
1370			// This is a little tricky, but when a diff contains a computed
1371			// list, set, or map that can only be interpolated after the apply
1372			// command has created the dependent resources, it could turn out
1373			// that the result is actually the same as the existing state which
1374			// would remove the key from the diff.
1375			if diffOld.NewComputed && (strings.HasSuffix(k, ".#") || strings.HasSuffix(k, ".%")) {
1376				ok = true
1377			}
1378
1379			// Similarly, in a RequiresNew scenario, a list that shows up in the plan
1380			// diff can disappear from the apply diff, which is calculated from an
1381			// empty state.
1382			if d.requiresNew() && (strings.HasSuffix(k, ".#") || strings.HasSuffix(k, ".%")) {
1383				ok = true
1384			}
1385
1386			if !ok {
1387				return false, fmt.Sprintf("attribute mismatch: %s", k)
1388			}
1389		}
1390
1391		// search for the suffix of the base of a [computed] map, list or set.
1392		match := multiVal.FindStringSubmatch(k)
1393
1394		if diffOld.NewComputed && len(match) == 2 {
1395			matchLen := len(match[1])
1396
1397			// This is a computed list, set, or map, so remove any keys with
1398			// this prefix from the check list.
1399			kprefix := k[:len(k)-matchLen]
1400			for k2, _ := range checkOld {
1401				if strings.HasPrefix(k2, kprefix) {
1402					delete(checkOld, k2)
1403				}
1404			}
1405			for k2, _ := range checkNew {
1406				if strings.HasPrefix(k2, kprefix) {
1407					delete(checkNew, k2)
1408				}
1409			}
1410		}
1411
1412		// We don't compare the values because we can't currently actually
1413		// guarantee to generate the same value two two diffs created from
1414		// the same state+config: we have some pesky interpolation functions
1415		// that do not behave as pure functions (uuid, timestamp) and so they
1416		// can be different each time a diff is produced.
1417		// FIXME: Re-organize our config handling so that we don't re-evaluate
1418		// expressions when we produce a second comparison diff during
1419		// apply (for EvalCompareDiff).
1420	}
1421
1422	// Check for leftover attributes
1423	if len(checkNew) > 0 {
1424		extras := make([]string, 0, len(checkNew))
1425		for attr, _ := range checkNew {
1426			extras = append(extras, attr)
1427		}
1428		return false,
1429			fmt.Sprintf("extra attributes: %s", strings.Join(extras, ", "))
1430	}
1431
1432	return true, ""
1433}
1434
1435// moduleDiffSort implements sort.Interface to sort module diffs by path.
1436type moduleDiffSort []*ModuleDiff
1437
1438func (s moduleDiffSort) Len() int      { return len(s) }
1439func (s moduleDiffSort) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
1440func (s moduleDiffSort) Less(i, j int) bool {
1441	a := s[i]
1442	b := s[j]
1443
1444	// If the lengths are different, then the shorter one always wins
1445	if len(a.Path) != len(b.Path) {
1446		return len(a.Path) < len(b.Path)
1447	}
1448
1449	// Otherwise, compare lexically
1450	return strings.Join(a.Path, ".") < strings.Join(b.Path, ".")
1451}
1452