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