github.com/leighwaller/terraform@v0.11.12-beta1/terraform/resource_test.go (about)

     1  package terraform
     2  
     3  import (
     4  	"fmt"
     5  	"reflect"
     6  	"strconv"
     7  	"testing"
     8  
     9  	"github.com/hashicorp/hil"
    10  	"github.com/hashicorp/hil/ast"
    11  	"github.com/hashicorp/terraform/config"
    12  	"github.com/mitchellh/reflectwalk"
    13  )
    14  
    15  func TestInstanceInfo(t *testing.T) {
    16  	cases := []struct {
    17  		Info   *InstanceInfo
    18  		Result string
    19  	}{
    20  		{
    21  			&InstanceInfo{
    22  				Id: "foo",
    23  			},
    24  			"foo",
    25  		},
    26  		{
    27  			&InstanceInfo{
    28  				Id:         "foo",
    29  				ModulePath: rootModulePath,
    30  			},
    31  			"foo",
    32  		},
    33  		{
    34  			&InstanceInfo{
    35  				Id:         "foo",
    36  				ModulePath: []string{"root", "consul"},
    37  			},
    38  			"module.consul.foo",
    39  		},
    40  	}
    41  
    42  	for i, tc := range cases {
    43  		actual := tc.Info.HumanId()
    44  		if actual != tc.Result {
    45  			t.Fatalf("%d: %s", i, actual)
    46  		}
    47  	}
    48  }
    49  
    50  func TestInstanceInfoResourceAddress(t *testing.T) {
    51  	tests := []struct {
    52  		Input *InstanceInfo
    53  		Want  string
    54  	}{
    55  		{
    56  			&InstanceInfo{
    57  				Id: "test_resource.baz",
    58  			},
    59  			"test_resource.baz",
    60  		},
    61  		{
    62  			&InstanceInfo{
    63  				Id:         "test_resource.baz",
    64  				ModulePath: rootModulePath,
    65  			},
    66  			"test_resource.baz",
    67  		},
    68  		{
    69  			&InstanceInfo{
    70  				Id:         "test_resource.baz",
    71  				ModulePath: []string{"root", "foo"},
    72  			},
    73  			"module.foo.test_resource.baz",
    74  		},
    75  		{
    76  			&InstanceInfo{
    77  				Id:         "test_resource.baz",
    78  				ModulePath: []string{"root", "foo", "bar"},
    79  			},
    80  			"module.foo.module.bar.test_resource.baz",
    81  		},
    82  		{
    83  			&InstanceInfo{
    84  				Id: "test_resource.baz (tainted)",
    85  			},
    86  			"test_resource.baz.tainted",
    87  		},
    88  		{
    89  			&InstanceInfo{
    90  				Id: "test_resource.baz (deposed #0)",
    91  			},
    92  			"test_resource.baz.deposed",
    93  		},
    94  	}
    95  
    96  	for i, test := range tests {
    97  		t.Run(strconv.Itoa(i), func(t *testing.T) {
    98  			gotAddr := test.Input.ResourceAddress()
    99  			got := gotAddr.String()
   100  			if got != test.Want {
   101  				t.Fatalf("wrong result\ngot:  %s\nwant: %s", got, test.Want)
   102  			}
   103  		})
   104  	}
   105  }
   106  
   107  func TestResourceConfigGet(t *testing.T) {
   108  	cases := []struct {
   109  		Config map[string]interface{}
   110  		Vars   map[string]interface{}
   111  		Key    string
   112  		Value  interface{}
   113  	}{
   114  		{
   115  			Config: nil,
   116  			Key:    "foo",
   117  			Value:  nil,
   118  		},
   119  
   120  		{
   121  			Config: map[string]interface{}{
   122  				"foo": "bar",
   123  			},
   124  			Key:   "foo",
   125  			Value: "bar",
   126  		},
   127  
   128  		{
   129  			Config: map[string]interface{}{
   130  				"foo": "${var.foo}",
   131  			},
   132  			Key:   "foo",
   133  			Value: "${var.foo}",
   134  		},
   135  
   136  		{
   137  			Config: map[string]interface{}{
   138  				"foo": "${var.foo}",
   139  			},
   140  			Vars:  map[string]interface{}{"foo": unknownValue()},
   141  			Key:   "foo",
   142  			Value: "${var.foo}",
   143  		},
   144  
   145  		{
   146  			Config: map[string]interface{}{
   147  				"foo": []interface{}{1, 2, 5},
   148  			},
   149  			Key:   "foo.0",
   150  			Value: 1,
   151  		},
   152  
   153  		{
   154  			Config: map[string]interface{}{
   155  				"foo": []interface{}{1, 2, 5},
   156  			},
   157  			Key:   "foo.5",
   158  			Value: nil,
   159  		},
   160  
   161  		{
   162  			Config: map[string]interface{}{
   163  				"foo": []interface{}{1, 2, 5},
   164  			},
   165  			Key:   "foo.-1",
   166  			Value: nil,
   167  		},
   168  
   169  		// get from map
   170  		{
   171  			Config: map[string]interface{}{
   172  				"mapname": []map[string]interface{}{
   173  					map[string]interface{}{"key": 1},
   174  				},
   175  			},
   176  			Key:   "mapname.0.key",
   177  			Value: 1,
   178  		},
   179  
   180  		// get from map with dot in key
   181  		{
   182  			Config: map[string]interface{}{
   183  				"mapname": []map[string]interface{}{
   184  					map[string]interface{}{"key.name": 1},
   185  				},
   186  			},
   187  			Key:   "mapname.0.key.name",
   188  			Value: 1,
   189  		},
   190  
   191  		// get from map with overlapping key names
   192  		{
   193  			Config: map[string]interface{}{
   194  				"mapname": []map[string]interface{}{
   195  					map[string]interface{}{
   196  						"key.name":   1,
   197  						"key.name.2": 2,
   198  					},
   199  				},
   200  			},
   201  			Key:   "mapname.0.key.name.2",
   202  			Value: 2,
   203  		},
   204  		{
   205  			Config: map[string]interface{}{
   206  				"mapname": []map[string]interface{}{
   207  					map[string]interface{}{
   208  						"key.name":     1,
   209  						"key.name.foo": 2,
   210  					},
   211  				},
   212  			},
   213  			Key:   "mapname.0.key.name",
   214  			Value: 1,
   215  		},
   216  		{
   217  			Config: map[string]interface{}{
   218  				"mapname": []map[string]interface{}{
   219  					map[string]interface{}{
   220  						"listkey": []map[string]interface{}{
   221  							{"key": 3},
   222  						},
   223  					},
   224  				},
   225  			},
   226  			Key:   "mapname.0.listkey.0.key",
   227  			Value: 3,
   228  		},
   229  
   230  		// A map assigned to a list via interpolation should Get a non-existent
   231  		// value. The test code now also checks that Get doesn't return (nil,
   232  		// true), which it previously did for this configuration.
   233  		{
   234  			Config: map[string]interface{}{
   235  				"maplist": "${var.maplist}",
   236  			},
   237  			Key:   "maplist.0",
   238  			Value: nil,
   239  		},
   240  
   241  		// Reference list of maps variable.
   242  		// This does not work from GetRaw.
   243  		{
   244  			Vars: map[string]interface{}{
   245  				"maplist": []interface{}{
   246  					map[string]interface{}{
   247  						"key": "a",
   248  					},
   249  					map[string]interface{}{
   250  						"key": "b",
   251  					},
   252  				},
   253  			},
   254  			Config: map[string]interface{}{
   255  				"maplist": "${var.maplist}",
   256  			},
   257  			Key:   "maplist.0",
   258  			Value: map[string]interface{}{"key": "a"},
   259  		},
   260  
   261  		// Reference a map-of-lists variable.
   262  		// This does not work from GetRaw.
   263  		{
   264  			Vars: map[string]interface{}{
   265  				"listmap": map[string]interface{}{
   266  					"key1": []interface{}{"a", "b"},
   267  					"key2": []interface{}{"c", "d"},
   268  				},
   269  			},
   270  			Config: map[string]interface{}{
   271  				"listmap": "${var.listmap}",
   272  			},
   273  			Key:   "listmap.key1",
   274  			Value: []interface{}{"a", "b"},
   275  		},
   276  
   277  		// FIXME: this is ambiguous, and matches the nested map
   278  		//        leaving here to catch this behaviour if it changes.
   279  		{
   280  			Config: map[string]interface{}{
   281  				"mapname": []map[string]interface{}{
   282  					map[string]interface{}{
   283  						"key.name":   1,
   284  						"key.name.0": 2,
   285  						"key":        map[string]interface{}{"name": 3},
   286  					},
   287  				},
   288  			},
   289  			Key:   "mapname.0.key.name",
   290  			Value: 3,
   291  		},
   292  		/*
   293  			// TODO: can't access this nested list at all.
   294  			// FIXME: key with name matching substring of nested list can panic
   295  			{
   296  				Config: map[string]interface{}{
   297  					"mapname": []map[string]interface{}{
   298  						map[string]interface{}{
   299  							"key.name": []map[string]interface{}{
   300  								{"subkey": 1},
   301  							},
   302  							"key": 3,
   303  						},
   304  					},
   305  				},
   306  				Key:   "mapname.0.key.name.0.subkey",
   307  				Value: 3,
   308  			},
   309  		*/
   310  	}
   311  
   312  	for i, tc := range cases {
   313  		var rawC *config.RawConfig
   314  		if tc.Config != nil {
   315  			var err error
   316  			rawC, err = config.NewRawConfig(tc.Config)
   317  			if err != nil {
   318  				t.Fatalf("err: %s", err)
   319  			}
   320  		}
   321  
   322  		if tc.Vars != nil {
   323  			vs := make(map[string]ast.Variable)
   324  			for k, v := range tc.Vars {
   325  				hilVar, err := hil.InterfaceToVariable(v)
   326  				if err != nil {
   327  					t.Fatalf("%#v to var: %s", v, err)
   328  				}
   329  
   330  				vs["var."+k] = hilVar
   331  			}
   332  
   333  			if err := rawC.Interpolate(vs); err != nil {
   334  				t.Fatalf("err: %s", err)
   335  			}
   336  		}
   337  
   338  		rc := NewResourceConfig(rawC)
   339  		rc.interpolateForce()
   340  
   341  		// Test getting a key
   342  		t.Run(fmt.Sprintf("get-%d", i), func(t *testing.T) {
   343  			v, ok := rc.Get(tc.Key)
   344  			if ok && v == nil {
   345  				t.Fatal("(nil, true) returned from Get")
   346  			}
   347  
   348  			if !reflect.DeepEqual(v, tc.Value) {
   349  				t.Fatalf("%d bad: %#v", i, v)
   350  			}
   351  		})
   352  
   353  		// If we have vars, we don't test copying
   354  		if len(tc.Vars) > 0 {
   355  			continue
   356  		}
   357  
   358  		// Test copying and equality
   359  		t.Run(fmt.Sprintf("copy-and-equal-%d", i), func(t *testing.T) {
   360  			copy := rc.DeepCopy()
   361  			if !reflect.DeepEqual(copy, rc) {
   362  				t.Fatalf("bad:\n\n%#v\n\n%#v", copy, rc)
   363  			}
   364  
   365  			if !copy.Equal(rc) {
   366  				t.Fatalf("copy != rc:\n\n%#v\n\n%#v", copy, rc)
   367  			}
   368  			if !rc.Equal(copy) {
   369  				t.Fatalf("rc != copy:\n\n%#v\n\n%#v", copy, rc)
   370  			}
   371  		})
   372  	}
   373  }
   374  
   375  func TestResourceConfigGetRaw(t *testing.T) {
   376  	cases := []struct {
   377  		Config map[string]interface{}
   378  		Vars   map[string]interface{}
   379  		Key    string
   380  		Value  interface{}
   381  	}{
   382  		// Referencing a list-of-maps variable doesn't work from GetRaw.
   383  		// The ConfigFieldReader currently catches this case and looks up the
   384  		// variable in the config.
   385  		{
   386  			Vars: map[string]interface{}{
   387  				"maplist": []interface{}{
   388  					map[string]interface{}{
   389  						"key": "a",
   390  					},
   391  					map[string]interface{}{
   392  						"key": "b",
   393  					},
   394  				},
   395  			},
   396  			Config: map[string]interface{}{
   397  				"maplist": "${var.maplist}",
   398  			},
   399  			Key:   "maplist.0",
   400  			Value: nil,
   401  		},
   402  		// Reference a map-of-lists variable.
   403  		// The ConfigFieldReader currently catches this case and looks up the
   404  		// variable in the config.
   405  		{
   406  			Vars: map[string]interface{}{
   407  				"listmap": map[string]interface{}{
   408  					"key1": []interface{}{"a", "b"},
   409  					"key2": []interface{}{"c", "d"},
   410  				},
   411  			},
   412  			Config: map[string]interface{}{
   413  				"listmap": "${var.listmap}",
   414  			},
   415  			Key:   "listmap.key1",
   416  			Value: nil,
   417  		},
   418  	}
   419  
   420  	for i, tc := range cases {
   421  		var rawC *config.RawConfig
   422  		if tc.Config != nil {
   423  			var err error
   424  			rawC, err = config.NewRawConfig(tc.Config)
   425  			if err != nil {
   426  				t.Fatalf("err: %s", err)
   427  			}
   428  		}
   429  
   430  		if tc.Vars != nil {
   431  			vs := make(map[string]ast.Variable)
   432  			for k, v := range tc.Vars {
   433  				hilVar, err := hil.InterfaceToVariable(v)
   434  				if err != nil {
   435  					t.Fatalf("%#v to var: %s", v, err)
   436  				}
   437  				vs["var."+k] = hilVar
   438  			}
   439  			if err := rawC.Interpolate(vs); err != nil {
   440  				t.Fatalf("err: %s", err)
   441  			}
   442  		}
   443  
   444  		rc := NewResourceConfig(rawC)
   445  		rc.interpolateForce()
   446  
   447  		// Test getting a key
   448  		t.Run(fmt.Sprintf("get-%d", i), func(t *testing.T) {
   449  			v, ok := rc.GetRaw(tc.Key)
   450  			if ok && v == nil {
   451  				t.Fatal("(nil, true) returned from GetRaw")
   452  			}
   453  
   454  			if !reflect.DeepEqual(v, tc.Value) {
   455  				t.Fatalf("%d bad: %#v", i, v)
   456  			}
   457  		})
   458  	}
   459  }
   460  
   461  func TestResourceConfigIsComputed(t *testing.T) {
   462  	cases := []struct {
   463  		Name   string
   464  		Config map[string]interface{}
   465  		Vars   map[string]interface{}
   466  		Key    string
   467  		Result bool
   468  	}{
   469  		{
   470  			Name: "basic value",
   471  			Config: map[string]interface{}{
   472  				"foo": "${var.foo}",
   473  			},
   474  			Vars: map[string]interface{}{
   475  				"foo": unknownValue(),
   476  			},
   477  			Key:    "foo",
   478  			Result: true,
   479  		},
   480  
   481  		{
   482  			Name: "set with a computed element",
   483  			Config: map[string]interface{}{
   484  				"foo": "${var.foo}",
   485  			},
   486  			Vars: map[string]interface{}{
   487  				"foo": []string{
   488  					"a",
   489  					unknownValue(),
   490  				},
   491  			},
   492  			Key:    "foo",
   493  			Result: true,
   494  		},
   495  
   496  		{
   497  			Name: "set with no computed elements",
   498  			Config: map[string]interface{}{
   499  				"foo": "${var.foo}",
   500  			},
   501  			Vars: map[string]interface{}{
   502  				"foo": []string{
   503  					"a",
   504  					"b",
   505  				},
   506  			},
   507  			Key:    "foo",
   508  			Result: false,
   509  		},
   510  
   511  		/*
   512  			{
   513  				Name: "set count with computed elements",
   514  				Config: map[string]interface{}{
   515  					"foo": "${var.foo}",
   516  				},
   517  				Vars: map[string]interface{}{
   518  					"foo": []string{
   519  						"a",
   520  						unknownValue(),
   521  					},
   522  				},
   523  				Key:    "foo.#",
   524  				Result: true,
   525  			},
   526  		*/
   527  
   528  		{
   529  			Name: "set count with computed elements",
   530  			Config: map[string]interface{}{
   531  				"foo": []interface{}{"${var.foo}"},
   532  			},
   533  			Vars: map[string]interface{}{
   534  				"foo": []string{
   535  					"a",
   536  					unknownValue(),
   537  				},
   538  			},
   539  			Key:    "foo.#",
   540  			Result: true,
   541  		},
   542  
   543  		{
   544  			Name: "nested set with computed elements",
   545  			Config: map[string]interface{}{
   546  				"route": []map[string]interface{}{
   547  					map[string]interface{}{
   548  						"index":   "1",
   549  						"gateway": []interface{}{"${var.foo}"},
   550  					},
   551  				},
   552  			},
   553  			Vars: map[string]interface{}{
   554  				"foo": unknownValue(),
   555  			},
   556  			Key:    "route.0.gateway",
   557  			Result: true,
   558  		},
   559  	}
   560  
   561  	for i, tc := range cases {
   562  		t.Run(fmt.Sprintf("%d-%s", i, tc.Name), func(t *testing.T) {
   563  			var rawC *config.RawConfig
   564  			if tc.Config != nil {
   565  				var err error
   566  				rawC, err = config.NewRawConfig(tc.Config)
   567  				if err != nil {
   568  					t.Fatalf("err: %s", err)
   569  				}
   570  			}
   571  
   572  			if tc.Vars != nil {
   573  				vs := make(map[string]ast.Variable)
   574  				for k, v := range tc.Vars {
   575  					hilVar, err := hil.InterfaceToVariable(v)
   576  					if err != nil {
   577  						t.Fatalf("%#v to var: %s", v, err)
   578  					}
   579  
   580  					vs["var."+k] = hilVar
   581  				}
   582  
   583  				if err := rawC.Interpolate(vs); err != nil {
   584  					t.Fatalf("err: %s", err)
   585  				}
   586  			}
   587  
   588  			rc := NewResourceConfig(rawC)
   589  			rc.interpolateForce()
   590  
   591  			t.Logf("Config: %#v", rc)
   592  
   593  			actual := rc.IsComputed(tc.Key)
   594  			if actual != tc.Result {
   595  				t.Fatalf("bad: %#v", actual)
   596  			}
   597  		})
   598  	}
   599  }
   600  
   601  func TestResourceConfigCheckSet(t *testing.T) {
   602  	cases := []struct {
   603  		Name   string
   604  		Config map[string]interface{}
   605  		Vars   map[string]interface{}
   606  		Input  []string
   607  		Errs   bool
   608  	}{
   609  		{
   610  			Name: "computed basic",
   611  			Config: map[string]interface{}{
   612  				"foo": "${var.foo}",
   613  			},
   614  			Vars: map[string]interface{}{
   615  				"foo": unknownValue(),
   616  			},
   617  			Input: []string{"foo"},
   618  			Errs:  false,
   619  		},
   620  
   621  		{
   622  			Name: "basic",
   623  			Config: map[string]interface{}{
   624  				"foo": "bar",
   625  			},
   626  			Vars:  nil,
   627  			Input: []string{"foo"},
   628  			Errs:  false,
   629  		},
   630  
   631  		{
   632  			Name: "basic with not set",
   633  			Config: map[string]interface{}{
   634  				"foo": "bar",
   635  			},
   636  			Vars:  nil,
   637  			Input: []string{"foo", "bar"},
   638  			Errs:  true,
   639  		},
   640  
   641  		{
   642  			Name: "basic with one computed",
   643  			Config: map[string]interface{}{
   644  				"foo": "bar",
   645  				"bar": "${var.foo}",
   646  			},
   647  			Vars: map[string]interface{}{
   648  				"foo": unknownValue(),
   649  			},
   650  			Input: []string{"foo", "bar"},
   651  			Errs:  false,
   652  		},
   653  	}
   654  
   655  	for i, tc := range cases {
   656  		t.Run(fmt.Sprintf("%d-%s", i, tc.Name), func(t *testing.T) {
   657  			var rawC *config.RawConfig
   658  			if tc.Config != nil {
   659  				var err error
   660  				rawC, err = config.NewRawConfig(tc.Config)
   661  				if err != nil {
   662  					t.Fatalf("err: %s", err)
   663  				}
   664  			}
   665  
   666  			if tc.Vars != nil {
   667  				vs := make(map[string]ast.Variable)
   668  				for k, v := range tc.Vars {
   669  					hilVar, err := hil.InterfaceToVariable(v)
   670  					if err != nil {
   671  						t.Fatalf("%#v to var: %s", v, err)
   672  					}
   673  
   674  					vs["var."+k] = hilVar
   675  				}
   676  
   677  				if err := rawC.Interpolate(vs); err != nil {
   678  					t.Fatalf("err: %s", err)
   679  				}
   680  			}
   681  
   682  			rc := NewResourceConfig(rawC)
   683  			rc.interpolateForce()
   684  
   685  			t.Logf("Config: %#v", rc)
   686  
   687  			errs := rc.CheckSet(tc.Input)
   688  			if tc.Errs != (len(errs) > 0) {
   689  				t.Fatalf("bad: %#v", errs)
   690  			}
   691  		})
   692  	}
   693  }
   694  
   695  func TestResourceConfigDeepCopy_nil(t *testing.T) {
   696  	var nilRc *ResourceConfig
   697  	actual := nilRc.DeepCopy()
   698  	if actual != nil {
   699  		t.Fatalf("bad: %#v", actual)
   700  	}
   701  }
   702  
   703  func TestResourceConfigDeepCopy_nilComputed(t *testing.T) {
   704  	rc := &ResourceConfig{}
   705  	actual := rc.DeepCopy()
   706  	if actual.ComputedKeys != nil {
   707  		t.Fatalf("bad: %#v", actual)
   708  	}
   709  }
   710  
   711  func TestResourceConfigEqual_nil(t *testing.T) {
   712  	var nilRc *ResourceConfig
   713  	notNil := NewResourceConfig(nil)
   714  
   715  	if nilRc.Equal(notNil) {
   716  		t.Fatal("should not be equal")
   717  	}
   718  
   719  	if notNil.Equal(nilRc) {
   720  		t.Fatal("should not be equal")
   721  	}
   722  }
   723  
   724  func TestResourceConfigEqual_computedKeyOrder(t *testing.T) {
   725  	c := map[string]interface{}{"foo": "${a.b.c}"}
   726  	rc := NewResourceConfig(config.TestRawConfig(t, c))
   727  	rc2 := NewResourceConfig(config.TestRawConfig(t, c))
   728  
   729  	// Set the computed keys manual
   730  	rc.ComputedKeys = []string{"foo", "bar"}
   731  	rc2.ComputedKeys = []string{"bar", "foo"}
   732  
   733  	if !rc.Equal(rc2) {
   734  		t.Fatal("should be equal")
   735  	}
   736  }
   737  
   738  func TestUnknownCheckWalker(t *testing.T) {
   739  	cases := []struct {
   740  		Name   string
   741  		Input  interface{}
   742  		Result bool
   743  	}{
   744  		{
   745  			"primitive",
   746  			42,
   747  			false,
   748  		},
   749  
   750  		{
   751  			"primitive computed",
   752  			unknownValue(),
   753  			true,
   754  		},
   755  
   756  		{
   757  			"list",
   758  			[]interface{}{"foo", unknownValue()},
   759  			true,
   760  		},
   761  
   762  		{
   763  			"nested list",
   764  			[]interface{}{
   765  				"foo",
   766  				[]interface{}{unknownValue()},
   767  			},
   768  			true,
   769  		},
   770  	}
   771  
   772  	for i, tc := range cases {
   773  		t.Run(fmt.Sprintf("%d-%s", i, tc.Name), func(t *testing.T) {
   774  			var w unknownCheckWalker
   775  			if err := reflectwalk.Walk(tc.Input, &w); err != nil {
   776  				t.Fatalf("err: %s", err)
   777  			}
   778  
   779  			if w.Unknown != tc.Result {
   780  				t.Fatalf("bad: %v", w.Unknown)
   781  			}
   782  		})
   783  	}
   784  }
   785  
   786  func testResourceConfig(
   787  	t *testing.T, c map[string]interface{}) *ResourceConfig {
   788  	raw, err := config.NewRawConfig(c)
   789  	if err != nil {
   790  		t.Fatalf("err: %s", err)
   791  	}
   792  
   793  	return NewResourceConfig(raw)
   794  }