13 "github.com/mitchellh/copystructure"
16 // DiffChangeType is an enum with the kind of changes a diff has planned.
17 type DiffChangeType byte
20 DiffInvalid DiffChangeType = iota
28 // multiVal matches the index key to a flatmapped set, list or map
29 var multiVal = regexp.MustCompile(`\.(#|%)$`)
31 // Diff trackes the changes that are necessary to apply a configuration
32 // to an existing infrastructure.
34 // Modules contains all the modules that have a diff
38 // Prune cleans out unused structures in the diff without affecting
39 // the behavior of the diff at all.
41 // This is not safe to call concurrently. This is safe to call on a
43 func (d *Diff) Prune() {
48 // Prune all empty modules
49 newModules := make([]*ModuleDiff, 0, len(d.Modules))
50 for _, m := range d.Modules {
51 // If the module isn't empty, we keep it
53 newModules = append(newModules, m)
56 if len(newModules) == 0 {
59 d.Modules = newModules
62 // AddModule adds the module with the given path to the diff.
64 // This should be the preferred method to add module diffs since it
65 // allows us to optimize lookups later as well as control sorting.
66 func (d *Diff) AddModule(path []string) *ModuleDiff {
67 m := &ModuleDiff{Path: path}
69 d.Modules = append(d.Modules, m)
73 // ModuleByPath is used to lookup the module diff for the given path.
74 // This should be the preferred lookup mechanism as it allows for future
75 // lookup optimizations.
76 func (d *Diff) ModuleByPath(path []string) *ModuleDiff {
80 for _, mod := range d.Modules {
82 panic("missing module path")
84 if reflect.DeepEqual(mod.Path, path) {
91 // RootModule returns the ModuleState for the root module
92 func (d *Diff) RootModule() *ModuleDiff {
93 root := d.ModuleByPath(rootModulePath)
95 panic("missing root module")
100 // Empty returns true if the diff has no changes.
101 func (d *Diff) Empty() bool {
106 for _, m := range d.Modules {
115 // Equal compares two diffs for exact equality.
117 // This is different from the Same comparison that is supported which
118 // checks for operation equality taking into account computed values. Equal
119 // instead checks for exact equality.
120 func (d *Diff) Equal(d2 *Diff) bool {
121 // If one is nil, they must both be nil
122 if d == nil || d2 == nil {
127 sort.Sort(moduleDiffSort(d.Modules))
128 sort.Sort(moduleDiffSort(d2.Modules))
130 // Copy since we have to modify the module destroy flag to false so
131 // we don't compare that. TODO: delete this when we get rid of the
132 // destroy flag on modules.
133 dCopy := d.DeepCopy()
134 d2Copy := d2.DeepCopy()
135 for _, m := range dCopy.Modules {
138 for _, m := range d2Copy.Modules {
143 return reflect.DeepEqual(dCopy, d2Copy)
146 // DeepCopy performs a deep copy of all parts of the Diff, making the
147 // resulting Diff safe to use without modifying this one.
148 func (d *Diff) DeepCopy() *Diff {
149 copy, err := copystructure.Config{Lock: true}.Copy(d)
157 func (d *Diff) String() string {
160 keys := make([]string, 0, len(d.Modules))
161 lookup := make(map[string]*ModuleDiff)
162 for _, m := range d.Modules {
163 key := fmt.Sprintf("module.%s", strings.Join(m.Path[1:], "."))
164 keys = append(keys, key)
169 for _, key := range keys {
173 // If we're the root module, we just write the output directly.
174 if reflect.DeepEqual(m.Path, rootModulePath) {
175 buf.WriteString(mStr + "\n")
179 buf.WriteString(fmt.Sprintf("%s:\n", key))
181 s := bufio.NewScanner(strings.NewReader(mStr))
183 buf.WriteString(fmt.Sprintf(" %s\n", s.Text()))
187 return strings.TrimSpace(buf.String())
190 func (d *Diff) init() {
191 if d.Modules == nil {
192 rootDiff := &ModuleDiff{Path: rootModulePath}
193 d.Modules = []*ModuleDiff{rootDiff}
195 for _, m := range d.Modules {
200 // ModuleDiff tracks the differences between resources to apply within
202 type ModuleDiff struct {
204 Resources map[string]*InstanceDiff
205 Destroy bool // Set only by the destroy plan
208 func (d *ModuleDiff) init() {
209 if d.Resources == nil {
210 d.Resources = make(map[string]*InstanceDiff)
212 for _, r := range d.Resources {
217 // ChangeType returns the type of changes that the diff for this
220 // At a module level, this will only be DiffNone, DiffUpdate, DiffDestroy, or
221 // DiffCreate. If an instance within the module has a DiffDestroyCreate
222 // then this will register as a DiffCreate for a module.
223 func (d *ModuleDiff) ChangeType() DiffChangeType {
225 for _, r := range d.Resources {
226 change := r.ChangeType()
228 case DiffCreate, DiffDestroy:
229 if result == DiffNone {
232 case DiffDestroyCreate, DiffUpdate:
240 // Empty returns true if the diff has no changes within this module.
241 func (d *ModuleDiff) Empty() bool {
246 if len(d.Resources) == 0 {
250 for _, rd := range d.Resources {
259 // Instances returns the instance diffs for the id given. This can return
260 // multiple instance diffs if there are counts within the resource.
261 func (d *ModuleDiff) Instances(id string) []*InstanceDiff {
262 var result []*InstanceDiff
263 for k, diff := range d.Resources {
264 if k == id || strings.HasPrefix(k, id+".") {
266 result = append(result, diff)
274 // IsRoot says whether or not this module diff is for the root module.
275 func (d *ModuleDiff) IsRoot() bool {
276 return reflect.DeepEqual(d.Path, rootModulePath)
279 // String outputs the diff in a long but command-line friendly output
280 // format that users can read to quickly inspect a diff.
281 func (d *ModuleDiff) String() string {
284 names := make([]string, 0, len(d.Resources))
285 for name, _ := range d.Resources {
286 names = append(names, name)
290 for _, name := range names {
291 rdiff := d.Resources[name]
295 case rdiff.RequiresNew() && (rdiff.GetDestroy() || rdiff.GetDestroyTainted()):
296 crud = "DESTROY/CREATE"
297 case rdiff.GetDestroy() || rdiff.GetDestroyDeposed():
299 case rdiff.RequiresNew():
304 if !rdiff.GetDestroy() && rdiff.GetDestroyDeposed() {
305 extra = " (deposed only)"
308 buf.WriteString(fmt.Sprintf(
315 rdiffAttrs := rdiff.CopyAttributes()
316 keys := make([]string, 0, len(rdiffAttrs))
317 for key, _ := range rdiffAttrs {
322 keys = append(keys, key)
323 if len(key) > keyLen {
329 for _, attrK := range keys {
330 attrDiff, _ := rdiff.GetAttribute(attrK)
334 if attrDiff.NewComputed {
338 if attrDiff.Sensitive {
344 if attrDiff.RequiresNew {
345 updateMsg = " (forces new resource)"
346 } else if attrDiff.Sensitive {
347 updateMsg = " (attribute changed)"
350 buf.WriteString(fmt.Sprintf(
351 " %s:%s %#v => %#v%s\n",
353 strings.Repeat(" ", keyLen-len(attrK)),
363 // InstanceDiff is the diff of a resource from some state to another.
364 type InstanceDiff struct {
366 Attributes map[string]*ResourceAttrDiff
371 // Meta is a simple K/V map that is stored in a diff and persisted to
372 // plans but otherwise is completely ignored by Terraform core. It is
373 // mean to be used for additional data a resource may want to pass through.
374 // The value here must only contain Go primitives and collections.
375 Meta map[string]interface{}
378 func (d *InstanceDiff) Lock() { d.mu.Lock() }
379 func (d *InstanceDiff) Unlock() { d.mu.Unlock() }
381 // ResourceAttrDiff is the diff of a single attribute of a resource.
382 type ResourceAttrDiff struct {
383 Old string // Old Value
384 New string // New Value
385 NewComputed bool // True if new value is computed (unknown currently)
386 NewRemoved bool // True if this attribute is being removed
387 NewExtra interface{} // Extra information for the provider
388 RequiresNew bool // True if change requires new resource
389 Sensitive bool // True if the data should not be displayed in UI output
393 // Empty returns true if the diff for this attr is neutral
394 func (d *ResourceAttrDiff) Empty() bool {
395 return d.Old == d.New && !d.NewComputed && !d.NewRemoved
398 func (d *ResourceAttrDiff) GoString() string {
399 return fmt.Sprintf("*%#v", *d)
402 // DiffAttrType is an enum type that says whether a resource attribute
403 // diff is an input attribute (comes from the configuration) or an
404 // output attribute (comes as a result of applying the configuration). An
405 // example input would be "ami" for AWS and an example output would be
407 type DiffAttrType byte
410 DiffAttrUnknown DiffAttrType = iota
415 func (d *InstanceDiff) init() {
416 if d.Attributes == nil {
417 d.Attributes = make(map[string]*ResourceAttrDiff)
421 func NewInstanceDiff() *InstanceDiff {
422 return &InstanceDiff{Attributes: make(map[string]*ResourceAttrDiff)}
425 func (d *InstanceDiff) Copy() (*InstanceDiff, error) {
430 dCopy, err := copystructure.Config{Lock: true}.Copy(d)
435 return dCopy.(*InstanceDiff), nil
438 // ChangeType returns the DiffChangeType represented by the diff
439 // for this single instance.
440 func (d *InstanceDiff) ChangeType() DiffChangeType {
445 if d.RequiresNew() && (d.GetDestroy() || d.GetDestroyTainted()) {
446 return DiffDestroyCreate
449 if d.GetDestroy() || d.GetDestroyDeposed() {
460 // Empty returns true if this diff encapsulates no changes.
461 func (d *InstanceDiff) Empty() bool {
471 len(d.Attributes) == 0
474 // Equal compares two diffs for exact equality.
476 // This is different from the Same comparison that is supported which
477 // checks for operation equality taking into account computed values. Equal
478 // instead checks for exact equality.
479 func (d *InstanceDiff) Equal(d2 *InstanceDiff) bool {
480 // If one is nil, they must both be nil
481 if d == nil || d2 == nil {
486 return reflect.DeepEqual(d, d2)
489 // DeepCopy performs a deep copy of all parts of the InstanceDiff
490 func (d *InstanceDiff) DeepCopy() *InstanceDiff {
491 copy, err := copystructure.Config{Lock: true}.Copy(d)
496 return copy.(*InstanceDiff)
499 func (d *InstanceDiff) GoString() string {
500 return fmt.Sprintf("*%#v", InstanceDiff{
501 Attributes: d.Attributes,
503 DestroyTainted: d.DestroyTainted,
504 DestroyDeposed: d.DestroyDeposed,
508 // RequiresNew returns true if the diff requires the creation of a new
509 // resource (implying the destruction of the old).
510 func (d *InstanceDiff) RequiresNew() bool {
518 return d.requiresNew()
521 func (d *InstanceDiff) requiresNew() bool {
526 if d.DestroyTainted {
530 for _, rd := range d.Attributes {
531 if rd != nil && rd.RequiresNew {
539 func (d *InstanceDiff) GetDestroyDeposed() bool {
543 return d.DestroyDeposed
546 func (d *InstanceDiff) SetDestroyDeposed(b bool) {
553 // These methods are properly locked, for use outside other InstanceDiff
554 // methods but everywhere else within in the terraform package.
555 // TODO refactor the locking scheme
556 func (d *InstanceDiff) SetTainted(b bool) {
563 func (d *InstanceDiff) GetDestroyTainted() bool {
567 return d.DestroyTainted
570 func (d *InstanceDiff) SetDestroy(b bool) {
577 func (d *InstanceDiff) GetDestroy() bool {
584 func (d *InstanceDiff) SetAttribute(key string, attr *ResourceAttrDiff) {
588 d.Attributes[key] = attr
591 func (d *InstanceDiff) DelAttribute(key string) {
595 delete(d.Attributes, key)
598 func (d *InstanceDiff) GetAttribute(key string) (*ResourceAttrDiff, bool) {
602 attr, ok := d.Attributes[key]
605 func (d *InstanceDiff) GetAttributesLen() int {
609 return len(d.Attributes)
612 // Safely copies the Attributes map
613 func (d *InstanceDiff) CopyAttributes() map[string]*ResourceAttrDiff {
617 attrs := make(map[string]*ResourceAttrDiff)
618 for k, v := range d.Attributes {
625 // Same checks whether or not two InstanceDiff's are the "same". When
626 // we say "same", it is not necessarily exactly equal. Instead, it is
627 // just checking that the same attributes are changing, a destroy
628 // isn't suddenly happening, etc.
629 func (d *InstanceDiff) Same(d2 *InstanceDiff) (bool, string) {
630 // we can safely compare the pointers without a lock
632 case d == nil && d2 == nil:
634 case d == nil || d2 == nil:
635 return false, "one nil"
643 // If we're going from requiring new to NOT requiring new, then we have
644 // to see if all required news were computed. If so, it is allowed since
645 // computed may also mean "same value and therefore not new".
646 oldNew := d.requiresNew()
647 newNew := d2.RequiresNew()
648 if oldNew && !newNew {
651 // This section builds a list of ignorable attributes for requiresNew
652 // by removing off any elements of collections going to zero elements.
653 // For collections going to zero, they may not exist at all in the
654 // new diff (and hence RequiresNew == false).
655 ignoreAttrs := make(map[string]struct{})
656 for k, diffOld := range d.Attributes {
657 if !strings.HasSuffix(k, ".%") && !strings.HasSuffix(k, ".#") {
661 // This case is in here as a protection measure. The bug that this
662 // code originally fixed (GH-11349) didn't have to deal with computed
663 // so I'm not 100% sure what the correct behavior is. Best to leave
665 if diffOld.NewComputed {
669 // We're looking for the case a map goes to exactly 0.
670 if diffOld.New != "0" {
674 // Found it! Ignore all of these. The prefix here is stripping
675 // off the "%" so it is just "k."
676 prefix := k[:len(k)-1]
677 for k2, _ := range d.Attributes {
678 if strings.HasPrefix(k2, prefix) {
679 ignoreAttrs[k2] = struct{}{}
684 for k, rd := range d.Attributes {
685 if _, ok := ignoreAttrs[k]; ok {
689 // If the field is requires new and NOT computed, then what
690 // we have is a diff mismatch for sure. We set that the old
691 // diff does REQUIRE a ForceNew.
692 if rd != nil && rd.RequiresNew && !rd.NewComputed {
699 if oldNew != newNew {
700 return false, fmt.Sprintf(
701 "diff RequiresNew; old: %t, new: %t", oldNew, newNew)
704 // Verify that destroy matches. The second boolean here allows us to
705 // have mismatching Destroy if we're moving from RequiresNew true
706 // to false above. Therefore, the second boolean will only pass if
707 // we're moving from Destroy: true to false as well.
708 if d.Destroy != d2.GetDestroy() && d.requiresNew() == oldNew {
709 return false, fmt.Sprintf(
710 "diff: Destroy; old: %t, new: %t", d.Destroy, d2.GetDestroy())
713 // Go through the old diff and make sure the new diff has all the
714 // same attributes. To start, build up the check map to be all the keys.
715 checkOld := make(map[string]struct{})
716 checkNew := make(map[string]struct{})
717 for k, _ := range d.Attributes {
718 checkOld[k] = struct{}{}
720 for k, _ := range d2.CopyAttributes() {
721 checkNew[k] = struct{}{}
724 // Make an ordered list so we are sure the approximated hashes are left
725 // to process at the end of the loop
726 keys := make([]string, 0, len(d.Attributes))
727 for k, _ := range d.Attributes {
728 keys = append(keys, k)
730 sort.StringSlice(keys).Sort()
732 for _, k := range keys {
733 diffOld := d.Attributes[k]
735 if _, ok := checkOld[k]; !ok {
736 // We're not checking this key for whatever reason (see where
737 // check is modified).
741 // Remove this key since we'll never hit it again
745 _, ok := d2.GetAttribute(k)
747 // If there's no new attribute, and the old diff expected the attribute
748 // to be removed, that's just fine.
749 if diffOld.NewRemoved {
753 // If the last diff was a computed value then the absense of
754 // that value is allowed since it may mean the value ended up
756 if diffOld.NewComputed {
760 // No exact match, but maybe this is a set containing computed
761 // values. So check if there is an approximate hash in the key
762 // and if so, try to match the key.
763 if strings.Contains(k, "~") {
764 parts := strings.Split(k, ".")
765 parts2 := append([]string(nil), parts...)
767 re := regexp.MustCompile(`^~\d+$`)
768 for i, part := range parts {
769 if re.MatchString(part) {
770 // we're going to consider this the base of a
771 // computed hash, and remove all longer matching fields
775 parts2 = parts2[:i+1]
780 re, err := regexp.Compile("^" + strings.Join(parts2, `\.`))
782 return false, fmt.Sprintf("regexp failed to compile; err: %#v", err)
785 for k2, _ := range checkNew {
786 if re.MatchString(k2) {
792 // This is a little tricky, but when a diff contains a computed
793 // list, set, or map that can only be interpolated after the apply
794 // command has created the dependent resources, it could turn out
795 // that the result is actually the same as the existing state which
796 // would remove the key from the diff.
797 if diffOld.NewComputed && (strings.HasSuffix(k, ".#") || strings.HasSuffix(k, ".%")) {
801 // Similarly, in a RequiresNew scenario, a list that shows up in the plan
802 // diff can disappear from the apply diff, which is calculated from an
804 if d.requiresNew() && (strings.HasSuffix(k, ".#") || strings.HasSuffix(k, ".%")) {
809 return false, fmt.Sprintf("attribute mismatch: %s", k)
813 // search for the suffix of the base of a [computed] map, list or set.
814 match := multiVal.FindStringSubmatch(k)
816 if diffOld.NewComputed && len(match) == 2 {
817 matchLen := len(match[1])
819 // This is a computed list, set, or map, so remove any keys with
820 // this prefix from the check list.
821 kprefix := k[:len(k)-matchLen]
822 for k2, _ := range checkOld {
823 if strings.HasPrefix(k2, kprefix) {
827 for k2, _ := range checkNew {
828 if strings.HasPrefix(k2, kprefix) {
834 // TODO: check for the same value if not computed
837 // Check for leftover attributes
838 if len(checkNew) > 0 {
839 extras := make([]string, 0, len(checkNew))
840 for attr, _ := range checkNew {
841 extras = append(extras, attr)
844 fmt.Sprintf("extra attributes: %s", strings.Join(extras, ", "))
850 // moduleDiffSort implements sort.Interface to sort module diffs by path.
851 type moduleDiffSort []*ModuleDiff
853 func (s moduleDiffSort) Len() int { return len(s) }
854 func (s moduleDiffSort) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
855 func (s moduleDiffSort) Less(i, j int) bool {
859 // If the lengths are different, then the shorter one always wins
860 if len(a.Path) != len(b.Path) {
861 return len(a.Path) < len(b.Path)
864 // Otherwise, compare lexically
865 return strings.Join(a.Path, ".") < strings.Join(b.Path, ".")