github.com/bpineau/terraform@v0.8.0-rc1.0.20161126184705-a8886012d185/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  		// FIXME: this is ambiguous, and matches the nested map
   176  		//        leaving here to catch this behaviour if it changes.
   177  		{
   178  			Config: map[string]interface{}{
   179  				"mapname": []map[string]interface{}{
   180  					map[string]interface{}{
   181  						"key.name":   1,
   182  						"key.name.0": 2,
   183  						"key":        map[string]interface{}{"name": 3},
   184  					},
   185  				},
   186  			},
   187  			Key:   "mapname.0.key.name",
   188  			Value: 3,
   189  		},
   190  		/*
   191  			// TODO: can't access this nested list at all.
   192  			// FIXME: key with name matching substring of nested list can panic
   193  			{
   194  				Config: map[string]interface{}{
   195  					"mapname": []map[string]interface{}{
   196  						map[string]interface{}{
   197  							"key.name": []map[string]interface{}{
   198  								{"subkey": 1},
   199  							},
   200  							"key": 3,
   201  						},
   202  					},
   203  				},
   204  				Key:   "mapname.0.key.name.0.subkey",
   205  				Value: 3,
   206  			},
   207  		*/
   208  	}
   209  
   210  	for i, tc := range cases {
   211  		var rawC *config.RawConfig
   212  		if tc.Config != nil {
   213  			var err error
   214  			rawC, err = config.NewRawConfig(tc.Config)
   215  			if err != nil {
   216  				t.Fatalf("err: %s", err)
   217  			}
   218  		}
   219  
   220  		if tc.Vars != nil {
   221  			vs := make(map[string]ast.Variable)
   222  			for k, v := range tc.Vars {
   223  				hilVar, err := hil.InterfaceToVariable(v)
   224  				if err != nil {
   225  					t.Fatalf("%#v to var: %s", v, err)
   226  				}
   227  
   228  				vs["var."+k] = hilVar
   229  			}
   230  
   231  			if err := rawC.Interpolate(vs); err != nil {
   232  				t.Fatalf("err: %s", err)
   233  			}
   234  		}
   235  
   236  		rc := NewResourceConfig(rawC)
   237  		rc.interpolateForce()
   238  
   239  		// Test getting a key
   240  		t.Run(fmt.Sprintf("get-%d", i), func(t *testing.T) {
   241  			v, ok := rc.Get(tc.Key)
   242  			if ok && v == nil {
   243  				t.Fatal("(nil, true) returned from Get")
   244  			}
   245  
   246  			if !reflect.DeepEqual(v, tc.Value) {
   247  				t.Fatalf("%d bad: %#v", i, v)
   248  			}
   249  		})
   250  
   251  		// If we have vars, we don't test copying
   252  		if len(tc.Vars) > 0 {
   253  			continue
   254  		}
   255  
   256  		// Test copying and equality
   257  		t.Run(fmt.Sprintf("copy-and-equal-%d", i), func(t *testing.T) {
   258  			copy := rc.DeepCopy()
   259  			if !reflect.DeepEqual(copy, rc) {
   260  				t.Fatalf("bad:\n\n%#v\n\n%#v", copy, rc)
   261  			}
   262  
   263  			if !copy.Equal(rc) {
   264  				t.Fatalf("copy != rc:\n\n%#v\n\n%#v", copy, rc)
   265  			}
   266  			if !rc.Equal(copy) {
   267  				t.Fatalf("rc != copy:\n\n%#v\n\n%#v", copy, rc)
   268  			}
   269  		})
   270  	}
   271  }
   272  
   273  func TestResourceConfigIsComputed(t *testing.T) {
   274  	cases := []struct {
   275  		Name   string
   276  		Config map[string]interface{}
   277  		Vars   map[string]interface{}
   278  		Key    string
   279  		Result bool
   280  	}{
   281  		{
   282  			Name: "basic value",
   283  			Config: map[string]interface{}{
   284  				"foo": "${var.foo}",
   285  			},
   286  			Vars: map[string]interface{}{
   287  				"foo": unknownValue(),
   288  			},
   289  			Key:    "foo",
   290  			Result: true,
   291  		},
   292  
   293  		{
   294  			Name: "set with a computed element",
   295  			Config: map[string]interface{}{
   296  				"foo": "${var.foo}",
   297  			},
   298  			Vars: map[string]interface{}{
   299  				"foo": []string{
   300  					"a",
   301  					unknownValue(),
   302  				},
   303  			},
   304  			Key:    "foo",
   305  			Result: true,
   306  		},
   307  
   308  		{
   309  			Name: "set with no computed elements",
   310  			Config: map[string]interface{}{
   311  				"foo": "${var.foo}",
   312  			},
   313  			Vars: map[string]interface{}{
   314  				"foo": []string{
   315  					"a",
   316  					"b",
   317  				},
   318  			},
   319  			Key:    "foo",
   320  			Result: false,
   321  		},
   322  
   323  		/*
   324  			{
   325  				Name: "set count with computed elements",
   326  				Config: map[string]interface{}{
   327  					"foo": "${var.foo}",
   328  				},
   329  				Vars: map[string]interface{}{
   330  					"foo": []string{
   331  						"a",
   332  						unknownValue(),
   333  					},
   334  				},
   335  				Key:    "foo.#",
   336  				Result: true,
   337  			},
   338  		*/
   339  
   340  		{
   341  			Name: "set count with computed elements",
   342  			Config: map[string]interface{}{
   343  				"foo": []interface{}{"${var.foo}"},
   344  			},
   345  			Vars: map[string]interface{}{
   346  				"foo": []string{
   347  					"a",
   348  					unknownValue(),
   349  				},
   350  			},
   351  			Key:    "foo.#",
   352  			Result: true,
   353  		},
   354  
   355  		{
   356  			Name: "nested set with computed elements",
   357  			Config: map[string]interface{}{
   358  				"route": []map[string]interface{}{
   359  					map[string]interface{}{
   360  						"index":   "1",
   361  						"gateway": []interface{}{"${var.foo}"},
   362  					},
   363  				},
   364  			},
   365  			Vars: map[string]interface{}{
   366  				"foo": unknownValue(),
   367  			},
   368  			Key:    "route.0.gateway",
   369  			Result: true,
   370  		},
   371  	}
   372  
   373  	for i, tc := range cases {
   374  		t.Run(fmt.Sprintf("%d-%s", i, tc.Name), func(t *testing.T) {
   375  			var rawC *config.RawConfig
   376  			if tc.Config != nil {
   377  				var err error
   378  				rawC, err = config.NewRawConfig(tc.Config)
   379  				if err != nil {
   380  					t.Fatalf("err: %s", err)
   381  				}
   382  			}
   383  
   384  			if tc.Vars != nil {
   385  				vs := make(map[string]ast.Variable)
   386  				for k, v := range tc.Vars {
   387  					hilVar, err := hil.InterfaceToVariable(v)
   388  					if err != nil {
   389  						t.Fatalf("%#v to var: %s", v, err)
   390  					}
   391  
   392  					vs["var."+k] = hilVar
   393  				}
   394  
   395  				if err := rawC.Interpolate(vs); err != nil {
   396  					t.Fatalf("err: %s", err)
   397  				}
   398  			}
   399  
   400  			rc := NewResourceConfig(rawC)
   401  			rc.interpolateForce()
   402  
   403  			t.Logf("Config: %#v", rc)
   404  
   405  			actual := rc.IsComputed(tc.Key)
   406  			if actual != tc.Result {
   407  				t.Fatalf("bad: %#v", actual)
   408  			}
   409  		})
   410  	}
   411  }
   412  
   413  func TestResourceConfigCheckSet(t *testing.T) {
   414  	cases := []struct {
   415  		Name   string
   416  		Config map[string]interface{}
   417  		Vars   map[string]interface{}
   418  		Input  []string
   419  		Errs   bool
   420  	}{
   421  		{
   422  			Name: "computed basic",
   423  			Config: map[string]interface{}{
   424  				"foo": "${var.foo}",
   425  			},
   426  			Vars: map[string]interface{}{
   427  				"foo": unknownValue(),
   428  			},
   429  			Input: []string{"foo"},
   430  			Errs:  false,
   431  		},
   432  
   433  		{
   434  			Name: "basic",
   435  			Config: map[string]interface{}{
   436  				"foo": "bar",
   437  			},
   438  			Vars:  nil,
   439  			Input: []string{"foo"},
   440  			Errs:  false,
   441  		},
   442  
   443  		{
   444  			Name: "basic with not set",
   445  			Config: map[string]interface{}{
   446  				"foo": "bar",
   447  			},
   448  			Vars:  nil,
   449  			Input: []string{"foo", "bar"},
   450  			Errs:  true,
   451  		},
   452  
   453  		{
   454  			Name: "basic with one computed",
   455  			Config: map[string]interface{}{
   456  				"foo": "bar",
   457  				"bar": "${var.foo}",
   458  			},
   459  			Vars: map[string]interface{}{
   460  				"foo": unknownValue(),
   461  			},
   462  			Input: []string{"foo", "bar"},
   463  			Errs:  false,
   464  		},
   465  	}
   466  
   467  	for i, tc := range cases {
   468  		t.Run(fmt.Sprintf("%d-%s", i, tc.Name), func(t *testing.T) {
   469  			var rawC *config.RawConfig
   470  			if tc.Config != nil {
   471  				var err error
   472  				rawC, err = config.NewRawConfig(tc.Config)
   473  				if err != nil {
   474  					t.Fatalf("err: %s", err)
   475  				}
   476  			}
   477  
   478  			if tc.Vars != nil {
   479  				vs := make(map[string]ast.Variable)
   480  				for k, v := range tc.Vars {
   481  					hilVar, err := hil.InterfaceToVariable(v)
   482  					if err != nil {
   483  						t.Fatalf("%#v to var: %s", v, err)
   484  					}
   485  
   486  					vs["var."+k] = hilVar
   487  				}
   488  
   489  				if err := rawC.Interpolate(vs); err != nil {
   490  					t.Fatalf("err: %s", err)
   491  				}
   492  			}
   493  
   494  			rc := NewResourceConfig(rawC)
   495  			rc.interpolateForce()
   496  
   497  			t.Logf("Config: %#v", rc)
   498  
   499  			errs := rc.CheckSet(tc.Input)
   500  			if tc.Errs != (len(errs) > 0) {
   501  				t.Fatalf("bad: %#v", errs)
   502  			}
   503  		})
   504  	}
   505  }
   506  
   507  func TestResourceConfigDeepCopy_nil(t *testing.T) {
   508  	var nilRc *ResourceConfig
   509  	actual := nilRc.DeepCopy()
   510  	if actual != nil {
   511  		t.Fatalf("bad: %#v", actual)
   512  	}
   513  }
   514  
   515  func TestResourceConfigDeepCopy_nilComputed(t *testing.T) {
   516  	rc := &ResourceConfig{}
   517  	actual := rc.DeepCopy()
   518  	if actual.ComputedKeys != nil {
   519  		t.Fatalf("bad: %#v", actual)
   520  	}
   521  }
   522  
   523  func TestResourceConfigEqual_nil(t *testing.T) {
   524  	var nilRc *ResourceConfig
   525  	notNil := NewResourceConfig(nil)
   526  
   527  	if nilRc.Equal(notNil) {
   528  		t.Fatal("should not be equal")
   529  	}
   530  
   531  	if notNil.Equal(nilRc) {
   532  		t.Fatal("should not be equal")
   533  	}
   534  }
   535  
   536  func TestResourceConfigEqual_computedKeyOrder(t *testing.T) {
   537  	c := map[string]interface{}{"foo": "${a.b.c}"}
   538  	rc := NewResourceConfig(config.TestRawConfig(t, c))
   539  	rc2 := NewResourceConfig(config.TestRawConfig(t, c))
   540  
   541  	// Set the computed keys manual
   542  	rc.ComputedKeys = []string{"foo", "bar"}
   543  	rc2.ComputedKeys = []string{"bar", "foo"}
   544  
   545  	if !rc.Equal(rc2) {
   546  		t.Fatal("should be equal")
   547  	}
   548  }
   549  
   550  func TestUnknownCheckWalker(t *testing.T) {
   551  	cases := []struct {
   552  		Name   string
   553  		Input  interface{}
   554  		Result bool
   555  	}{
   556  		{
   557  			"primitive",
   558  			42,
   559  			false,
   560  		},
   561  
   562  		{
   563  			"primitive computed",
   564  			unknownValue(),
   565  			true,
   566  		},
   567  
   568  		{
   569  			"list",
   570  			[]interface{}{"foo", unknownValue()},
   571  			true,
   572  		},
   573  
   574  		{
   575  			"nested list",
   576  			[]interface{}{
   577  				"foo",
   578  				[]interface{}{unknownValue()},
   579  			},
   580  			true,
   581  		},
   582  	}
   583  
   584  	for i, tc := range cases {
   585  		t.Run(fmt.Sprintf("%d-%s", i, tc.Name), func(t *testing.T) {
   586  			var w unknownCheckWalker
   587  			if err := reflectwalk.Walk(tc.Input, &w); err != nil {
   588  				t.Fatalf("err: %s", err)
   589  			}
   590  
   591  			if w.Unknown != tc.Result {
   592  				t.Fatalf("bad: %v", w.Unknown)
   593  			}
   594  		})
   595  	}
   596  }
   597  
   598  func testResourceConfig(
   599  	t *testing.T, c map[string]interface{}) *ResourceConfig {
   600  	raw, err := config.NewRawConfig(c)
   601  	if err != nil {
   602  		t.Fatalf("err: %s", err)
   603  	}
   604  
   605  	return NewResourceConfig(raw)
   606  }