github.com/dtx/terraform@v0.7.4-0.20160907225157-926acfd0821e/terraform/state_test.go (about)

     1  package terraform
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/json"
     6  	"reflect"
     7  	"strings"
     8  	"testing"
     9  
    10  	"github.com/hashicorp/terraform/config"
    11  )
    12  
    13  func TestStateValidate(t *testing.T) {
    14  	cases := map[string]struct {
    15  		In  *State
    16  		Err bool
    17  	}{
    18  		"empty state": {
    19  			&State{},
    20  			false,
    21  		},
    22  
    23  		"multiple modules": {
    24  			&State{
    25  				Modules: []*ModuleState{
    26  					&ModuleState{
    27  						Path: []string{"root", "foo"},
    28  					},
    29  					&ModuleState{
    30  						Path: []string{"root", "foo"},
    31  					},
    32  				},
    33  			},
    34  			true,
    35  		},
    36  	}
    37  
    38  	for name, tc := range cases {
    39  		// Init the state
    40  		tc.In.init()
    41  
    42  		err := tc.In.Validate()
    43  		if (err != nil) != tc.Err {
    44  			t.Fatalf("%s: err: %s", name, err)
    45  		}
    46  	}
    47  }
    48  
    49  func TestStateAddModule(t *testing.T) {
    50  	cases := []struct {
    51  		In  [][]string
    52  		Out [][]string
    53  	}{
    54  		{
    55  			[][]string{
    56  				[]string{"root"},
    57  				[]string{"root", "child"},
    58  			},
    59  			[][]string{
    60  				[]string{"root"},
    61  				[]string{"root", "child"},
    62  			},
    63  		},
    64  
    65  		{
    66  			[][]string{
    67  				[]string{"root", "foo", "bar"},
    68  				[]string{"root", "foo"},
    69  				[]string{"root"},
    70  				[]string{"root", "bar"},
    71  			},
    72  			[][]string{
    73  				[]string{"root"},
    74  				[]string{"root", "bar"},
    75  				[]string{"root", "foo"},
    76  				[]string{"root", "foo", "bar"},
    77  			},
    78  		},
    79  		// Same last element, different middle element
    80  		{
    81  			[][]string{
    82  				[]string{"root", "foo", "bar"}, // This one should sort after...
    83  				[]string{"root", "foo"},
    84  				[]string{"root"},
    85  				[]string{"root", "bar", "bar"}, // ...this one.
    86  				[]string{"root", "bar"},
    87  			},
    88  			[][]string{
    89  				[]string{"root"},
    90  				[]string{"root", "bar"},
    91  				[]string{"root", "foo"},
    92  				[]string{"root", "bar", "bar"},
    93  				[]string{"root", "foo", "bar"},
    94  			},
    95  		},
    96  	}
    97  
    98  	for _, tc := range cases {
    99  		s := new(State)
   100  		for _, p := range tc.In {
   101  			s.AddModule(p)
   102  		}
   103  
   104  		actual := make([][]string, 0, len(tc.In))
   105  		for _, m := range s.Modules {
   106  			actual = append(actual, m.Path)
   107  		}
   108  
   109  		if !reflect.DeepEqual(actual, tc.Out) {
   110  			t.Fatalf("In: %#v\n\nOut: %#v", tc.In, actual)
   111  		}
   112  	}
   113  }
   114  
   115  func TestStateOutputTypeRoundTrip(t *testing.T) {
   116  	state := &State{
   117  		Modules: []*ModuleState{
   118  			&ModuleState{
   119  				Path: RootModulePath,
   120  				Outputs: map[string]*OutputState{
   121  					"string_output": &OutputState{
   122  						Value: "String Value",
   123  						Type:  "string",
   124  					},
   125  				},
   126  			},
   127  		},
   128  	}
   129  	state.init()
   130  
   131  	buf := new(bytes.Buffer)
   132  	if err := WriteState(state, buf); err != nil {
   133  		t.Fatalf("err: %s", err)
   134  	}
   135  
   136  	roundTripped, err := ReadState(buf)
   137  	if err != nil {
   138  		t.Fatalf("err: %s", err)
   139  	}
   140  
   141  	if !reflect.DeepEqual(state, roundTripped) {
   142  		t.Logf("expected:\n%#v", state)
   143  		t.Fatalf("got:\n%#v", roundTripped)
   144  	}
   145  }
   146  
   147  func TestStateModuleOrphans(t *testing.T) {
   148  	state := &State{
   149  		Modules: []*ModuleState{
   150  			&ModuleState{
   151  				Path: RootModulePath,
   152  			},
   153  			&ModuleState{
   154  				Path: []string{RootModuleName, "foo"},
   155  			},
   156  			&ModuleState{
   157  				Path: []string{RootModuleName, "bar"},
   158  			},
   159  		},
   160  	}
   161  
   162  	state.init()
   163  
   164  	config := testModule(t, "state-module-orphans").Config()
   165  	actual := state.ModuleOrphans(RootModulePath, config)
   166  	expected := [][]string{
   167  		[]string{RootModuleName, "foo"},
   168  	}
   169  
   170  	if !reflect.DeepEqual(actual, expected) {
   171  		t.Fatalf("bad: %#v", actual)
   172  	}
   173  }
   174  
   175  func TestStateModuleOrphans_nested(t *testing.T) {
   176  	state := &State{
   177  		Modules: []*ModuleState{
   178  			&ModuleState{
   179  				Path: RootModulePath,
   180  			},
   181  			&ModuleState{
   182  				Path: []string{RootModuleName, "foo", "bar"},
   183  			},
   184  		},
   185  	}
   186  
   187  	state.init()
   188  
   189  	actual := state.ModuleOrphans(RootModulePath, nil)
   190  	expected := [][]string{
   191  		[]string{RootModuleName, "foo"},
   192  	}
   193  
   194  	if !reflect.DeepEqual(actual, expected) {
   195  		t.Fatalf("bad: %#v", actual)
   196  	}
   197  }
   198  
   199  func TestStateModuleOrphans_nilConfig(t *testing.T) {
   200  	state := &State{
   201  		Modules: []*ModuleState{
   202  			&ModuleState{
   203  				Path: RootModulePath,
   204  			},
   205  			&ModuleState{
   206  				Path: []string{RootModuleName, "foo"},
   207  			},
   208  			&ModuleState{
   209  				Path: []string{RootModuleName, "bar"},
   210  			},
   211  		},
   212  	}
   213  
   214  	state.init()
   215  
   216  	actual := state.ModuleOrphans(RootModulePath, nil)
   217  	expected := [][]string{
   218  		[]string{RootModuleName, "foo"},
   219  		[]string{RootModuleName, "bar"},
   220  	}
   221  
   222  	if !reflect.DeepEqual(actual, expected) {
   223  		t.Fatalf("bad: %#v", actual)
   224  	}
   225  }
   226  
   227  func TestStateModuleOrphans_deepNestedNilConfig(t *testing.T) {
   228  	state := &State{
   229  		Modules: []*ModuleState{
   230  			&ModuleState{
   231  				Path: RootModulePath,
   232  			},
   233  			&ModuleState{
   234  				Path: []string{RootModuleName, "parent", "childfoo"},
   235  			},
   236  			&ModuleState{
   237  				Path: []string{RootModuleName, "parent", "childbar"},
   238  			},
   239  		},
   240  	}
   241  
   242  	state.init()
   243  
   244  	actual := state.ModuleOrphans(RootModulePath, nil)
   245  	expected := [][]string{
   246  		[]string{RootModuleName, "parent"},
   247  	}
   248  
   249  	if !reflect.DeepEqual(actual, expected) {
   250  		t.Fatalf("bad: %#v", actual)
   251  	}
   252  }
   253  
   254  func TestStateDeepCopy(t *testing.T) {
   255  	cases := []struct {
   256  		One, Two *State
   257  		F        func(*State) interface{}
   258  	}{
   259  		// Version
   260  		{
   261  			&State{Version: 5},
   262  			&State{Version: 5},
   263  			func(s *State) interface{} { return s.Version },
   264  		},
   265  
   266  		// TFVersion
   267  		{
   268  			&State{TFVersion: "5"},
   269  			&State{TFVersion: "5"},
   270  			func(s *State) interface{} { return s.TFVersion },
   271  		},
   272  	}
   273  
   274  	for i, tc := range cases {
   275  		actual := tc.F(tc.One.DeepCopy())
   276  		expected := tc.F(tc.Two)
   277  		if !reflect.DeepEqual(actual, expected) {
   278  			t.Fatalf("Bad: %d\n\n%s\n\n%s", i, actual, expected)
   279  		}
   280  	}
   281  }
   282  
   283  func TestStateEqual(t *testing.T) {
   284  	cases := []struct {
   285  		Result   bool
   286  		One, Two *State
   287  	}{
   288  		// Nils
   289  		{
   290  			false,
   291  			nil,
   292  			&State{Version: 2},
   293  		},
   294  
   295  		{
   296  			true,
   297  			nil,
   298  			nil,
   299  		},
   300  
   301  		// Different versions
   302  		{
   303  			false,
   304  			&State{Version: 5},
   305  			&State{Version: 2},
   306  		},
   307  
   308  		// Different modules
   309  		{
   310  			false,
   311  			&State{
   312  				Modules: []*ModuleState{
   313  					&ModuleState{
   314  						Path: RootModulePath,
   315  					},
   316  				},
   317  			},
   318  			&State{},
   319  		},
   320  
   321  		{
   322  			true,
   323  			&State{
   324  				Modules: []*ModuleState{
   325  					&ModuleState{
   326  						Path: RootModulePath,
   327  					},
   328  				},
   329  			},
   330  			&State{
   331  				Modules: []*ModuleState{
   332  					&ModuleState{
   333  						Path: RootModulePath,
   334  					},
   335  				},
   336  			},
   337  		},
   338  
   339  		// Meta differs
   340  		{
   341  			false,
   342  			&State{
   343  				Modules: []*ModuleState{
   344  					&ModuleState{
   345  						Path: rootModulePath,
   346  						Resources: map[string]*ResourceState{
   347  							"test_instance.foo": &ResourceState{
   348  								Primary: &InstanceState{
   349  									Meta: map[string]string{
   350  										"schema_version": "1",
   351  									},
   352  								},
   353  							},
   354  						},
   355  					},
   356  				},
   357  			},
   358  			&State{
   359  				Modules: []*ModuleState{
   360  					&ModuleState{
   361  						Path: rootModulePath,
   362  						Resources: map[string]*ResourceState{
   363  							"test_instance.foo": &ResourceState{
   364  								Primary: &InstanceState{
   365  									Meta: map[string]string{
   366  										"schema_version": "2",
   367  									},
   368  								},
   369  							},
   370  						},
   371  					},
   372  				},
   373  			},
   374  		},
   375  	}
   376  
   377  	for i, tc := range cases {
   378  		if tc.One.Equal(tc.Two) != tc.Result {
   379  			t.Fatalf("Bad: %d\n\n%s\n\n%s", i, tc.One.String(), tc.Two.String())
   380  		}
   381  		if tc.Two.Equal(tc.One) != tc.Result {
   382  			t.Fatalf("Bad: %d\n\n%s\n\n%s", i, tc.One.String(), tc.Two.String())
   383  		}
   384  	}
   385  }
   386  
   387  func TestStateCompareAges(t *testing.T) {
   388  	cases := []struct {
   389  		Result   StateAgeComparison
   390  		Err      bool
   391  		One, Two *State
   392  	}{
   393  		{
   394  			StateAgeEqual, false,
   395  			&State{
   396  				Lineage: "1",
   397  				Serial:  2,
   398  			},
   399  			&State{
   400  				Lineage: "1",
   401  				Serial:  2,
   402  			},
   403  		},
   404  		{
   405  			StateAgeReceiverOlder, false,
   406  			&State{
   407  				Lineage: "1",
   408  				Serial:  2,
   409  			},
   410  			&State{
   411  				Lineage: "1",
   412  				Serial:  3,
   413  			},
   414  		},
   415  		{
   416  			StateAgeReceiverNewer, false,
   417  			&State{
   418  				Lineage: "1",
   419  				Serial:  3,
   420  			},
   421  			&State{
   422  				Lineage: "1",
   423  				Serial:  2,
   424  			},
   425  		},
   426  		{
   427  			StateAgeEqual, true,
   428  			&State{
   429  				Lineage: "1",
   430  				Serial:  2,
   431  			},
   432  			&State{
   433  				Lineage: "2",
   434  				Serial:  2,
   435  			},
   436  		},
   437  		{
   438  			StateAgeEqual, true,
   439  			&State{
   440  				Lineage: "1",
   441  				Serial:  3,
   442  			},
   443  			&State{
   444  				Lineage: "2",
   445  				Serial:  2,
   446  			},
   447  		},
   448  	}
   449  
   450  	for i, tc := range cases {
   451  		result, err := tc.One.CompareAges(tc.Two)
   452  
   453  		if err != nil && !tc.Err {
   454  			t.Errorf(
   455  				"%d: got error, but want success\n\n%s\n\n%s",
   456  				i, tc.One, tc.Two,
   457  			)
   458  			continue
   459  		}
   460  
   461  		if err == nil && tc.Err {
   462  			t.Errorf(
   463  				"%d: got success, but want error\n\n%s\n\n%s",
   464  				i, tc.One, tc.Two,
   465  			)
   466  			continue
   467  		}
   468  
   469  		if result != tc.Result {
   470  			t.Errorf(
   471  				"%d: got result %d, but want %d\n\n%s\n\n%s",
   472  				i, result, tc.Result, tc.One, tc.Two,
   473  			)
   474  			continue
   475  		}
   476  	}
   477  }
   478  
   479  func TestStateSameLineage(t *testing.T) {
   480  	cases := []struct {
   481  		Result   bool
   482  		One, Two *State
   483  	}{
   484  		{
   485  			true,
   486  			&State{
   487  				Lineage: "1",
   488  			},
   489  			&State{
   490  				Lineage: "1",
   491  			},
   492  		},
   493  		{
   494  			// Empty lineage is compatible with all
   495  			true,
   496  			&State{
   497  				Lineage: "",
   498  			},
   499  			&State{
   500  				Lineage: "1",
   501  			},
   502  		},
   503  		{
   504  			// Empty lineage is compatible with all
   505  			true,
   506  			&State{
   507  				Lineage: "1",
   508  			},
   509  			&State{
   510  				Lineage: "",
   511  			},
   512  		},
   513  		{
   514  			false,
   515  			&State{
   516  				Lineage: "1",
   517  			},
   518  			&State{
   519  				Lineage: "2",
   520  			},
   521  		},
   522  	}
   523  
   524  	for i, tc := range cases {
   525  		result := tc.One.SameLineage(tc.Two)
   526  
   527  		if result != tc.Result {
   528  			t.Errorf(
   529  				"%d: got %v, but want %v\n\n%s\n\n%s",
   530  				i, result, tc.Result, tc.One, tc.Two,
   531  			)
   532  			continue
   533  		}
   534  	}
   535  }
   536  
   537  func TestStateIncrementSerialMaybe(t *testing.T) {
   538  	cases := map[string]struct {
   539  		S1, S2 *State
   540  		Serial int64
   541  	}{
   542  		"S2 is nil": {
   543  			&State{},
   544  			nil,
   545  			0,
   546  		},
   547  		"S2 is identical": {
   548  			&State{},
   549  			&State{},
   550  			0,
   551  		},
   552  		"S2 is different": {
   553  			&State{},
   554  			&State{
   555  				Modules: []*ModuleState{
   556  					&ModuleState{Path: rootModulePath},
   557  				},
   558  			},
   559  			1,
   560  		},
   561  		"S2 is different, but only via Instance Metadata": {
   562  			&State{
   563  				Serial: 3,
   564  				Modules: []*ModuleState{
   565  					&ModuleState{
   566  						Path: rootModulePath,
   567  						Resources: map[string]*ResourceState{
   568  							"test_instance.foo": &ResourceState{
   569  								Primary: &InstanceState{
   570  									Meta: map[string]string{},
   571  								},
   572  							},
   573  						},
   574  					},
   575  				},
   576  			},
   577  			&State{
   578  				Serial: 3,
   579  				Modules: []*ModuleState{
   580  					&ModuleState{
   581  						Path: rootModulePath,
   582  						Resources: map[string]*ResourceState{
   583  							"test_instance.foo": &ResourceState{
   584  								Primary: &InstanceState{
   585  									Meta: map[string]string{
   586  										"schema_version": "1",
   587  									},
   588  								},
   589  							},
   590  						},
   591  					},
   592  				},
   593  			},
   594  			4,
   595  		},
   596  		"S1 serial is higher": {
   597  			&State{Serial: 5},
   598  			&State{
   599  				Serial: 3,
   600  				Modules: []*ModuleState{
   601  					&ModuleState{Path: rootModulePath},
   602  				},
   603  			},
   604  			5,
   605  		},
   606  		"S2 has a different TFVersion": {
   607  			&State{TFVersion: "0.1"},
   608  			&State{TFVersion: "0.2"},
   609  			1,
   610  		},
   611  	}
   612  
   613  	for name, tc := range cases {
   614  		tc.S1.IncrementSerialMaybe(tc.S2)
   615  		if tc.S1.Serial != tc.Serial {
   616  			t.Fatalf("Bad: %s\nGot: %d", name, tc.S1.Serial)
   617  		}
   618  	}
   619  }
   620  
   621  func TestStateRemove(t *testing.T) {
   622  	cases := map[string]struct {
   623  		Address  string
   624  		One, Two *State
   625  	}{
   626  		"simple resource": {
   627  			"test_instance.foo",
   628  			&State{
   629  				Modules: []*ModuleState{
   630  					&ModuleState{
   631  						Path: rootModulePath,
   632  						Resources: map[string]*ResourceState{
   633  							"test_instance.foo": &ResourceState{
   634  								Type: "test_instance",
   635  								Primary: &InstanceState{
   636  									ID: "foo",
   637  								},
   638  							},
   639  
   640  							"test_instance.bar": &ResourceState{
   641  								Type: "test_instance",
   642  								Primary: &InstanceState{
   643  									ID: "foo",
   644  								},
   645  							},
   646  						},
   647  					},
   648  				},
   649  			},
   650  			&State{
   651  				Modules: []*ModuleState{
   652  					&ModuleState{
   653  						Path: rootModulePath,
   654  						Resources: map[string]*ResourceState{
   655  							"test_instance.bar": &ResourceState{
   656  								Type: "test_instance",
   657  								Primary: &InstanceState{
   658  									ID: "foo",
   659  								},
   660  							},
   661  						},
   662  					},
   663  				},
   664  			},
   665  		},
   666  
   667  		"single instance": {
   668  			"test_instance.foo.primary",
   669  			&State{
   670  				Modules: []*ModuleState{
   671  					&ModuleState{
   672  						Path: rootModulePath,
   673  						Resources: map[string]*ResourceState{
   674  							"test_instance.foo": &ResourceState{
   675  								Type: "test_instance",
   676  								Primary: &InstanceState{
   677  									ID: "foo",
   678  								},
   679  							},
   680  						},
   681  					},
   682  				},
   683  			},
   684  			&State{
   685  				Modules: []*ModuleState{
   686  					&ModuleState{
   687  						Path:      rootModulePath,
   688  						Resources: map[string]*ResourceState{},
   689  					},
   690  				},
   691  			},
   692  		},
   693  
   694  		"single instance in multi-count": {
   695  			"test_instance.foo[0]",
   696  			&State{
   697  				Modules: []*ModuleState{
   698  					&ModuleState{
   699  						Path: rootModulePath,
   700  						Resources: map[string]*ResourceState{
   701  							"test_instance.foo.0": &ResourceState{
   702  								Type: "test_instance",
   703  								Primary: &InstanceState{
   704  									ID: "foo",
   705  								},
   706  							},
   707  
   708  							"test_instance.foo.1": &ResourceState{
   709  								Type: "test_instance",
   710  								Primary: &InstanceState{
   711  									ID: "foo",
   712  								},
   713  							},
   714  						},
   715  					},
   716  				},
   717  			},
   718  			&State{
   719  				Modules: []*ModuleState{
   720  					&ModuleState{
   721  						Path: rootModulePath,
   722  						Resources: map[string]*ResourceState{
   723  							"test_instance.foo.1": &ResourceState{
   724  								Type: "test_instance",
   725  								Primary: &InstanceState{
   726  									ID: "foo",
   727  								},
   728  							},
   729  						},
   730  					},
   731  				},
   732  			},
   733  		},
   734  
   735  		"single resource, multi-count": {
   736  			"test_instance.foo",
   737  			&State{
   738  				Modules: []*ModuleState{
   739  					&ModuleState{
   740  						Path: rootModulePath,
   741  						Resources: map[string]*ResourceState{
   742  							"test_instance.foo.0": &ResourceState{
   743  								Type: "test_instance",
   744  								Primary: &InstanceState{
   745  									ID: "foo",
   746  								},
   747  							},
   748  
   749  							"test_instance.foo.1": &ResourceState{
   750  								Type: "test_instance",
   751  								Primary: &InstanceState{
   752  									ID: "foo",
   753  								},
   754  							},
   755  						},
   756  					},
   757  				},
   758  			},
   759  			&State{
   760  				Modules: []*ModuleState{
   761  					&ModuleState{
   762  						Path:      rootModulePath,
   763  						Resources: map[string]*ResourceState{},
   764  					},
   765  				},
   766  			},
   767  		},
   768  
   769  		"full module": {
   770  			"module.foo",
   771  			&State{
   772  				Modules: []*ModuleState{
   773  					&ModuleState{
   774  						Path: rootModulePath,
   775  						Resources: map[string]*ResourceState{
   776  							"test_instance.foo": &ResourceState{
   777  								Type: "test_instance",
   778  								Primary: &InstanceState{
   779  									ID: "foo",
   780  								},
   781  							},
   782  						},
   783  					},
   784  
   785  					&ModuleState{
   786  						Path: []string{"root", "foo"},
   787  						Resources: map[string]*ResourceState{
   788  							"test_instance.foo": &ResourceState{
   789  								Type: "test_instance",
   790  								Primary: &InstanceState{
   791  									ID: "foo",
   792  								},
   793  							},
   794  
   795  							"test_instance.bar": &ResourceState{
   796  								Type: "test_instance",
   797  								Primary: &InstanceState{
   798  									ID: "foo",
   799  								},
   800  							},
   801  						},
   802  					},
   803  				},
   804  			},
   805  			&State{
   806  				Modules: []*ModuleState{
   807  					&ModuleState{
   808  						Path: rootModulePath,
   809  						Resources: map[string]*ResourceState{
   810  							"test_instance.foo": &ResourceState{
   811  								Type: "test_instance",
   812  								Primary: &InstanceState{
   813  									ID: "foo",
   814  								},
   815  							},
   816  						},
   817  					},
   818  				},
   819  			},
   820  		},
   821  
   822  		"module and children": {
   823  			"module.foo",
   824  			&State{
   825  				Modules: []*ModuleState{
   826  					&ModuleState{
   827  						Path: rootModulePath,
   828  						Resources: map[string]*ResourceState{
   829  							"test_instance.foo": &ResourceState{
   830  								Type: "test_instance",
   831  								Primary: &InstanceState{
   832  									ID: "foo",
   833  								},
   834  							},
   835  						},
   836  					},
   837  
   838  					&ModuleState{
   839  						Path: []string{"root", "foo"},
   840  						Resources: map[string]*ResourceState{
   841  							"test_instance.foo": &ResourceState{
   842  								Type: "test_instance",
   843  								Primary: &InstanceState{
   844  									ID: "foo",
   845  								},
   846  							},
   847  
   848  							"test_instance.bar": &ResourceState{
   849  								Type: "test_instance",
   850  								Primary: &InstanceState{
   851  									ID: "foo",
   852  								},
   853  							},
   854  						},
   855  					},
   856  
   857  					&ModuleState{
   858  						Path: []string{"root", "foo", "bar"},
   859  						Resources: map[string]*ResourceState{
   860  							"test_instance.foo": &ResourceState{
   861  								Type: "test_instance",
   862  								Primary: &InstanceState{
   863  									ID: "foo",
   864  								},
   865  							},
   866  
   867  							"test_instance.bar": &ResourceState{
   868  								Type: "test_instance",
   869  								Primary: &InstanceState{
   870  									ID: "foo",
   871  								},
   872  							},
   873  						},
   874  					},
   875  				},
   876  			},
   877  			&State{
   878  				Modules: []*ModuleState{
   879  					&ModuleState{
   880  						Path: rootModulePath,
   881  						Resources: map[string]*ResourceState{
   882  							"test_instance.foo": &ResourceState{
   883  								Type: "test_instance",
   884  								Primary: &InstanceState{
   885  									ID: "foo",
   886  								},
   887  							},
   888  						},
   889  					},
   890  				},
   891  			},
   892  		},
   893  	}
   894  
   895  	for k, tc := range cases {
   896  		if err := tc.One.Remove(tc.Address); err != nil {
   897  			t.Fatalf("bad: %s\n\n%s", k, err)
   898  		}
   899  
   900  		if !tc.One.Equal(tc.Two) {
   901  			t.Fatalf("Bad: %s\n\n%s\n\n%s", k, tc.One.String(), tc.Two.String())
   902  		}
   903  	}
   904  }
   905  
   906  func TestResourceStateEqual(t *testing.T) {
   907  	cases := []struct {
   908  		Result   bool
   909  		One, Two *ResourceState
   910  	}{
   911  		// Different types
   912  		{
   913  			false,
   914  			&ResourceState{Type: "foo"},
   915  			&ResourceState{Type: "bar"},
   916  		},
   917  
   918  		// Different dependencies
   919  		{
   920  			false,
   921  			&ResourceState{Dependencies: []string{"foo"}},
   922  			&ResourceState{Dependencies: []string{"bar"}},
   923  		},
   924  
   925  		{
   926  			false,
   927  			&ResourceState{Dependencies: []string{"foo", "bar"}},
   928  			&ResourceState{Dependencies: []string{"foo"}},
   929  		},
   930  
   931  		{
   932  			true,
   933  			&ResourceState{Dependencies: []string{"bar", "foo"}},
   934  			&ResourceState{Dependencies: []string{"foo", "bar"}},
   935  		},
   936  
   937  		// Different primaries
   938  		{
   939  			false,
   940  			&ResourceState{Primary: nil},
   941  			&ResourceState{Primary: &InstanceState{ID: "foo"}},
   942  		},
   943  
   944  		{
   945  			true,
   946  			&ResourceState{Primary: &InstanceState{ID: "foo"}},
   947  			&ResourceState{Primary: &InstanceState{ID: "foo"}},
   948  		},
   949  
   950  		// Different tainted
   951  		{
   952  			false,
   953  			&ResourceState{
   954  				Primary: &InstanceState{
   955  					ID: "foo",
   956  				},
   957  			},
   958  			&ResourceState{
   959  				Primary: &InstanceState{
   960  					ID:      "foo",
   961  					Tainted: true,
   962  				},
   963  			},
   964  		},
   965  
   966  		{
   967  			true,
   968  			&ResourceState{
   969  				Primary: &InstanceState{
   970  					ID:      "foo",
   971  					Tainted: true,
   972  				},
   973  			},
   974  			&ResourceState{
   975  				Primary: &InstanceState{
   976  					ID:      "foo",
   977  					Tainted: true,
   978  				},
   979  			},
   980  		},
   981  	}
   982  
   983  	for i, tc := range cases {
   984  		if tc.One.Equal(tc.Two) != tc.Result {
   985  			t.Fatalf("Bad: %d\n\n%s\n\n%s", i, tc.One.String(), tc.Two.String())
   986  		}
   987  		if tc.Two.Equal(tc.One) != tc.Result {
   988  			t.Fatalf("Bad: %d\n\n%s\n\n%s", i, tc.One.String(), tc.Two.String())
   989  		}
   990  	}
   991  }
   992  
   993  func TestResourceStateTaint(t *testing.T) {
   994  	cases := map[string]struct {
   995  		Input  *ResourceState
   996  		Output *ResourceState
   997  	}{
   998  		"no primary": {
   999  			&ResourceState{},
  1000  			&ResourceState{},
  1001  		},
  1002  
  1003  		"primary, not tainted": {
  1004  			&ResourceState{
  1005  				Primary: &InstanceState{ID: "foo"},
  1006  			},
  1007  			&ResourceState{
  1008  				Primary: &InstanceState{
  1009  					ID:      "foo",
  1010  					Tainted: true,
  1011  				},
  1012  			},
  1013  		},
  1014  
  1015  		"primary, tainted": {
  1016  			&ResourceState{
  1017  				Primary: &InstanceState{
  1018  					ID:      "foo",
  1019  					Tainted: true,
  1020  				},
  1021  			},
  1022  			&ResourceState{
  1023  				Primary: &InstanceState{
  1024  					ID:      "foo",
  1025  					Tainted: true,
  1026  				},
  1027  			},
  1028  		},
  1029  	}
  1030  
  1031  	for k, tc := range cases {
  1032  		tc.Input.Taint()
  1033  		if !reflect.DeepEqual(tc.Input, tc.Output) {
  1034  			t.Fatalf(
  1035  				"Failure: %s\n\nExpected: %#v\n\nGot: %#v",
  1036  				k, tc.Output, tc.Input)
  1037  		}
  1038  	}
  1039  }
  1040  
  1041  func TestResourceStateUntaint(t *testing.T) {
  1042  	cases := map[string]struct {
  1043  		Input          *ResourceState
  1044  		ExpectedOutput *ResourceState
  1045  	}{
  1046  		"no primary, err": {
  1047  			Input:          &ResourceState{},
  1048  			ExpectedOutput: &ResourceState{},
  1049  		},
  1050  
  1051  		"primary, not tainted": {
  1052  			Input: &ResourceState{
  1053  				Primary: &InstanceState{ID: "foo"},
  1054  			},
  1055  			ExpectedOutput: &ResourceState{
  1056  				Primary: &InstanceState{ID: "foo"},
  1057  			},
  1058  		},
  1059  		"primary, tainted": {
  1060  			Input: &ResourceState{
  1061  				Primary: &InstanceState{
  1062  					ID:      "foo",
  1063  					Tainted: true,
  1064  				},
  1065  			},
  1066  			ExpectedOutput: &ResourceState{
  1067  				Primary: &InstanceState{ID: "foo"},
  1068  			},
  1069  		},
  1070  	}
  1071  
  1072  	for k, tc := range cases {
  1073  		tc.Input.Untaint()
  1074  		if !reflect.DeepEqual(tc.Input, tc.ExpectedOutput) {
  1075  			t.Fatalf(
  1076  				"Failure: %s\n\nExpected: %#v\n\nGot: %#v",
  1077  				k, tc.ExpectedOutput, tc.Input)
  1078  		}
  1079  	}
  1080  }
  1081  
  1082  func TestInstanceStateEmpty(t *testing.T) {
  1083  	cases := map[string]struct {
  1084  		In     *InstanceState
  1085  		Result bool
  1086  	}{
  1087  		"nil is empty": {
  1088  			nil,
  1089  			true,
  1090  		},
  1091  		"non-nil but without ID is empty": {
  1092  			&InstanceState{},
  1093  			true,
  1094  		},
  1095  		"with ID is not empty": {
  1096  			&InstanceState{
  1097  				ID: "i-abc123",
  1098  			},
  1099  			false,
  1100  		},
  1101  	}
  1102  
  1103  	for tn, tc := range cases {
  1104  		if tc.In.Empty() != tc.Result {
  1105  			t.Fatalf("%q expected %#v to be empty: %#v", tn, tc.In, tc.Result)
  1106  		}
  1107  	}
  1108  }
  1109  
  1110  func TestInstanceStateEqual(t *testing.T) {
  1111  	cases := []struct {
  1112  		Result   bool
  1113  		One, Two *InstanceState
  1114  	}{
  1115  		// Nils
  1116  		{
  1117  			false,
  1118  			nil,
  1119  			&InstanceState{},
  1120  		},
  1121  
  1122  		{
  1123  			false,
  1124  			&InstanceState{},
  1125  			nil,
  1126  		},
  1127  
  1128  		// Different IDs
  1129  		{
  1130  			false,
  1131  			&InstanceState{ID: "foo"},
  1132  			&InstanceState{ID: "bar"},
  1133  		},
  1134  
  1135  		// Different Attributes
  1136  		{
  1137  			false,
  1138  			&InstanceState{Attributes: map[string]string{"foo": "bar"}},
  1139  			&InstanceState{Attributes: map[string]string{"foo": "baz"}},
  1140  		},
  1141  
  1142  		// Different Attribute keys
  1143  		{
  1144  			false,
  1145  			&InstanceState{Attributes: map[string]string{"foo": "bar"}},
  1146  			&InstanceState{Attributes: map[string]string{"bar": "baz"}},
  1147  		},
  1148  
  1149  		{
  1150  			false,
  1151  			&InstanceState{Attributes: map[string]string{"bar": "baz"}},
  1152  			&InstanceState{Attributes: map[string]string{"foo": "bar"}},
  1153  		},
  1154  	}
  1155  
  1156  	for i, tc := range cases {
  1157  		if tc.One.Equal(tc.Two) != tc.Result {
  1158  			t.Fatalf("Bad: %d\n\n%s\n\n%s", i, tc.One.String(), tc.Two.String())
  1159  		}
  1160  	}
  1161  }
  1162  
  1163  func TestStateEmpty(t *testing.T) {
  1164  	cases := []struct {
  1165  		In     *State
  1166  		Result bool
  1167  	}{
  1168  		{
  1169  			nil,
  1170  			true,
  1171  		},
  1172  		{
  1173  			&State{},
  1174  			true,
  1175  		},
  1176  		{
  1177  			&State{
  1178  				Remote: &RemoteState{Type: "foo"},
  1179  			},
  1180  			true,
  1181  		},
  1182  		{
  1183  			&State{
  1184  				Modules: []*ModuleState{
  1185  					&ModuleState{},
  1186  				},
  1187  			},
  1188  			false,
  1189  		},
  1190  	}
  1191  
  1192  	for i, tc := range cases {
  1193  		if tc.In.Empty() != tc.Result {
  1194  			t.Fatalf("bad %d %#v:\n\n%#v", i, tc.Result, tc.In)
  1195  		}
  1196  	}
  1197  }
  1198  
  1199  func TestStateFromFutureTerraform(t *testing.T) {
  1200  	cases := []struct {
  1201  		In     string
  1202  		Result bool
  1203  	}{
  1204  		{
  1205  			"",
  1206  			false,
  1207  		},
  1208  		{
  1209  			"0.1",
  1210  			false,
  1211  		},
  1212  		{
  1213  			"999.15.1",
  1214  			true,
  1215  		},
  1216  	}
  1217  
  1218  	for _, tc := range cases {
  1219  		state := &State{TFVersion: tc.In}
  1220  		actual := state.FromFutureTerraform()
  1221  		if actual != tc.Result {
  1222  			t.Fatalf("%s: bad: %v", tc.In, actual)
  1223  		}
  1224  	}
  1225  }
  1226  
  1227  func TestStateIsRemote(t *testing.T) {
  1228  	cases := []struct {
  1229  		In     *State
  1230  		Result bool
  1231  	}{
  1232  		{
  1233  			nil,
  1234  			false,
  1235  		},
  1236  		{
  1237  			&State{},
  1238  			false,
  1239  		},
  1240  		{
  1241  			&State{
  1242  				Remote: &RemoteState{Type: "foo"},
  1243  			},
  1244  			true,
  1245  		},
  1246  	}
  1247  
  1248  	for i, tc := range cases {
  1249  		if tc.In.IsRemote() != tc.Result {
  1250  			t.Fatalf("bad %d %#v:\n\n%#v", i, tc.Result, tc.In)
  1251  		}
  1252  	}
  1253  }
  1254  
  1255  func TestInstanceState_MergeDiff(t *testing.T) {
  1256  	is := InstanceState{
  1257  		ID: "foo",
  1258  		Attributes: map[string]string{
  1259  			"foo":  "bar",
  1260  			"port": "8000",
  1261  		},
  1262  	}
  1263  
  1264  	diff := &InstanceDiff{
  1265  		Attributes: map[string]*ResourceAttrDiff{
  1266  			"foo": &ResourceAttrDiff{
  1267  				Old: "bar",
  1268  				New: "baz",
  1269  			},
  1270  			"bar": &ResourceAttrDiff{
  1271  				Old: "",
  1272  				New: "foo",
  1273  			},
  1274  			"baz": &ResourceAttrDiff{
  1275  				Old:         "",
  1276  				New:         "foo",
  1277  				NewComputed: true,
  1278  			},
  1279  			"port": &ResourceAttrDiff{
  1280  				NewRemoved: true,
  1281  			},
  1282  		},
  1283  	}
  1284  
  1285  	is2 := is.MergeDiff(diff)
  1286  
  1287  	expected := map[string]string{
  1288  		"foo": "baz",
  1289  		"bar": "foo",
  1290  		"baz": config.UnknownVariableValue,
  1291  	}
  1292  
  1293  	if !reflect.DeepEqual(expected, is2.Attributes) {
  1294  		t.Fatalf("bad: %#v", is2.Attributes)
  1295  	}
  1296  }
  1297  
  1298  func TestInstanceState_MergeDiff_nil(t *testing.T) {
  1299  	var is *InstanceState
  1300  
  1301  	diff := &InstanceDiff{
  1302  		Attributes: map[string]*ResourceAttrDiff{
  1303  			"foo": &ResourceAttrDiff{
  1304  				Old: "",
  1305  				New: "baz",
  1306  			},
  1307  		},
  1308  	}
  1309  
  1310  	is2 := is.MergeDiff(diff)
  1311  
  1312  	expected := map[string]string{
  1313  		"foo": "baz",
  1314  	}
  1315  
  1316  	if !reflect.DeepEqual(expected, is2.Attributes) {
  1317  		t.Fatalf("bad: %#v", is2.Attributes)
  1318  	}
  1319  }
  1320  
  1321  func TestInstanceState_MergeDiff_nilDiff(t *testing.T) {
  1322  	is := InstanceState{
  1323  		ID: "foo",
  1324  		Attributes: map[string]string{
  1325  			"foo": "bar",
  1326  		},
  1327  	}
  1328  
  1329  	is2 := is.MergeDiff(nil)
  1330  
  1331  	expected := map[string]string{
  1332  		"foo": "bar",
  1333  	}
  1334  
  1335  	if !reflect.DeepEqual(expected, is2.Attributes) {
  1336  		t.Fatalf("bad: %#v", is2.Attributes)
  1337  	}
  1338  }
  1339  
  1340  func TestReadWriteState(t *testing.T) {
  1341  	state := &State{
  1342  		Serial:  9,
  1343  		Lineage: "5d1ad1a1-4027-4665-a908-dbe6adff11d8",
  1344  		Remote: &RemoteState{
  1345  			Type: "http",
  1346  			Config: map[string]string{
  1347  				"url": "http://my-cool-server.com/",
  1348  			},
  1349  		},
  1350  		Modules: []*ModuleState{
  1351  			&ModuleState{
  1352  				Path: rootModulePath,
  1353  				Dependencies: []string{
  1354  					"aws_instance.bar",
  1355  				},
  1356  				Resources: map[string]*ResourceState{
  1357  					"foo": &ResourceState{
  1358  						Primary: &InstanceState{
  1359  							ID: "bar",
  1360  							Ephemeral: EphemeralState{
  1361  								ConnInfo: map[string]string{
  1362  									"type":     "ssh",
  1363  									"user":     "root",
  1364  									"password": "supersecret",
  1365  								},
  1366  							},
  1367  						},
  1368  					},
  1369  				},
  1370  			},
  1371  		},
  1372  	}
  1373  	state.init()
  1374  
  1375  	buf := new(bytes.Buffer)
  1376  	if err := WriteState(state, buf); err != nil {
  1377  		t.Fatalf("err: %s", err)
  1378  	}
  1379  
  1380  	// Verify that the version and serial are set
  1381  	if state.Version != StateVersion {
  1382  		t.Fatalf("bad version number: %d", state.Version)
  1383  	}
  1384  
  1385  	actual, err := ReadState(buf)
  1386  	if err != nil {
  1387  		t.Fatalf("err: %s", err)
  1388  	}
  1389  
  1390  	// ReadState should not restore sensitive information!
  1391  	mod := state.RootModule()
  1392  	mod.Resources["foo"].Primary.Ephemeral = EphemeralState{}
  1393  	mod.Resources["foo"].Primary.Ephemeral.init()
  1394  
  1395  	if !reflect.DeepEqual(actual, state) {
  1396  		t.Logf("expected:\n%#v", state)
  1397  		t.Fatalf("got:\n%#v", actual)
  1398  	}
  1399  }
  1400  
  1401  func TestReadStateNewVersion(t *testing.T) {
  1402  	type out struct {
  1403  		Version int
  1404  	}
  1405  
  1406  	buf, err := json.Marshal(&out{StateVersion + 1})
  1407  	if err != nil {
  1408  		t.Fatalf("err: %v", err)
  1409  	}
  1410  
  1411  	s, err := ReadState(bytes.NewReader(buf))
  1412  	if s != nil {
  1413  		t.Fatalf("unexpected: %#v", s)
  1414  	}
  1415  	if !strings.Contains(err.Error(), "does not support state version") {
  1416  		t.Fatalf("err: %v", err)
  1417  	}
  1418  }
  1419  
  1420  func TestReadStateTFVersion(t *testing.T) {
  1421  	type tfVersion struct {
  1422  		Version   int    `json:"version"`
  1423  		TFVersion string `json:"terraform_version"`
  1424  	}
  1425  
  1426  	cases := []struct {
  1427  		Written string
  1428  		Read    string
  1429  		Err     bool
  1430  	}{
  1431  		{
  1432  			"0.0.0",
  1433  			"0.0.0",
  1434  			false,
  1435  		},
  1436  		{
  1437  			"",
  1438  			"",
  1439  			false,
  1440  		},
  1441  		{
  1442  			"bad",
  1443  			"",
  1444  			true,
  1445  		},
  1446  	}
  1447  
  1448  	for _, tc := range cases {
  1449  		buf, err := json.Marshal(&tfVersion{
  1450  			Version:   2,
  1451  			TFVersion: tc.Written,
  1452  		})
  1453  		if err != nil {
  1454  			t.Fatalf("err: %v", err)
  1455  		}
  1456  
  1457  		s, err := ReadState(bytes.NewReader(buf))
  1458  		if (err != nil) != tc.Err {
  1459  			t.Fatalf("%s: err: %s", tc.Written, err)
  1460  		}
  1461  		if err != nil {
  1462  			continue
  1463  		}
  1464  
  1465  		if s.TFVersion != tc.Read {
  1466  			t.Fatalf("%s: bad: %s", tc.Written, s.TFVersion)
  1467  		}
  1468  	}
  1469  }
  1470  
  1471  func TestWriteStateTFVersion(t *testing.T) {
  1472  	cases := []struct {
  1473  		Write string
  1474  		Read  string
  1475  		Err   bool
  1476  	}{
  1477  		{
  1478  			"0.0.0",
  1479  			"0.0.0",
  1480  			false,
  1481  		},
  1482  		{
  1483  			"",
  1484  			"",
  1485  			false,
  1486  		},
  1487  		{
  1488  			"bad",
  1489  			"",
  1490  			true,
  1491  		},
  1492  	}
  1493  
  1494  	for _, tc := range cases {
  1495  		var buf bytes.Buffer
  1496  		err := WriteState(&State{TFVersion: tc.Write}, &buf)
  1497  		if (err != nil) != tc.Err {
  1498  			t.Fatalf("%s: err: %s", tc.Write, err)
  1499  		}
  1500  		if err != nil {
  1501  			continue
  1502  		}
  1503  
  1504  		s, err := ReadState(&buf)
  1505  		if err != nil {
  1506  			t.Fatalf("%s: err: %s", tc.Write, err)
  1507  		}
  1508  
  1509  		if s.TFVersion != tc.Read {
  1510  			t.Fatalf("%s: bad: %s", tc.Write, s.TFVersion)
  1511  		}
  1512  	}
  1513  }
  1514  
  1515  func TestParseResourceStateKey(t *testing.T) {
  1516  	cases := []struct {
  1517  		Input       string
  1518  		Expected    *ResourceStateKey
  1519  		ExpectedErr bool
  1520  	}{
  1521  		{
  1522  			Input: "aws_instance.foo.3",
  1523  			Expected: &ResourceStateKey{
  1524  				Mode:  config.ManagedResourceMode,
  1525  				Type:  "aws_instance",
  1526  				Name:  "foo",
  1527  				Index: 3,
  1528  			},
  1529  		},
  1530  		{
  1531  			Input: "aws_instance.foo.0",
  1532  			Expected: &ResourceStateKey{
  1533  				Mode:  config.ManagedResourceMode,
  1534  				Type:  "aws_instance",
  1535  				Name:  "foo",
  1536  				Index: 0,
  1537  			},
  1538  		},
  1539  		{
  1540  			Input: "aws_instance.foo",
  1541  			Expected: &ResourceStateKey{
  1542  				Mode:  config.ManagedResourceMode,
  1543  				Type:  "aws_instance",
  1544  				Name:  "foo",
  1545  				Index: -1,
  1546  			},
  1547  		},
  1548  		{
  1549  			Input: "data.aws_ami.foo",
  1550  			Expected: &ResourceStateKey{
  1551  				Mode:  config.DataResourceMode,
  1552  				Type:  "aws_ami",
  1553  				Name:  "foo",
  1554  				Index: -1,
  1555  			},
  1556  		},
  1557  		{
  1558  			Input:       "aws_instance.foo.malformed",
  1559  			ExpectedErr: true,
  1560  		},
  1561  		{
  1562  			Input:       "aws_instance.foo.malformedwithnumber.123",
  1563  			ExpectedErr: true,
  1564  		},
  1565  		{
  1566  			Input:       "malformed",
  1567  			ExpectedErr: true,
  1568  		},
  1569  	}
  1570  	for _, tc := range cases {
  1571  		rsk, err := ParseResourceStateKey(tc.Input)
  1572  		if rsk != nil && tc.Expected != nil && !rsk.Equal(tc.Expected) {
  1573  			t.Fatalf("%s: expected %s, got %s", tc.Input, tc.Expected, rsk)
  1574  		}
  1575  		if (err != nil) != tc.ExpectedErr {
  1576  			t.Fatalf("%s: expected err: %t, got %s", tc.Input, tc.ExpectedErr, err)
  1577  		}
  1578  	}
  1579  }