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