github.com/serbaut/terraform@v0.6.12-0.20160607213102-ac2d195cc560/terraform/diff_test.go (about)

     1  package terraform
     2  
     3  import (
     4  	"reflect"
     5  	"strings"
     6  	"testing"
     7  )
     8  
     9  func TestDiffEmpty(t *testing.T) {
    10  	diff := new(Diff)
    11  	if !diff.Empty() {
    12  		t.Fatal("should be empty")
    13  	}
    14  
    15  	mod := diff.AddModule(rootModulePath)
    16  	mod.Resources["nodeA"] = &InstanceDiff{
    17  		Attributes: map[string]*ResourceAttrDiff{
    18  			"foo": &ResourceAttrDiff{
    19  				Old: "foo",
    20  				New: "bar",
    21  			},
    22  		},
    23  	}
    24  
    25  	if diff.Empty() {
    26  		t.Fatal("should not be empty")
    27  	}
    28  }
    29  
    30  func TestModuleDiff_ChangeType(t *testing.T) {
    31  	cases := []struct {
    32  		Diff   *ModuleDiff
    33  		Result DiffChangeType
    34  	}{
    35  		{
    36  			&ModuleDiff{},
    37  			DiffNone,
    38  		},
    39  		{
    40  			&ModuleDiff{
    41  				Resources: map[string]*InstanceDiff{
    42  					"foo": &InstanceDiff{Destroy: true},
    43  				},
    44  			},
    45  			DiffDestroy,
    46  		},
    47  		{
    48  			&ModuleDiff{
    49  				Resources: map[string]*InstanceDiff{
    50  					"foo": &InstanceDiff{
    51  						Attributes: map[string]*ResourceAttrDiff{
    52  							"foo": &ResourceAttrDiff{
    53  								Old: "",
    54  								New: "bar",
    55  							},
    56  						},
    57  					},
    58  				},
    59  			},
    60  			DiffUpdate,
    61  		},
    62  		{
    63  			&ModuleDiff{
    64  				Resources: map[string]*InstanceDiff{
    65  					"foo": &InstanceDiff{
    66  						Attributes: map[string]*ResourceAttrDiff{
    67  							"foo": &ResourceAttrDiff{
    68  								Old:         "",
    69  								New:         "bar",
    70  								RequiresNew: true,
    71  							},
    72  						},
    73  					},
    74  				},
    75  			},
    76  			DiffCreate,
    77  		},
    78  		{
    79  			&ModuleDiff{
    80  				Resources: map[string]*InstanceDiff{
    81  					"foo": &InstanceDiff{
    82  						Destroy: true,
    83  						Attributes: map[string]*ResourceAttrDiff{
    84  							"foo": &ResourceAttrDiff{
    85  								Old:         "",
    86  								New:         "bar",
    87  								RequiresNew: true,
    88  							},
    89  						},
    90  					},
    91  				},
    92  			},
    93  			DiffUpdate,
    94  		},
    95  	}
    96  
    97  	for i, tc := range cases {
    98  		actual := tc.Diff.ChangeType()
    99  		if actual != tc.Result {
   100  			t.Fatalf("%d: %#v", i, actual)
   101  		}
   102  	}
   103  }
   104  
   105  func TestModuleDiff_Empty(t *testing.T) {
   106  	diff := new(ModuleDiff)
   107  	if !diff.Empty() {
   108  		t.Fatal("should be empty")
   109  	}
   110  
   111  	diff.Resources = map[string]*InstanceDiff{
   112  		"nodeA": &InstanceDiff{},
   113  	}
   114  
   115  	if !diff.Empty() {
   116  		t.Fatal("should be empty")
   117  	}
   118  
   119  	diff.Resources["nodeA"].Attributes = map[string]*ResourceAttrDiff{
   120  		"foo": &ResourceAttrDiff{
   121  			Old: "foo",
   122  			New: "bar",
   123  		},
   124  	}
   125  
   126  	if diff.Empty() {
   127  		t.Fatal("should not be empty")
   128  	}
   129  
   130  	diff.Resources["nodeA"].Attributes = nil
   131  	diff.Resources["nodeA"].Destroy = true
   132  
   133  	if diff.Empty() {
   134  		t.Fatal("should not be empty")
   135  	}
   136  }
   137  
   138  func TestModuleDiff_String(t *testing.T) {
   139  	diff := &ModuleDiff{
   140  		Resources: map[string]*InstanceDiff{
   141  			"nodeA": &InstanceDiff{
   142  				Attributes: map[string]*ResourceAttrDiff{
   143  					"foo": &ResourceAttrDiff{
   144  						Old: "foo",
   145  						New: "bar",
   146  					},
   147  					"bar": &ResourceAttrDiff{
   148  						Old:         "foo",
   149  						NewComputed: true,
   150  					},
   151  					"longfoo": &ResourceAttrDiff{
   152  						Old:         "foo",
   153  						New:         "bar",
   154  						RequiresNew: true,
   155  					},
   156  					"secretfoo": &ResourceAttrDiff{
   157  						Old:       "foo",
   158  						New:       "bar",
   159  						Sensitive: true,
   160  					},
   161  				},
   162  			},
   163  		},
   164  	}
   165  
   166  	actual := strings.TrimSpace(diff.String())
   167  	expected := strings.TrimSpace(moduleDiffStrBasic)
   168  	if actual != expected {
   169  		t.Fatalf("bad:\n%s", actual)
   170  	}
   171  }
   172  
   173  func TestInstanceDiff_ChangeType(t *testing.T) {
   174  	cases := []struct {
   175  		Diff   *InstanceDiff
   176  		Result DiffChangeType
   177  	}{
   178  		{
   179  			&InstanceDiff{},
   180  			DiffNone,
   181  		},
   182  		{
   183  			&InstanceDiff{Destroy: true},
   184  			DiffDestroy,
   185  		},
   186  		{
   187  			&InstanceDiff{
   188  				Attributes: map[string]*ResourceAttrDiff{
   189  					"foo": &ResourceAttrDiff{
   190  						Old: "",
   191  						New: "bar",
   192  					},
   193  				},
   194  			},
   195  			DiffUpdate,
   196  		},
   197  		{
   198  			&InstanceDiff{
   199  				Attributes: map[string]*ResourceAttrDiff{
   200  					"foo": &ResourceAttrDiff{
   201  						Old:         "",
   202  						New:         "bar",
   203  						RequiresNew: true,
   204  					},
   205  				},
   206  			},
   207  			DiffCreate,
   208  		},
   209  		{
   210  			&InstanceDiff{
   211  				Destroy: true,
   212  				Attributes: map[string]*ResourceAttrDiff{
   213  					"foo": &ResourceAttrDiff{
   214  						Old:         "",
   215  						New:         "bar",
   216  						RequiresNew: true,
   217  					},
   218  				},
   219  			},
   220  			DiffDestroyCreate,
   221  		},
   222  		{
   223  			&InstanceDiff{
   224  				DestroyTainted: true,
   225  				Attributes: map[string]*ResourceAttrDiff{
   226  					"foo": &ResourceAttrDiff{
   227  						Old:         "",
   228  						New:         "bar",
   229  						RequiresNew: true,
   230  					},
   231  				},
   232  			},
   233  			DiffDestroyCreate,
   234  		},
   235  	}
   236  
   237  	for i, tc := range cases {
   238  		actual := tc.Diff.ChangeType()
   239  		if actual != tc.Result {
   240  			t.Fatalf("%d: %#v", i, actual)
   241  		}
   242  	}
   243  }
   244  
   245  func TestInstanceDiff_Empty(t *testing.T) {
   246  	var rd *InstanceDiff
   247  
   248  	if !rd.Empty() {
   249  		t.Fatal("should be empty")
   250  	}
   251  
   252  	rd = new(InstanceDiff)
   253  
   254  	if !rd.Empty() {
   255  		t.Fatal("should be empty")
   256  	}
   257  
   258  	rd = &InstanceDiff{Destroy: true}
   259  
   260  	if rd.Empty() {
   261  		t.Fatal("should not be empty")
   262  	}
   263  
   264  	rd = &InstanceDiff{
   265  		Attributes: map[string]*ResourceAttrDiff{
   266  			"foo": &ResourceAttrDiff{
   267  				New: "bar",
   268  			},
   269  		},
   270  	}
   271  
   272  	if rd.Empty() {
   273  		t.Fatal("should not be empty")
   274  	}
   275  }
   276  
   277  func TestModuleDiff_Instances(t *testing.T) {
   278  	yesDiff := &InstanceDiff{Destroy: true}
   279  	noDiff := &InstanceDiff{Destroy: true, DestroyTainted: true}
   280  
   281  	cases := []struct {
   282  		Diff   *ModuleDiff
   283  		Id     string
   284  		Result []*InstanceDiff
   285  	}{
   286  		{
   287  			&ModuleDiff{
   288  				Resources: map[string]*InstanceDiff{
   289  					"foo": yesDiff,
   290  					"bar": noDiff,
   291  				},
   292  			},
   293  			"foo",
   294  			[]*InstanceDiff{
   295  				yesDiff,
   296  			},
   297  		},
   298  
   299  		{
   300  			&ModuleDiff{
   301  				Resources: map[string]*InstanceDiff{
   302  					"foo":   yesDiff,
   303  					"foo.0": yesDiff,
   304  					"bar":   noDiff,
   305  				},
   306  			},
   307  			"foo",
   308  			[]*InstanceDiff{
   309  				yesDiff,
   310  				yesDiff,
   311  			},
   312  		},
   313  
   314  		{
   315  			&ModuleDiff{
   316  				Resources: map[string]*InstanceDiff{
   317  					"foo":     yesDiff,
   318  					"foo.0":   yesDiff,
   319  					"foo_bar": noDiff,
   320  					"bar":     noDiff,
   321  				},
   322  			},
   323  			"foo",
   324  			[]*InstanceDiff{
   325  				yesDiff,
   326  				yesDiff,
   327  			},
   328  		},
   329  	}
   330  
   331  	for i, tc := range cases {
   332  		actual := tc.Diff.Instances(tc.Id)
   333  		if !reflect.DeepEqual(actual, tc.Result) {
   334  			t.Fatalf("%d: %#v", i, actual)
   335  		}
   336  	}
   337  }
   338  
   339  func TestInstanceDiff_RequiresNew(t *testing.T) {
   340  	rd := &InstanceDiff{
   341  		Attributes: map[string]*ResourceAttrDiff{
   342  			"foo": &ResourceAttrDiff{},
   343  		},
   344  	}
   345  
   346  	if rd.RequiresNew() {
   347  		t.Fatal("should not require new")
   348  	}
   349  
   350  	rd.Attributes["foo"].RequiresNew = true
   351  
   352  	if !rd.RequiresNew() {
   353  		t.Fatal("should require new")
   354  	}
   355  }
   356  
   357  func TestInstanceDiff_RequiresNew_nil(t *testing.T) {
   358  	var rd *InstanceDiff
   359  
   360  	if rd.RequiresNew() {
   361  		t.Fatal("should not require new")
   362  	}
   363  }
   364  
   365  func TestInstanceDiffSame(t *testing.T) {
   366  	cases := []struct {
   367  		One, Two *InstanceDiff
   368  		Same     bool
   369  		Reason   string
   370  	}{
   371  		{
   372  			&InstanceDiff{},
   373  			&InstanceDiff{},
   374  			true,
   375  			"",
   376  		},
   377  
   378  		{
   379  			nil,
   380  			nil,
   381  			true,
   382  			"",
   383  		},
   384  
   385  		{
   386  			&InstanceDiff{Destroy: false},
   387  			&InstanceDiff{Destroy: true},
   388  			false,
   389  			"diff: Destroy; old: false, new: true",
   390  		},
   391  
   392  		{
   393  			&InstanceDiff{Destroy: true},
   394  			&InstanceDiff{Destroy: true},
   395  			true,
   396  			"",
   397  		},
   398  
   399  		{
   400  			&InstanceDiff{
   401  				Attributes: map[string]*ResourceAttrDiff{
   402  					"foo": &ResourceAttrDiff{},
   403  				},
   404  			},
   405  			&InstanceDiff{
   406  				Attributes: map[string]*ResourceAttrDiff{
   407  					"foo": &ResourceAttrDiff{},
   408  				},
   409  			},
   410  			true,
   411  			"",
   412  		},
   413  
   414  		{
   415  			&InstanceDiff{
   416  				Attributes: map[string]*ResourceAttrDiff{
   417  					"bar": &ResourceAttrDiff{},
   418  				},
   419  			},
   420  			&InstanceDiff{
   421  				Attributes: map[string]*ResourceAttrDiff{
   422  					"foo": &ResourceAttrDiff{},
   423  				},
   424  			},
   425  			false,
   426  			"attribute mismatch: bar",
   427  		},
   428  
   429  		// Extra attributes
   430  		{
   431  			&InstanceDiff{
   432  				Attributes: map[string]*ResourceAttrDiff{
   433  					"foo": &ResourceAttrDiff{},
   434  				},
   435  			},
   436  			&InstanceDiff{
   437  				Attributes: map[string]*ResourceAttrDiff{
   438  					"foo": &ResourceAttrDiff{},
   439  					"bar": &ResourceAttrDiff{},
   440  				},
   441  			},
   442  			false,
   443  			"extra attributes: bar",
   444  		},
   445  
   446  		{
   447  			&InstanceDiff{
   448  				Attributes: map[string]*ResourceAttrDiff{
   449  					"foo": &ResourceAttrDiff{RequiresNew: true},
   450  				},
   451  			},
   452  			&InstanceDiff{
   453  				Attributes: map[string]*ResourceAttrDiff{
   454  					"foo": &ResourceAttrDiff{RequiresNew: false},
   455  				},
   456  			},
   457  			false,
   458  			"diff RequiresNew; old: true, new: false",
   459  		},
   460  
   461  		{
   462  			&InstanceDiff{
   463  				Attributes: map[string]*ResourceAttrDiff{
   464  					"foo.#": &ResourceAttrDiff{NewComputed: true},
   465  				},
   466  			},
   467  			&InstanceDiff{
   468  				Attributes: map[string]*ResourceAttrDiff{
   469  					"foo.#": &ResourceAttrDiff{
   470  						Old: "0",
   471  						New: "1",
   472  					},
   473  					"foo.0": &ResourceAttrDiff{
   474  						Old: "",
   475  						New: "12",
   476  					},
   477  				},
   478  			},
   479  			true,
   480  			"",
   481  		},
   482  
   483  		{
   484  			&InstanceDiff{
   485  				Attributes: map[string]*ResourceAttrDiff{
   486  					"foo.#": &ResourceAttrDiff{
   487  						Old: "0",
   488  						New: "1",
   489  					},
   490  					"foo.~35964334.bar": &ResourceAttrDiff{
   491  						Old: "",
   492  						New: "${var.foo}",
   493  					},
   494  				},
   495  			},
   496  			&InstanceDiff{
   497  				Attributes: map[string]*ResourceAttrDiff{
   498  					"foo.#": &ResourceAttrDiff{
   499  						Old: "0",
   500  						New: "1",
   501  					},
   502  					"foo.87654323.bar": &ResourceAttrDiff{
   503  						Old: "",
   504  						New: "12",
   505  					},
   506  				},
   507  			},
   508  			true,
   509  			"",
   510  		},
   511  
   512  		{
   513  			&InstanceDiff{
   514  				Attributes: map[string]*ResourceAttrDiff{
   515  					"foo.#": &ResourceAttrDiff{
   516  						Old:         "0",
   517  						NewComputed: true,
   518  					},
   519  				},
   520  			},
   521  			&InstanceDiff{
   522  				Attributes: map[string]*ResourceAttrDiff{},
   523  			},
   524  			true,
   525  			"",
   526  		},
   527  
   528  		// In a DESTROY/CREATE scenario, the plan diff will be run against the
   529  		// state of the old instance, while the apply diff will be run against an
   530  		// empty state (because the state is cleared when the destroy runs.)
   531  		// For complex attributes, this can result in keys that seem to disappear
   532  		// between the two diffs, when in reality everything is working just fine.
   533  		//
   534  		// Same() needs to take into account this scenario by analyzing NewRemoved
   535  		// and treating as "Same" a diff that does indeed have that key removed.
   536  		{
   537  			&InstanceDiff{
   538  				Attributes: map[string]*ResourceAttrDiff{
   539  					"somemap.oldkey": &ResourceAttrDiff{
   540  						Old:        "long ago",
   541  						New:        "",
   542  						NewRemoved: true,
   543  					},
   544  					"somemap.newkey": &ResourceAttrDiff{
   545  						Old: "",
   546  						New: "brave new world",
   547  					},
   548  				},
   549  			},
   550  			&InstanceDiff{
   551  				Attributes: map[string]*ResourceAttrDiff{
   552  					"somemap.newkey": &ResourceAttrDiff{
   553  						Old: "",
   554  						New: "brave new world",
   555  					},
   556  				},
   557  			},
   558  			true,
   559  			"",
   560  		},
   561  
   562  		// Another thing that can occur in DESTROY/CREATE scenarios is that list
   563  		// values that are going to zero have diffs that show up at plan time but
   564  		// are gone at apply time. The NewRemoved handling catches the fields and
   565  		// treats them as OK, but it also needs to treat the .# field itself as
   566  		// okay to be present in the old diff but not in the new one.
   567  		{
   568  			&InstanceDiff{
   569  				Attributes: map[string]*ResourceAttrDiff{
   570  					"reqnew": &ResourceAttrDiff{
   571  						Old:         "old",
   572  						New:         "new",
   573  						RequiresNew: true,
   574  					},
   575  					"somemap.#": &ResourceAttrDiff{
   576  						Old: "1",
   577  						New: "0",
   578  					},
   579  					"somemap.oldkey": &ResourceAttrDiff{
   580  						Old:        "long ago",
   581  						New:        "",
   582  						NewRemoved: true,
   583  					},
   584  				},
   585  			},
   586  			&InstanceDiff{
   587  				Attributes: map[string]*ResourceAttrDiff{
   588  					"reqnew": &ResourceAttrDiff{
   589  						Old:         "",
   590  						New:         "new",
   591  						RequiresNew: true,
   592  					},
   593  				},
   594  			},
   595  			true,
   596  			"",
   597  		},
   598  	}
   599  
   600  	for i, tc := range cases {
   601  		same, reason := tc.One.Same(tc.Two)
   602  		if same != tc.Same {
   603  			t.Fatalf("%d: expected same: %t, got %t (%s)\n\n one: %#v\n\ntwo: %#v",
   604  				i, tc.Same, same, reason, tc.One, tc.Two)
   605  		}
   606  		if reason != tc.Reason {
   607  			t.Fatalf(
   608  				"%d: bad reason\n\nexpected: %#v\n\ngot: %#v", i, tc.Reason, reason)
   609  		}
   610  	}
   611  }
   612  
   613  const moduleDiffStrBasic = `
   614  CREATE: nodeA
   615    bar:       "foo" => "<computed>"
   616    foo:       "foo" => "bar"
   617    longfoo:   "foo" => "bar" (forces new resource)
   618    secretfoo: "<sensitive>" => "<sensitive>" (attribute changed)
   619  `