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