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