github.com/federicobaldo/terraform@v0.6.15-0.20160323222747-b20f680cbf05/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 TestResourceStateUntaint(t *testing.T) {
   506  	cases := map[string]struct {
   507  		Input          *ResourceState
   508  		Index          func() int
   509  		ExpectedOutput *ResourceState
   510  		ExpectedErrMsg string
   511  	}{
   512  		"no primary, no tainted, err": {
   513  			Input:          &ResourceState{},
   514  			ExpectedOutput: &ResourceState{},
   515  			ExpectedErrMsg: "Nothing to untaint",
   516  		},
   517  
   518  		"one tainted, no primary": {
   519  			Input: &ResourceState{
   520  				Tainted: []*InstanceState{
   521  					&InstanceState{ID: "foo"},
   522  				},
   523  			},
   524  			ExpectedOutput: &ResourceState{
   525  				Primary: &InstanceState{ID: "foo"},
   526  				Tainted: []*InstanceState{},
   527  			},
   528  		},
   529  
   530  		"one tainted, existing primary error": {
   531  			Input: &ResourceState{
   532  				Primary: &InstanceState{ID: "foo"},
   533  				Tainted: []*InstanceState{
   534  					&InstanceState{ID: "foo"},
   535  				},
   536  			},
   537  			ExpectedErrMsg: "Resource has a primary",
   538  		},
   539  
   540  		"multiple tainted, no index": {
   541  			Input: &ResourceState{
   542  				Tainted: []*InstanceState{
   543  					&InstanceState{ID: "bar"},
   544  					&InstanceState{ID: "foo"},
   545  				},
   546  			},
   547  			ExpectedErrMsg: "please specify an index",
   548  		},
   549  
   550  		"multiple tainted, with index": {
   551  			Input: &ResourceState{
   552  				Tainted: []*InstanceState{
   553  					&InstanceState{ID: "bar"},
   554  					&InstanceState{ID: "foo"},
   555  				},
   556  			},
   557  			Index: func() int { return 1 },
   558  			ExpectedOutput: &ResourceState{
   559  				Primary: &InstanceState{ID: "foo"},
   560  				Tainted: []*InstanceState{
   561  					&InstanceState{ID: "bar"},
   562  				},
   563  			},
   564  		},
   565  
   566  		"index out of bounds error": {
   567  			Input: &ResourceState{
   568  				Tainted: []*InstanceState{
   569  					&InstanceState{ID: "bar"},
   570  					&InstanceState{ID: "foo"},
   571  				},
   572  			},
   573  			Index:          func() int { return 2 },
   574  			ExpectedErrMsg: "out of range",
   575  		},
   576  	}
   577  
   578  	for k, tc := range cases {
   579  		index := -1
   580  		if tc.Index != nil {
   581  			index = tc.Index()
   582  		}
   583  		err := tc.Input.Untaint(index)
   584  		if tc.ExpectedErrMsg == "" && err != nil {
   585  			t.Fatalf("[%s] unexpected err: %s", k, err)
   586  		}
   587  		if tc.ExpectedErrMsg != "" {
   588  			if strings.Contains(err.Error(), tc.ExpectedErrMsg) {
   589  				continue
   590  			}
   591  			t.Fatalf("[%s] expected err: %s to contain: %s",
   592  				k, err, tc.ExpectedErrMsg)
   593  		}
   594  		if !reflect.DeepEqual(tc.Input, tc.ExpectedOutput) {
   595  			t.Fatalf(
   596  				"Failure: %s\n\nExpected: %#v\n\nGot: %#v",
   597  				k, tc.ExpectedOutput, tc.Input)
   598  		}
   599  	}
   600  }
   601  
   602  func TestInstanceStateEmpty(t *testing.T) {
   603  	cases := map[string]struct {
   604  		In     *InstanceState
   605  		Result bool
   606  	}{
   607  		"nil is empty": {
   608  			nil,
   609  			true,
   610  		},
   611  		"non-nil but without ID is empty": {
   612  			&InstanceState{},
   613  			true,
   614  		},
   615  		"with ID is not empty": {
   616  			&InstanceState{
   617  				ID: "i-abc123",
   618  			},
   619  			false,
   620  		},
   621  	}
   622  
   623  	for tn, tc := range cases {
   624  		if tc.In.Empty() != tc.Result {
   625  			t.Fatalf("%q expected %#v to be empty: %#v", tn, tc.In, tc.Result)
   626  		}
   627  	}
   628  }
   629  
   630  func TestInstanceStateEqual(t *testing.T) {
   631  	cases := []struct {
   632  		Result   bool
   633  		One, Two *InstanceState
   634  	}{
   635  		// Nils
   636  		{
   637  			false,
   638  			nil,
   639  			&InstanceState{},
   640  		},
   641  
   642  		{
   643  			false,
   644  			&InstanceState{},
   645  			nil,
   646  		},
   647  
   648  		// Different IDs
   649  		{
   650  			false,
   651  			&InstanceState{ID: "foo"},
   652  			&InstanceState{ID: "bar"},
   653  		},
   654  
   655  		// Different Attributes
   656  		{
   657  			false,
   658  			&InstanceState{Attributes: map[string]string{"foo": "bar"}},
   659  			&InstanceState{Attributes: map[string]string{"foo": "baz"}},
   660  		},
   661  
   662  		// Different Attribute keys
   663  		{
   664  			false,
   665  			&InstanceState{Attributes: map[string]string{"foo": "bar"}},
   666  			&InstanceState{Attributes: map[string]string{"bar": "baz"}},
   667  		},
   668  
   669  		{
   670  			false,
   671  			&InstanceState{Attributes: map[string]string{"bar": "baz"}},
   672  			&InstanceState{Attributes: map[string]string{"foo": "bar"}},
   673  		},
   674  	}
   675  
   676  	for i, tc := range cases {
   677  		if tc.One.Equal(tc.Two) != tc.Result {
   678  			t.Fatalf("Bad: %d\n\n%s\n\n%s", i, tc.One.String(), tc.Two.String())
   679  		}
   680  	}
   681  }
   682  
   683  func TestStateEmpty(t *testing.T) {
   684  	cases := []struct {
   685  		In     *State
   686  		Result bool
   687  	}{
   688  		{
   689  			nil,
   690  			true,
   691  		},
   692  		{
   693  			&State{},
   694  			true,
   695  		},
   696  		{
   697  			&State{
   698  				Remote: &RemoteState{Type: "foo"},
   699  			},
   700  			true,
   701  		},
   702  		{
   703  			&State{
   704  				Modules: []*ModuleState{
   705  					&ModuleState{},
   706  				},
   707  			},
   708  			false,
   709  		},
   710  	}
   711  
   712  	for i, tc := range cases {
   713  		if tc.In.Empty() != tc.Result {
   714  			t.Fatalf("bad %d %#v:\n\n%#v", i, tc.Result, tc.In)
   715  		}
   716  	}
   717  }
   718  
   719  func TestStateIsRemote(t *testing.T) {
   720  	cases := []struct {
   721  		In     *State
   722  		Result bool
   723  	}{
   724  		{
   725  			nil,
   726  			false,
   727  		},
   728  		{
   729  			&State{},
   730  			false,
   731  		},
   732  		{
   733  			&State{
   734  				Remote: &RemoteState{Type: "foo"},
   735  			},
   736  			true,
   737  		},
   738  	}
   739  
   740  	for i, tc := range cases {
   741  		if tc.In.IsRemote() != tc.Result {
   742  			t.Fatalf("bad %d %#v:\n\n%#v", i, tc.Result, tc.In)
   743  		}
   744  	}
   745  }
   746  
   747  func TestInstanceState_MergeDiff(t *testing.T) {
   748  	is := InstanceState{
   749  		ID: "foo",
   750  		Attributes: map[string]string{
   751  			"foo":  "bar",
   752  			"port": "8000",
   753  		},
   754  	}
   755  
   756  	diff := &InstanceDiff{
   757  		Attributes: map[string]*ResourceAttrDiff{
   758  			"foo": &ResourceAttrDiff{
   759  				Old: "bar",
   760  				New: "baz",
   761  			},
   762  			"bar": &ResourceAttrDiff{
   763  				Old: "",
   764  				New: "foo",
   765  			},
   766  			"baz": &ResourceAttrDiff{
   767  				Old:         "",
   768  				New:         "foo",
   769  				NewComputed: true,
   770  			},
   771  			"port": &ResourceAttrDiff{
   772  				NewRemoved: true,
   773  			},
   774  		},
   775  	}
   776  
   777  	is2 := is.MergeDiff(diff)
   778  
   779  	expected := map[string]string{
   780  		"foo": "baz",
   781  		"bar": "foo",
   782  		"baz": config.UnknownVariableValue,
   783  	}
   784  
   785  	if !reflect.DeepEqual(expected, is2.Attributes) {
   786  		t.Fatalf("bad: %#v", is2.Attributes)
   787  	}
   788  }
   789  
   790  func TestInstanceState_MergeDiff_nil(t *testing.T) {
   791  	var is *InstanceState = nil
   792  
   793  	diff := &InstanceDiff{
   794  		Attributes: map[string]*ResourceAttrDiff{
   795  			"foo": &ResourceAttrDiff{
   796  				Old: "",
   797  				New: "baz",
   798  			},
   799  		},
   800  	}
   801  
   802  	is2 := is.MergeDiff(diff)
   803  
   804  	expected := map[string]string{
   805  		"foo": "baz",
   806  	}
   807  
   808  	if !reflect.DeepEqual(expected, is2.Attributes) {
   809  		t.Fatalf("bad: %#v", is2.Attributes)
   810  	}
   811  }
   812  
   813  func TestInstanceState_MergeDiff_nilDiff(t *testing.T) {
   814  	is := InstanceState{
   815  		ID: "foo",
   816  		Attributes: map[string]string{
   817  			"foo": "bar",
   818  		},
   819  	}
   820  
   821  	is2 := is.MergeDiff(nil)
   822  
   823  	expected := map[string]string{
   824  		"foo": "bar",
   825  	}
   826  
   827  	if !reflect.DeepEqual(expected, is2.Attributes) {
   828  		t.Fatalf("bad: %#v", is2.Attributes)
   829  	}
   830  }
   831  
   832  func TestReadUpgradeState(t *testing.T) {
   833  	state := &StateV1{
   834  		Resources: map[string]*ResourceStateV1{
   835  			"foo": &ResourceStateV1{
   836  				ID: "bar",
   837  			},
   838  		},
   839  	}
   840  	buf := new(bytes.Buffer)
   841  	if err := testWriteStateV1(state, buf); err != nil {
   842  		t.Fatalf("err: %s", err)
   843  	}
   844  
   845  	// ReadState should transparently detect the old
   846  	// version and upgrade up so the latest.
   847  	actual, err := ReadState(buf)
   848  	if err != nil {
   849  		t.Fatalf("err: %s", err)
   850  	}
   851  
   852  	upgraded, err := upgradeV1State(state)
   853  	if err != nil {
   854  		t.Fatalf("err: %s", err)
   855  	}
   856  
   857  	if !reflect.DeepEqual(actual, upgraded) {
   858  		t.Fatalf("bad: %#v", actual)
   859  	}
   860  }
   861  
   862  func TestReadWriteState(t *testing.T) {
   863  	state := &State{
   864  		Serial: 9,
   865  		Remote: &RemoteState{
   866  			Type: "http",
   867  			Config: map[string]string{
   868  				"url": "http://my-cool-server.com/",
   869  			},
   870  		},
   871  		Modules: []*ModuleState{
   872  			&ModuleState{
   873  				Path: rootModulePath,
   874  				Dependencies: []string{
   875  					"aws_instance.bar",
   876  				},
   877  				Resources: map[string]*ResourceState{
   878  					"foo": &ResourceState{
   879  						Primary: &InstanceState{
   880  							ID: "bar",
   881  							Ephemeral: EphemeralState{
   882  								ConnInfo: map[string]string{
   883  									"type":     "ssh",
   884  									"user":     "root",
   885  									"password": "supersecret",
   886  								},
   887  							},
   888  						},
   889  					},
   890  				},
   891  			},
   892  		},
   893  	}
   894  
   895  	buf := new(bytes.Buffer)
   896  	if err := WriteState(state, buf); err != nil {
   897  		t.Fatalf("err: %s", err)
   898  	}
   899  
   900  	// Verify that the version and serial are set
   901  	if state.Version != StateVersion {
   902  		t.Fatalf("bad version number: %d", state.Version)
   903  	}
   904  
   905  	actual, err := ReadState(buf)
   906  	if err != nil {
   907  		t.Fatalf("err: %s", err)
   908  	}
   909  
   910  	// ReadState should not restore sensitive information!
   911  	mod := state.RootModule()
   912  	mod.Resources["foo"].Primary.Ephemeral = EphemeralState{}
   913  
   914  	if !reflect.DeepEqual(actual, state) {
   915  		t.Fatalf("bad: %#v", actual)
   916  	}
   917  }
   918  
   919  func TestReadStateNewVersion(t *testing.T) {
   920  	type out struct {
   921  		Version int
   922  	}
   923  
   924  	buf, err := json.Marshal(&out{StateVersion + 1})
   925  	if err != nil {
   926  		t.Fatalf("err: %v", err)
   927  	}
   928  
   929  	s, err := ReadState(bytes.NewReader(buf))
   930  	if s != nil {
   931  		t.Fatalf("unexpected: %#v", s)
   932  	}
   933  	if !strings.Contains(err.Error(), "not supported") {
   934  		t.Fatalf("err: %v", err)
   935  	}
   936  }
   937  
   938  func TestUpgradeV1State(t *testing.T) {
   939  	old := &StateV1{
   940  		Outputs: map[string]string{
   941  			"ip": "127.0.0.1",
   942  		},
   943  		Resources: map[string]*ResourceStateV1{
   944  			"foo": &ResourceStateV1{
   945  				Type: "test_resource",
   946  				ID:   "bar",
   947  				Attributes: map[string]string{
   948  					"key": "val",
   949  				},
   950  			},
   951  			"bar": &ResourceStateV1{
   952  				Type: "test_resource",
   953  				ID:   "1234",
   954  				Attributes: map[string]string{
   955  					"a": "b",
   956  				},
   957  			},
   958  		},
   959  		Tainted: map[string]struct{}{
   960  			"bar": struct{}{},
   961  		},
   962  	}
   963  	state, err := upgradeV1State(old)
   964  	if err != nil {
   965  		t.Fatalf("err: %v", err)
   966  	}
   967  
   968  	if len(state.Modules) != 1 {
   969  		t.Fatalf("should only have root module: %#v", state.Modules)
   970  	}
   971  	root := state.RootModule()
   972  
   973  	if len(root.Outputs) != 1 {
   974  		t.Fatalf("bad outputs: %v", root.Outputs)
   975  	}
   976  	if root.Outputs["ip"] != "127.0.0.1" {
   977  		t.Fatalf("bad outputs: %v", root.Outputs)
   978  	}
   979  
   980  	if len(root.Resources) != 2 {
   981  		t.Fatalf("bad resources: %v", root.Resources)
   982  	}
   983  
   984  	foo := root.Resources["foo"]
   985  	if foo.Type != "test_resource" {
   986  		t.Fatalf("bad: %#v", foo)
   987  	}
   988  	if foo.Primary == nil || foo.Primary.ID != "bar" ||
   989  		foo.Primary.Attributes["key"] != "val" {
   990  		t.Fatalf("bad: %#v", foo)
   991  	}
   992  	if len(foo.Tainted) > 0 {
   993  		t.Fatalf("bad: %#v", foo)
   994  	}
   995  
   996  	bar := root.Resources["bar"]
   997  	if bar.Type != "test_resource" {
   998  		t.Fatalf("bad: %#v", bar)
   999  	}
  1000  	if bar.Primary != nil {
  1001  		t.Fatalf("bad: %#v", bar)
  1002  	}
  1003  	if len(bar.Tainted) != 1 {
  1004  		t.Fatalf("bad: %#v", bar)
  1005  	}
  1006  	bt := bar.Tainted[0]
  1007  	if bt.ID != "1234" || bt.Attributes["a"] != "b" {
  1008  		t.Fatalf("bad: %#v", bt)
  1009  	}
  1010  }
  1011  
  1012  func TestParseResourceStateKey(t *testing.T) {
  1013  	cases := []struct {
  1014  		Input       string
  1015  		Expected    *ResourceStateKey
  1016  		ExpectedErr bool
  1017  	}{
  1018  		{
  1019  			Input: "aws_instance.foo.3",
  1020  			Expected: &ResourceStateKey{
  1021  				Type:  "aws_instance",
  1022  				Name:  "foo",
  1023  				Index: 3,
  1024  			},
  1025  		},
  1026  		{
  1027  			Input: "aws_instance.foo.0",
  1028  			Expected: &ResourceStateKey{
  1029  				Type:  "aws_instance",
  1030  				Name:  "foo",
  1031  				Index: 0,
  1032  			},
  1033  		},
  1034  		{
  1035  			Input: "aws_instance.foo",
  1036  			Expected: &ResourceStateKey{
  1037  				Type:  "aws_instance",
  1038  				Name:  "foo",
  1039  				Index: -1,
  1040  			},
  1041  		},
  1042  		{
  1043  			Input:       "aws_instance.foo.malformed",
  1044  			ExpectedErr: true,
  1045  		},
  1046  		{
  1047  			Input:       "aws_instance.foo.malformedwithnumber.123",
  1048  			ExpectedErr: true,
  1049  		},
  1050  		{
  1051  			Input:       "malformed",
  1052  			ExpectedErr: true,
  1053  		},
  1054  	}
  1055  	for _, tc := range cases {
  1056  		rsk, err := ParseResourceStateKey(tc.Input)
  1057  		if rsk != nil && tc.Expected != nil && !rsk.Equal(tc.Expected) {
  1058  			t.Fatalf("%s: expected %s, got %s", tc.Input, tc.Expected, rsk)
  1059  		}
  1060  		if (err != nil) != tc.ExpectedErr {
  1061  			t.Fatalf("%s: expected err: %t, got %s", tc.Input, tc.ExpectedErr, err)
  1062  		}
  1063  	}
  1064  }