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