88 "regexp"
99 "sort"
1010 "strings"
11+ "sync"
1112)
1213
1314// DiffChangeType is an enum with the kind of changes a diff has planned.
@@ -216,9 +217,9 @@ func (d *ModuleDiff) String() string {
216217
217218 crud := "UPDATE"
218219 switch {
219- case rdiff .RequiresNew () && (rdiff .Destroy || rdiff .DestroyTainted ):
220+ case rdiff .RequiresNew () && (rdiff .GetDestroy () || rdiff .GetDestroyTainted () ):
220221 crud = "DESTROY/CREATE"
221- case rdiff .Destroy :
222+ case rdiff .GetDestroy () :
222223 crud = "DESTROY"
223224 case rdiff .RequiresNew ():
224225 crud = "CREATE"
@@ -230,8 +231,9 @@ func (d *ModuleDiff) String() string {
230231 name ))
231232
232233 keyLen := 0
233- keys := make ([]string , 0 , len (rdiff .Attributes ))
234- for key , _ := range rdiff .Attributes {
234+ rdiffAttrs := rdiff .CopyAttributes ()
235+ keys := make ([]string , 0 , len (rdiffAttrs ))
236+ for key , _ := range rdiffAttrs {
235237 if key == "id" {
236238 continue
237239 }
@@ -244,7 +246,7 @@ func (d *ModuleDiff) String() string {
244246 sort .Strings (keys )
245247
246248 for _ , attrK := range keys {
247- attrDiff := rdiff .Attributes [ attrK ]
249+ attrDiff , _ := rdiff .GetAttribute ( attrK )
248250
249251 v := attrDiff .New
250252 u := attrDiff .Old
@@ -279,6 +281,7 @@ func (d *ModuleDiff) String() string {
279281
280282// InstanceDiff is the diff of a resource from some state to another.
281283type InstanceDiff struct {
284+ mu sync.Mutex
282285 Attributes map [string ]* ResourceAttrDiff
283286 Destroy bool
284287 DestroyTainted bool
@@ -324,18 +327,22 @@ func (d *InstanceDiff) init() {
324327 }
325328}
326329
330+ func NewInstanceDiff () * InstanceDiff {
331+ return & InstanceDiff {Attributes : make (map [string ]* ResourceAttrDiff )}
332+ }
333+
327334// ChangeType returns the DiffChangeType represented by the diff
328335// for this single instance.
329336func (d * InstanceDiff ) ChangeType () DiffChangeType {
330337 if d .Empty () {
331338 return DiffNone
332339 }
333340
334- if d .RequiresNew () && (d .Destroy || d .DestroyTainted ) {
341+ if d .RequiresNew () && (d .GetDestroy () || d .GetDestroyTainted () ) {
335342 return DiffDestroyCreate
336343 }
337344
338- if d .Destroy {
345+ if d .GetDestroy () {
339346 return DiffDestroy
340347 }
341348
@@ -352,6 +359,8 @@ func (d *InstanceDiff) Empty() bool {
352359 return true
353360 }
354361
362+ d .mu .Lock ()
363+ defer d .mu .Unlock ()
355364 return ! d .Destroy && len (d .Attributes ) == 0
356365}
357366
@@ -366,6 +375,17 @@ func (d *InstanceDiff) RequiresNew() bool {
366375 return false
367376 }
368377
378+ d .mu .Lock ()
379+ defer d .mu .Unlock ()
380+
381+ return d .requiresNew ()
382+ }
383+
384+ func (d * InstanceDiff ) requiresNew () bool {
385+ if d == nil {
386+ return false
387+ }
388+
369389 if d .DestroyTainted {
370390 return true
371391 }
@@ -379,24 +399,103 @@ func (d *InstanceDiff) RequiresNew() bool {
379399 return false
380400}
381401
402+ // These methods are properly locked, for use outside other InstanceDiff
403+ // methods but everywhere else within in the terraform package.
404+ // TODO refactor the locking scheme
405+ func (d * InstanceDiff ) SetTainted (b bool ) {
406+ d .mu .Lock ()
407+ defer d .mu .Unlock ()
408+
409+ d .DestroyTainted = b
410+ }
411+
412+ func (d * InstanceDiff ) GetDestroyTainted () bool {
413+ d .mu .Lock ()
414+ defer d .mu .Unlock ()
415+
416+ return d .DestroyTainted
417+ }
418+
419+ func (d * InstanceDiff ) SetDestroy (b bool ) {
420+ d .mu .Lock ()
421+ defer d .mu .Unlock ()
422+
423+ d .Destroy = b
424+ }
425+
426+ func (d * InstanceDiff ) GetDestroy () bool {
427+ d .mu .Lock ()
428+ defer d .mu .Unlock ()
429+
430+ return d .Destroy
431+ }
432+
433+ func (d * InstanceDiff ) SetAttribute (key string , attr * ResourceAttrDiff ) {
434+ d .mu .Lock ()
435+ defer d .mu .Unlock ()
436+
437+ d .Attributes [key ] = attr
438+ }
439+
440+ func (d * InstanceDiff ) DelAttribute (key string ) {
441+ d .mu .Lock ()
442+ defer d .mu .Unlock ()
443+
444+ delete (d .Attributes , key )
445+ }
446+
447+ func (d * InstanceDiff ) GetAttribute (key string ) (* ResourceAttrDiff , bool ) {
448+ d .mu .Lock ()
449+ defer d .mu .Unlock ()
450+
451+ attr , ok := d .Attributes [key ]
452+ return attr , ok
453+ }
454+ func (d * InstanceDiff ) GetAttributesLen () int {
455+ d .mu .Lock ()
456+ defer d .mu .Unlock ()
457+
458+ return len (d .Attributes )
459+ }
460+
461+ // Safely copies the Attributes map
462+ func (d * InstanceDiff ) CopyAttributes () map [string ]* ResourceAttrDiff {
463+ d .mu .Lock ()
464+ defer d .mu .Unlock ()
465+
466+ attrs := make (map [string ]* ResourceAttrDiff )
467+ for k , v := range d .Attributes {
468+ attrs [k ] = v
469+ }
470+
471+ return attrs
472+ }
473+
382474// Same checks whether or not two InstanceDiff's are the "same". When
383475// we say "same", it is not necessarily exactly equal. Instead, it is
384476// just checking that the same attributes are changing, a destroy
385477// isn't suddenly happening, etc.
386478func (d * InstanceDiff ) Same (d2 * InstanceDiff ) (bool , string ) {
387- if d == nil && d2 == nil {
479+ // we can safely compare the pointers without a lock
480+ switch {
481+ case d == nil && d2 == nil :
482+ return true , ""
483+ case d == nil || d2 == nil :
484+ return false , "one nil"
485+ case d == d2 :
388486 return true , ""
389- } else if d == nil || d2 == nil {
390- return false , "both nil"
391487 }
392488
393- if d .Destroy != d2 .Destroy {
489+ d .mu .Lock ()
490+ defer d .mu .Unlock ()
491+
492+ if d .Destroy != d2 .GetDestroy () {
394493 return false , fmt .Sprintf (
395- "diff: Destroy; old: %t, new: %t" , d .Destroy , d2 .Destroy )
494+ "diff: Destroy; old: %t, new: %t" , d .Destroy , d2 .GetDestroy () )
396495 }
397- if d .RequiresNew () != d2 .RequiresNew () {
496+ if d .requiresNew () != d2 .RequiresNew () {
398497 return false , fmt .Sprintf (
399- "diff RequiresNew; old: %t, new: %t" , d .RequiresNew (), d2 .RequiresNew ())
498+ "diff RequiresNew; old: %t, new: %t" , d .requiresNew (), d2 .RequiresNew ())
400499 }
401500
402501 // Go through the old diff and make sure the new diff has all the
@@ -406,7 +505,7 @@ func (d *InstanceDiff) Same(d2 *InstanceDiff) (bool, string) {
406505 for k , _ := range d .Attributes {
407506 checkOld [k ] = struct {}{}
408507 }
409- for k , _ := range d2 .Attributes {
508+ for k , _ := range d2 .CopyAttributes () {
410509 checkNew [k ] = struct {}{}
411510 }
412511
@@ -431,7 +530,7 @@ func (d *InstanceDiff) Same(d2 *InstanceDiff) (bool, string) {
431530 delete (checkOld , k )
432531 delete (checkNew , k )
433532
434- _ , ok := d2 .Attributes [ k ]
533+ _ , ok := d2 .GetAttribute ( k )
435534 if ! ok {
436535 // If there's no new attribute, and the old diff expected the attribute
437536 // to be removed, that's just fine.
@@ -483,7 +582,7 @@ func (d *InstanceDiff) Same(d2 *InstanceDiff) (bool, string) {
483582 // Similarly, in a RequiresNew scenario, a list that shows up in the plan
484583 // diff can disappear from the apply diff, which is calculated from an
485584 // empty state.
486- if d .RequiresNew () && (strings .HasSuffix (k , ".#" ) || strings .HasSuffix (k , ".%" )) {
585+ if d .requiresNew () && (strings .HasSuffix (k , ".#" ) || strings .HasSuffix (k , ".%" )) {
487586 ok = true
488587 }
489588
0 commit comments