github.com/recobe182/terraform@v0.8.5-0.20170117231232-49ab22a935b7/terraform/resource_test.go (about)

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