github.com/mikesimons/terraform@v0.6.13-0.20160304043642-f11448c69214/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 TestStateModuleOrphans(t *testing.T) {
    80  	state := &State{
    81  		Modules: []*ModuleState{
    82  			&ModuleState{
    83  				Path: RootModulePath,
    84  			},
    85  			&ModuleState{
    86  				Path: []string{RootModuleName, "foo"},
    87  			},
    88  			&ModuleState{
    89  				Path: []string{RootModuleName, "bar"},
    90  			},
    91  		},
    92  	}
    93  
    94  	config := testModule(t, "state-module-orphans").Config()
    95  	actual := state.ModuleOrphans(RootModulePath, config)
    96  	expected := [][]string{
    97  		[]string{RootModuleName, "foo"},
    98  	}
    99  
   100  	if !reflect.DeepEqual(actual, expected) {
   101  		t.Fatalf("bad: %#v", actual)
   102  	}
   103  }
   104  
   105  func TestStateModuleOrphans_nested(t *testing.T) {
   106  	state := &State{
   107  		Modules: []*ModuleState{
   108  			&ModuleState{
   109  				Path: RootModulePath,
   110  			},
   111  			&ModuleState{
   112  				Path: []string{RootModuleName, "foo", "bar"},
   113  			},
   114  		},
   115  	}
   116  
   117  	actual := state.ModuleOrphans(RootModulePath, nil)
   118  	expected := [][]string{
   119  		[]string{RootModuleName, "foo"},
   120  	}
   121  
   122  	if !reflect.DeepEqual(actual, expected) {
   123  		t.Fatalf("bad: %#v", actual)
   124  	}
   125  }
   126  
   127  func TestStateModuleOrphans_nilConfig(t *testing.T) {
   128  	state := &State{
   129  		Modules: []*ModuleState{
   130  			&ModuleState{
   131  				Path: RootModulePath,
   132  			},
   133  			&ModuleState{
   134  				Path: []string{RootModuleName, "foo"},
   135  			},
   136  			&ModuleState{
   137  				Path: []string{RootModuleName, "bar"},
   138  			},
   139  		},
   140  	}
   141  
   142  	actual := state.ModuleOrphans(RootModulePath, nil)
   143  	expected := [][]string{
   144  		[]string{RootModuleName, "foo"},
   145  		[]string{RootModuleName, "bar"},
   146  	}
   147  
   148  	if !reflect.DeepEqual(actual, expected) {
   149  		t.Fatalf("bad: %#v", actual)
   150  	}
   151  }
   152  
   153  func TestStateModuleOrphans_deepNestedNilConfig(t *testing.T) {
   154  	state := &State{
   155  		Modules: []*ModuleState{
   156  			&ModuleState{
   157  				Path: RootModulePath,
   158  			},
   159  			&ModuleState{
   160  				Path: []string{RootModuleName, "parent", "childfoo"},
   161  			},
   162  			&ModuleState{
   163  				Path: []string{RootModuleName, "parent", "childbar"},
   164  			},
   165  		},
   166  	}
   167  
   168  	actual := state.ModuleOrphans(RootModulePath, nil)
   169  	expected := [][]string{
   170  		[]string{RootModuleName, "parent"},
   171  	}
   172  
   173  	if !reflect.DeepEqual(actual, expected) {
   174  		t.Fatalf("bad: %#v", actual)
   175  	}
   176  }
   177  
   178  func TestStateEqual(t *testing.T) {
   179  	cases := []struct {
   180  		Result   bool
   181  		One, Two *State
   182  	}{
   183  		// Nils
   184  		{
   185  			false,
   186  			nil,
   187  			&State{Version: 2},
   188  		},
   189  
   190  		{
   191  			true,
   192  			nil,
   193  			nil,
   194  		},
   195  
   196  		// Different versions
   197  		{
   198  			false,
   199  			&State{Version: 5},
   200  			&State{Version: 2},
   201  		},
   202  
   203  		// Different modules
   204  		{
   205  			false,
   206  			&State{
   207  				Modules: []*ModuleState{
   208  					&ModuleState{
   209  						Path: RootModulePath,
   210  					},
   211  				},
   212  			},
   213  			&State{},
   214  		},
   215  
   216  		{
   217  			true,
   218  			&State{
   219  				Modules: []*ModuleState{
   220  					&ModuleState{
   221  						Path: RootModulePath,
   222  					},
   223  				},
   224  			},
   225  			&State{
   226  				Modules: []*ModuleState{
   227  					&ModuleState{
   228  						Path: RootModulePath,
   229  					},
   230  				},
   231  			},
   232  		},
   233  
   234  		// Meta differs
   235  		{
   236  			false,
   237  			&State{
   238  				Modules: []*ModuleState{
   239  					&ModuleState{
   240  						Path: rootModulePath,
   241  						Resources: map[string]*ResourceState{
   242  							"test_instance.foo": &ResourceState{
   243  								Primary: &InstanceState{
   244  									Meta: map[string]string{
   245  										"schema_version": "1",
   246  									},
   247  								},
   248  							},
   249  						},
   250  					},
   251  				},
   252  			},
   253  			&State{
   254  				Modules: []*ModuleState{
   255  					&ModuleState{
   256  						Path: rootModulePath,
   257  						Resources: map[string]*ResourceState{
   258  							"test_instance.foo": &ResourceState{
   259  								Primary: &InstanceState{
   260  									Meta: map[string]string{
   261  										"schema_version": "2",
   262  									},
   263  								},
   264  							},
   265  						},
   266  					},
   267  				},
   268  			},
   269  		},
   270  	}
   271  
   272  	for i, tc := range cases {
   273  		if tc.One.Equal(tc.Two) != tc.Result {
   274  			t.Fatalf("Bad: %d\n\n%s\n\n%s", i, tc.One.String(), tc.Two.String())
   275  		}
   276  		if tc.Two.Equal(tc.One) != tc.Result {
   277  			t.Fatalf("Bad: %d\n\n%s\n\n%s", i, tc.One.String(), tc.Two.String())
   278  		}
   279  	}
   280  }
   281  
   282  func TestStateIncrementSerialMaybe(t *testing.T) {
   283  	cases := map[string]struct {
   284  		S1, S2 *State
   285  		Serial int64
   286  	}{
   287  		"S2 is nil": {
   288  			&State{},
   289  			nil,
   290  			0,
   291  		},
   292  		"S2 is identical": {
   293  			&State{},
   294  			&State{},
   295  			0,
   296  		},
   297  		"S2 is different": {
   298  			&State{},
   299  			&State{
   300  				Modules: []*ModuleState{
   301  					&ModuleState{Path: rootModulePath},
   302  				},
   303  			},
   304  			1,
   305  		},
   306  		"S2 is different, but only via Instance Metadata": {
   307  			&State{
   308  				Serial: 3,
   309  				Modules: []*ModuleState{
   310  					&ModuleState{
   311  						Path: rootModulePath,
   312  						Resources: map[string]*ResourceState{
   313  							"test_instance.foo": &ResourceState{
   314  								Primary: &InstanceState{
   315  									Meta: map[string]string{},
   316  								},
   317  							},
   318  						},
   319  					},
   320  				},
   321  			},
   322  			&State{
   323  				Serial: 3,
   324  				Modules: []*ModuleState{
   325  					&ModuleState{
   326  						Path: rootModulePath,
   327  						Resources: map[string]*ResourceState{
   328  							"test_instance.foo": &ResourceState{
   329  								Primary: &InstanceState{
   330  									Meta: map[string]string{
   331  										"schema_version": "1",
   332  									},
   333  								},
   334  							},
   335  						},
   336  					},
   337  				},
   338  			},
   339  			4,
   340  		},
   341  		"S1 serial is higher": {
   342  			&State{Serial: 5},
   343  			&State{
   344  				Serial: 3,
   345  				Modules: []*ModuleState{
   346  					&ModuleState{Path: rootModulePath},
   347  				},
   348  			},
   349  			5,
   350  		},
   351  	}
   352  
   353  	for name, tc := range cases {
   354  		tc.S1.IncrementSerialMaybe(tc.S2)
   355  		if tc.S1.Serial != tc.Serial {
   356  			t.Fatalf("Bad: %s\nGot: %d", name, tc.S1.Serial)
   357  		}
   358  	}
   359  }
   360  
   361  func TestResourceStateEqual(t *testing.T) {
   362  	cases := []struct {
   363  		Result   bool
   364  		One, Two *ResourceState
   365  	}{
   366  		// Different types
   367  		{
   368  			false,
   369  			&ResourceState{Type: "foo"},
   370  			&ResourceState{Type: "bar"},
   371  		},
   372  
   373  		// Different dependencies
   374  		{
   375  			false,
   376  			&ResourceState{Dependencies: []string{"foo"}},
   377  			&ResourceState{Dependencies: []string{"bar"}},
   378  		},
   379  
   380  		{
   381  			false,
   382  			&ResourceState{Dependencies: []string{"foo", "bar"}},
   383  			&ResourceState{Dependencies: []string{"foo"}},
   384  		},
   385  
   386  		{
   387  			true,
   388  			&ResourceState{Dependencies: []string{"bar", "foo"}},
   389  			&ResourceState{Dependencies: []string{"foo", "bar"}},
   390  		},
   391  
   392  		// Different primaries
   393  		{
   394  			false,
   395  			&ResourceState{Primary: nil},
   396  			&ResourceState{Primary: &InstanceState{ID: "foo"}},
   397  		},
   398  
   399  		{
   400  			true,
   401  			&ResourceState{Primary: &InstanceState{ID: "foo"}},
   402  			&ResourceState{Primary: &InstanceState{ID: "foo"}},
   403  		},
   404  
   405  		// Different tainted
   406  		{
   407  			false,
   408  			&ResourceState{
   409  				Tainted: nil,
   410  			},
   411  			&ResourceState{
   412  				Tainted: []*InstanceState{
   413  					&InstanceState{ID: "foo"},
   414  				},
   415  			},
   416  		},
   417  
   418  		{
   419  			true,
   420  			&ResourceState{
   421  				Tainted: []*InstanceState{
   422  					&InstanceState{ID: "foo"},
   423  				},
   424  			},
   425  			&ResourceState{
   426  				Tainted: []*InstanceState{
   427  					&InstanceState{ID: "foo"},
   428  				},
   429  			},
   430  		},
   431  
   432  		{
   433  			true,
   434  			&ResourceState{
   435  				Tainted: []*InstanceState{
   436  					&InstanceState{ID: "foo"},
   437  					nil,
   438  				},
   439  			},
   440  			&ResourceState{
   441  				Tainted: []*InstanceState{
   442  					&InstanceState{ID: "foo"},
   443  				},
   444  			},
   445  		},
   446  	}
   447  
   448  	for i, tc := range cases {
   449  		if tc.One.Equal(tc.Two) != tc.Result {
   450  			t.Fatalf("Bad: %d\n\n%s\n\n%s", i, tc.One.String(), tc.Two.String())
   451  		}
   452  		if tc.Two.Equal(tc.One) != tc.Result {
   453  			t.Fatalf("Bad: %d\n\n%s\n\n%s", i, tc.One.String(), tc.Two.String())
   454  		}
   455  	}
   456  }
   457  
   458  func TestResourceStateTaint(t *testing.T) {
   459  	cases := map[string]struct {
   460  		Input  *ResourceState
   461  		Output *ResourceState
   462  	}{
   463  		"no primary": {
   464  			&ResourceState{},
   465  			&ResourceState{},
   466  		},
   467  
   468  		"primary, no tainted": {
   469  			&ResourceState{
   470  				Primary: &InstanceState{ID: "foo"},
   471  			},
   472  			&ResourceState{
   473  				Tainted: []*InstanceState{
   474  					&InstanceState{ID: "foo"},
   475  				},
   476  			},
   477  		},
   478  
   479  		"primary, with tainted": {
   480  			&ResourceState{
   481  				Primary: &InstanceState{ID: "foo"},
   482  				Tainted: []*InstanceState{
   483  					&InstanceState{ID: "bar"},
   484  				},
   485  			},
   486  			&ResourceState{
   487  				Tainted: []*InstanceState{
   488  					&InstanceState{ID: "bar"},
   489  					&InstanceState{ID: "foo"},
   490  				},
   491  			},
   492  		},
   493  	}
   494  
   495  	for k, tc := range cases {
   496  		tc.Input.Taint()
   497  		if !reflect.DeepEqual(tc.Input, tc.Output) {
   498  			t.Fatalf(
   499  				"Failure: %s\n\nExpected: %#v\n\nGot: %#v",
   500  				k, tc.Output, tc.Input)
   501  		}
   502  	}
   503  }
   504  
   505  func TestInstanceStateEmpty(t *testing.T) {
   506  	cases := map[string]struct {
   507  		In     *InstanceState
   508  		Result bool
   509  	}{
   510  		"nil is empty": {
   511  			nil,
   512  			true,
   513  		},
   514  		"non-nil but without ID is empty": {
   515  			&InstanceState{},
   516  			true,
   517  		},
   518  		"with ID is not empty": {
   519  			&InstanceState{
   520  				ID: "i-abc123",
   521  			},
   522  			false,
   523  		},
   524  	}
   525  
   526  	for tn, tc := range cases {
   527  		if tc.In.Empty() != tc.Result {
   528  			t.Fatalf("%q expected %#v to be empty: %#v", tn, tc.In, tc.Result)
   529  		}
   530  	}
   531  }
   532  
   533  func TestInstanceStateEqual(t *testing.T) {
   534  	cases := []struct {
   535  		Result   bool
   536  		One, Two *InstanceState
   537  	}{
   538  		// Nils
   539  		{
   540  			false,
   541  			nil,
   542  			&InstanceState{},
   543  		},
   544  
   545  		{
   546  			false,
   547  			&InstanceState{},
   548  			nil,
   549  		},
   550  
   551  		// Different IDs
   552  		{
   553  			false,
   554  			&InstanceState{ID: "foo"},
   555  			&InstanceState{ID: "bar"},
   556  		},
   557  
   558  		// Different Attributes
   559  		{
   560  			false,
   561  			&InstanceState{Attributes: map[string]string{"foo": "bar"}},
   562  			&InstanceState{Attributes: map[string]string{"foo": "baz"}},
   563  		},
   564  
   565  		// Different Attribute keys
   566  		{
   567  			false,
   568  			&InstanceState{Attributes: map[string]string{"foo": "bar"}},
   569  			&InstanceState{Attributes: map[string]string{"bar": "baz"}},
   570  		},
   571  
   572  		{
   573  			false,
   574  			&InstanceState{Attributes: map[string]string{"bar": "baz"}},
   575  			&InstanceState{Attributes: map[string]string{"foo": "bar"}},
   576  		},
   577  	}
   578  
   579  	for i, tc := range cases {
   580  		if tc.One.Equal(tc.Two) != tc.Result {
   581  			t.Fatalf("Bad: %d\n\n%s\n\n%s", i, tc.One.String(), tc.Two.String())
   582  		}
   583  	}
   584  }
   585  
   586  func TestStateEmpty(t *testing.T) {
   587  	cases := []struct {
   588  		In     *State
   589  		Result bool
   590  	}{
   591  		{
   592  			nil,
   593  			true,
   594  		},
   595  		{
   596  			&State{},
   597  			true,
   598  		},
   599  		{
   600  			&State{
   601  				Remote: &RemoteState{Type: "foo"},
   602  			},
   603  			true,
   604  		},
   605  		{
   606  			&State{
   607  				Modules: []*ModuleState{
   608  					&ModuleState{},
   609  				},
   610  			},
   611  			false,
   612  		},
   613  	}
   614  
   615  	for i, tc := range cases {
   616  		if tc.In.Empty() != tc.Result {
   617  			t.Fatalf("bad %d %#v:\n\n%#v", i, tc.Result, tc.In)
   618  		}
   619  	}
   620  }
   621  
   622  func TestStateIsRemote(t *testing.T) {
   623  	cases := []struct {
   624  		In     *State
   625  		Result bool
   626  	}{
   627  		{
   628  			nil,
   629  			false,
   630  		},
   631  		{
   632  			&State{},
   633  			false,
   634  		},
   635  		{
   636  			&State{
   637  				Remote: &RemoteState{Type: "foo"},
   638  			},
   639  			true,
   640  		},
   641  	}
   642  
   643  	for i, tc := range cases {
   644  		if tc.In.IsRemote() != tc.Result {
   645  			t.Fatalf("bad %d %#v:\n\n%#v", i, tc.Result, tc.In)
   646  		}
   647  	}
   648  }
   649  
   650  func TestInstanceState_MergeDiff(t *testing.T) {
   651  	is := InstanceState{
   652  		ID: "foo",
   653  		Attributes: map[string]string{
   654  			"foo":  "bar",
   655  			"port": "8000",
   656  		},
   657  	}
   658  
   659  	diff := &InstanceDiff{
   660  		Attributes: map[string]*ResourceAttrDiff{
   661  			"foo": &ResourceAttrDiff{
   662  				Old: "bar",
   663  				New: "baz",
   664  			},
   665  			"bar": &ResourceAttrDiff{
   666  				Old: "",
   667  				New: "foo",
   668  			},
   669  			"baz": &ResourceAttrDiff{
   670  				Old:         "",
   671  				New:         "foo",
   672  				NewComputed: true,
   673  			},
   674  			"port": &ResourceAttrDiff{
   675  				NewRemoved: true,
   676  			},
   677  		},
   678  	}
   679  
   680  	is2 := is.MergeDiff(diff)
   681  
   682  	expected := map[string]string{
   683  		"foo": "baz",
   684  		"bar": "foo",
   685  		"baz": config.UnknownVariableValue,
   686  	}
   687  
   688  	if !reflect.DeepEqual(expected, is2.Attributes) {
   689  		t.Fatalf("bad: %#v", is2.Attributes)
   690  	}
   691  }
   692  
   693  func TestInstanceState_MergeDiff_nil(t *testing.T) {
   694  	var is *InstanceState = nil
   695  
   696  	diff := &InstanceDiff{
   697  		Attributes: map[string]*ResourceAttrDiff{
   698  			"foo": &ResourceAttrDiff{
   699  				Old: "",
   700  				New: "baz",
   701  			},
   702  		},
   703  	}
   704  
   705  	is2 := is.MergeDiff(diff)
   706  
   707  	expected := map[string]string{
   708  		"foo": "baz",
   709  	}
   710  
   711  	if !reflect.DeepEqual(expected, is2.Attributes) {
   712  		t.Fatalf("bad: %#v", is2.Attributes)
   713  	}
   714  }
   715  
   716  func TestInstanceState_MergeDiff_nilDiff(t *testing.T) {
   717  	is := InstanceState{
   718  		ID: "foo",
   719  		Attributes: map[string]string{
   720  			"foo": "bar",
   721  		},
   722  	}
   723  
   724  	is2 := is.MergeDiff(nil)
   725  
   726  	expected := map[string]string{
   727  		"foo": "bar",
   728  	}
   729  
   730  	if !reflect.DeepEqual(expected, is2.Attributes) {
   731  		t.Fatalf("bad: %#v", is2.Attributes)
   732  	}
   733  }
   734  
   735  func TestReadUpgradeState(t *testing.T) {
   736  	state := &StateV1{
   737  		Resources: map[string]*ResourceStateV1{
   738  			"foo": &ResourceStateV1{
   739  				ID: "bar",
   740  			},
   741  		},
   742  	}
   743  	buf := new(bytes.Buffer)
   744  	if err := testWriteStateV1(state, buf); err != nil {
   745  		t.Fatalf("err: %s", err)
   746  	}
   747  
   748  	// ReadState should transparently detect the old
   749  	// version and upgrade up so the latest.
   750  	actual, err := ReadState(buf)
   751  	if err != nil {
   752  		t.Fatalf("err: %s", err)
   753  	}
   754  
   755  	upgraded, err := upgradeV1State(state)
   756  	if err != nil {
   757  		t.Fatalf("err: %s", err)
   758  	}
   759  
   760  	if !reflect.DeepEqual(actual, upgraded) {
   761  		t.Fatalf("bad: %#v", actual)
   762  	}
   763  }
   764  
   765  func TestReadWriteState(t *testing.T) {
   766  	state := &State{
   767  		Serial: 9,
   768  		Remote: &RemoteState{
   769  			Type: "http",
   770  			Config: map[string]string{
   771  				"url": "http://my-cool-server.com/",
   772  			},
   773  		},
   774  		Modules: []*ModuleState{
   775  			&ModuleState{
   776  				Path: rootModulePath,
   777  				Dependencies: []string{
   778  					"aws_instance.bar",
   779  				},
   780  				Resources: map[string]*ResourceState{
   781  					"foo": &ResourceState{
   782  						Primary: &InstanceState{
   783  							ID: "bar",
   784  							Ephemeral: EphemeralState{
   785  								ConnInfo: map[string]string{
   786  									"type":     "ssh",
   787  									"user":     "root",
   788  									"password": "supersecret",
   789  								},
   790  							},
   791  						},
   792  					},
   793  				},
   794  			},
   795  		},
   796  	}
   797  
   798  	buf := new(bytes.Buffer)
   799  	if err := WriteState(state, buf); err != nil {
   800  		t.Fatalf("err: %s", err)
   801  	}
   802  
   803  	// Verify that the version and serial are set
   804  	if state.Version != StateVersion {
   805  		t.Fatalf("bad version number: %d", state.Version)
   806  	}
   807  
   808  	actual, err := ReadState(buf)
   809  	if err != nil {
   810  		t.Fatalf("err: %s", err)
   811  	}
   812  
   813  	// ReadState should not restore sensitive information!
   814  	mod := state.RootModule()
   815  	mod.Resources["foo"].Primary.Ephemeral = EphemeralState{}
   816  
   817  	if !reflect.DeepEqual(actual, state) {
   818  		t.Fatalf("bad: %#v", actual)
   819  	}
   820  }
   821  
   822  func TestReadStateNewVersion(t *testing.T) {
   823  	type out struct {
   824  		Version int
   825  	}
   826  
   827  	buf, err := json.Marshal(&out{StateVersion + 1})
   828  	if err != nil {
   829  		t.Fatalf("err: %v", err)
   830  	}
   831  
   832  	s, err := ReadState(bytes.NewReader(buf))
   833  	if s != nil {
   834  		t.Fatalf("unexpected: %#v", s)
   835  	}
   836  	if !strings.Contains(err.Error(), "not supported") {
   837  		t.Fatalf("err: %v", err)
   838  	}
   839  }
   840  
   841  func TestUpgradeV1State(t *testing.T) {
   842  	old := &StateV1{
   843  		Outputs: map[string]string{
   844  			"ip": "127.0.0.1",
   845  		},
   846  		Resources: map[string]*ResourceStateV1{
   847  			"foo": &ResourceStateV1{
   848  				Type: "test_resource",
   849  				ID:   "bar",
   850  				Attributes: map[string]string{
   851  					"key": "val",
   852  				},
   853  			},
   854  			"bar": &ResourceStateV1{
   855  				Type: "test_resource",
   856  				ID:   "1234",
   857  				Attributes: map[string]string{
   858  					"a": "b",
   859  				},
   860  			},
   861  		},
   862  		Tainted: map[string]struct{}{
   863  			"bar": struct{}{},
   864  		},
   865  	}
   866  	state, err := upgradeV1State(old)
   867  	if err != nil {
   868  		t.Fatalf("err: %v", err)
   869  	}
   870  
   871  	if len(state.Modules) != 1 {
   872  		t.Fatalf("should only have root module: %#v", state.Modules)
   873  	}
   874  	root := state.RootModule()
   875  
   876  	if len(root.Outputs) != 1 {
   877  		t.Fatalf("bad outputs: %v", root.Outputs)
   878  	}
   879  	if root.Outputs["ip"] != "127.0.0.1" {
   880  		t.Fatalf("bad outputs: %v", root.Outputs)
   881  	}
   882  
   883  	if len(root.Resources) != 2 {
   884  		t.Fatalf("bad resources: %v", root.Resources)
   885  	}
   886  
   887  	foo := root.Resources["foo"]
   888  	if foo.Type != "test_resource" {
   889  		t.Fatalf("bad: %#v", foo)
   890  	}
   891  	if foo.Primary == nil || foo.Primary.ID != "bar" ||
   892  		foo.Primary.Attributes["key"] != "val" {
   893  		t.Fatalf("bad: %#v", foo)
   894  	}
   895  	if len(foo.Tainted) > 0 {
   896  		t.Fatalf("bad: %#v", foo)
   897  	}
   898  
   899  	bar := root.Resources["bar"]
   900  	if bar.Type != "test_resource" {
   901  		t.Fatalf("bad: %#v", bar)
   902  	}
   903  	if bar.Primary != nil {
   904  		t.Fatalf("bad: %#v", bar)
   905  	}
   906  	if len(bar.Tainted) != 1 {
   907  		t.Fatalf("bad: %#v", bar)
   908  	}
   909  	bt := bar.Tainted[0]
   910  	if bt.ID != "1234" || bt.Attributes["a"] != "b" {
   911  		t.Fatalf("bad: %#v", bt)
   912  	}
   913  }
   914  
   915  func TestParseResourceStateKey(t *testing.T) {
   916  	cases := []struct {
   917  		Input       string
   918  		Expected    *ResourceStateKey
   919  		ExpectedErr bool
   920  	}{
   921  		{
   922  			Input: "aws_instance.foo.3",
   923  			Expected: &ResourceStateKey{
   924  				Type:  "aws_instance",
   925  				Name:  "foo",
   926  				Index: 3,
   927  			},
   928  		},
   929  		{
   930  			Input: "aws_instance.foo.0",
   931  			Expected: &ResourceStateKey{
   932  				Type:  "aws_instance",
   933  				Name:  "foo",
   934  				Index: 0,
   935  			},
   936  		},
   937  		{
   938  			Input: "aws_instance.foo",
   939  			Expected: &ResourceStateKey{
   940  				Type:  "aws_instance",
   941  				Name:  "foo",
   942  				Index: -1,
   943  			},
   944  		},
   945  		{
   946  			Input:       "aws_instance.foo.malformed",
   947  			ExpectedErr: true,
   948  		},
   949  		{
   950  			Input:       "aws_instance.foo.malformedwithnumber.123",
   951  			ExpectedErr: true,
   952  		},
   953  		{
   954  			Input:       "malformed",
   955  			ExpectedErr: true,
   956  		},
   957  	}
   958  	for _, tc := range cases {
   959  		rsk, err := ParseResourceStateKey(tc.Input)
   960  		if rsk != nil && tc.Expected != nil && !rsk.Equal(tc.Expected) {
   961  			t.Fatalf("%s: expected %s, got %s", tc.Input, tc.Expected, rsk)
   962  		}
   963  		if (err != nil) != tc.ExpectedErr {
   964  			t.Fatalf("%s: expected err: %t, got %s", tc.Input, tc.ExpectedErr, err)
   965  		}
   966  	}
   967  }