github.com/inge4pres/terraform@v0.7.5-0.20160930053151-bd083f84f376/terraform/state_test.go (about)

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