github.com/icebourg/terraform@v0.6.5-0.20151015205227-263cc1b85535/config/interpolate_funcs_test.go (about)

     1  package config
     2  
     3  import (
     4  	"fmt"
     5  	"io/ioutil"
     6  	"os"
     7  	"reflect"
     8  	"testing"
     9  
    10  	"github.com/hashicorp/terraform/config/lang"
    11  	"github.com/hashicorp/terraform/config/lang/ast"
    12  )
    13  
    14  func TestInterpolateFuncCompact(t *testing.T) {
    15  	testFunction(t, testFunctionConfig{
    16  		Cases: []testFunctionCase{
    17  			// empty string within array
    18  			{
    19  				`${compact(split(",", "a,,b"))}`,
    20  				NewStringList([]string{"a", "b"}).String(),
    21  				false,
    22  			},
    23  
    24  			// empty string at the end of array
    25  			{
    26  				`${compact(split(",", "a,b,"))}`,
    27  				NewStringList([]string{"a", "b"}).String(),
    28  				false,
    29  			},
    30  
    31  			// single empty string
    32  			{
    33  				`${compact(split(",", ""))}`,
    34  				NewStringList([]string{}).String(),
    35  				false,
    36  			},
    37  		},
    38  	})
    39  }
    40  
    41  func TestInterpolateFuncDeprecatedConcat(t *testing.T) {
    42  	testFunction(t, testFunctionConfig{
    43  		Cases: []testFunctionCase{
    44  			{
    45  				`${concat("foo", "bar")}`,
    46  				"foobar",
    47  				false,
    48  			},
    49  
    50  			{
    51  				`${concat("foo")}`,
    52  				"foo",
    53  				false,
    54  			},
    55  
    56  			{
    57  				`${concat()}`,
    58  				nil,
    59  				true,
    60  			},
    61  		},
    62  	})
    63  }
    64  
    65  func TestInterpolateFuncConcat(t *testing.T) {
    66  	testFunction(t, testFunctionConfig{
    67  		Cases: []testFunctionCase{
    68  			// String + list
    69  			{
    70  				`${concat("a", split(",", "b,c"))}`,
    71  				NewStringList([]string{"a", "b", "c"}).String(),
    72  				false,
    73  			},
    74  
    75  			// List + string
    76  			{
    77  				`${concat(split(",", "a,b"), "c")}`,
    78  				NewStringList([]string{"a", "b", "c"}).String(),
    79  				false,
    80  			},
    81  
    82  			// Single list
    83  			{
    84  				`${concat(split(",", ",foo,"))}`,
    85  				NewStringList([]string{"", "foo", ""}).String(),
    86  				false,
    87  			},
    88  			{
    89  				`${concat(split(",", "a,b,c"))}`,
    90  				NewStringList([]string{"a", "b", "c"}).String(),
    91  				false,
    92  			},
    93  
    94  			// Two lists
    95  			{
    96  				`${concat(split(",", "a,b,c"), split(",", "d,e"))}`,
    97  				NewStringList([]string{"a", "b", "c", "d", "e"}).String(),
    98  				false,
    99  			},
   100  			// Two lists with different separators
   101  			{
   102  				`${concat(split(",", "a,b,c"), split(" ", "d e"))}`,
   103  				NewStringList([]string{"a", "b", "c", "d", "e"}).String(),
   104  				false,
   105  			},
   106  
   107  			// More lists
   108  			{
   109  				`${concat(split(",", "a,b"), split(",", "c,d"), split(",", "e,f"), split(",", "0,1"))}`,
   110  				NewStringList([]string{"a", "b", "c", "d", "e", "f", "0", "1"}).String(),
   111  				false,
   112  			},
   113  		},
   114  	})
   115  }
   116  
   117  func TestInterpolateFuncFile(t *testing.T) {
   118  	tf, err := ioutil.TempFile("", "tf")
   119  	if err != nil {
   120  		t.Fatalf("err: %s", err)
   121  	}
   122  	path := tf.Name()
   123  	tf.Write([]byte("foo"))
   124  	tf.Close()
   125  	defer os.Remove(path)
   126  
   127  	testFunction(t, testFunctionConfig{
   128  		Cases: []testFunctionCase{
   129  			{
   130  				fmt.Sprintf(`${file("%s")}`, path),
   131  				"foo",
   132  				false,
   133  			},
   134  
   135  			// Invalid path
   136  			{
   137  				`${file("/i/dont/exist")}`,
   138  				nil,
   139  				true,
   140  			},
   141  
   142  			// Too many args
   143  			{
   144  				`${file("foo", "bar")}`,
   145  				nil,
   146  				true,
   147  			},
   148  		},
   149  	})
   150  }
   151  
   152  func TestInterpolateFuncFormat(t *testing.T) {
   153  	testFunction(t, testFunctionConfig{
   154  		Cases: []testFunctionCase{
   155  			{
   156  				`${format("hello")}`,
   157  				"hello",
   158  				false,
   159  			},
   160  
   161  			{
   162  				`${format("hello %s", "world")}`,
   163  				"hello world",
   164  				false,
   165  			},
   166  
   167  			{
   168  				`${format("hello %d", 42)}`,
   169  				"hello 42",
   170  				false,
   171  			},
   172  
   173  			{
   174  				`${format("hello %05d", 42)}`,
   175  				"hello 00042",
   176  				false,
   177  			},
   178  
   179  			{
   180  				`${format("hello %05d", 12345)}`,
   181  				"hello 12345",
   182  				false,
   183  			},
   184  		},
   185  	})
   186  }
   187  
   188  func TestInterpolateFuncFormatList(t *testing.T) {
   189  	testFunction(t, testFunctionConfig{
   190  		Cases: []testFunctionCase{
   191  			// formatlist requires at least one list
   192  			{
   193  				`${formatlist("hello")}`,
   194  				nil,
   195  				true,
   196  			},
   197  			{
   198  				`${formatlist("hello %s", "world")}`,
   199  				nil,
   200  				true,
   201  			},
   202  			// formatlist applies to each list element in turn
   203  			{
   204  				`${formatlist("<%s>", split(",", "A,B"))}`,
   205  				NewStringList([]string{"<A>", "<B>"}).String(),
   206  				false,
   207  			},
   208  			// formatlist repeats scalar elements
   209  			{
   210  				`${join(", ", formatlist("%s=%s", "x", split(",", "A,B,C")))}`,
   211  				"x=A, x=B, x=C",
   212  				false,
   213  			},
   214  			// Multiple lists are walked in parallel
   215  			{
   216  				`${join(", ", formatlist("%s=%s", split(",", "A,B,C"), split(",", "1,2,3")))}`,
   217  				"A=1, B=2, C=3",
   218  				false,
   219  			},
   220  			// Mismatched list lengths generate an error
   221  			{
   222  				`${formatlist("%s=%2s", split(",", "A,B,C,D"), split(",", "1,2,3"))}`,
   223  				nil,
   224  				true,
   225  			},
   226  			// Works with lists of length 1 [GH-2240]
   227  			{
   228  				`${formatlist("%s.id", split(",", "demo-rest-elb"))}`,
   229  				NewStringList([]string{"demo-rest-elb.id"}).String(),
   230  				false,
   231  			},
   232  		},
   233  	})
   234  }
   235  
   236  func TestInterpolateFuncIndex(t *testing.T) {
   237  	testFunction(t, testFunctionConfig{
   238  		Cases: []testFunctionCase{
   239  			{
   240  				`${index("test", "")}`,
   241  				nil,
   242  				true,
   243  			},
   244  
   245  			{
   246  				fmt.Sprintf(`${index("%s", "foo")}`,
   247  					NewStringList([]string{"notfoo", "stillnotfoo", "bar"}).String()),
   248  				nil,
   249  				true,
   250  			},
   251  
   252  			{
   253  				fmt.Sprintf(`${index("%s", "foo")}`,
   254  					NewStringList([]string{"foo"}).String()),
   255  				"0",
   256  				false,
   257  			},
   258  
   259  			{
   260  				fmt.Sprintf(`${index("%s", "bar")}`,
   261  					NewStringList([]string{"foo", "spam", "bar", "eggs"}).String()),
   262  				"2",
   263  				false,
   264  			},
   265  		},
   266  	})
   267  }
   268  
   269  func TestInterpolateFuncJoin(t *testing.T) {
   270  	testFunction(t, testFunctionConfig{
   271  		Cases: []testFunctionCase{
   272  			{
   273  				`${join(",")}`,
   274  				nil,
   275  				true,
   276  			},
   277  
   278  			{
   279  				fmt.Sprintf(`${join(",", "%s")}`,
   280  					NewStringList([]string{"foo"}).String()),
   281  				"foo",
   282  				false,
   283  			},
   284  
   285  			/*
   286  				TODO
   287  				{
   288  					`${join(",", "foo", "bar")}`,
   289  					"foo,bar",
   290  					false,
   291  				},
   292  			*/
   293  
   294  			{
   295  				fmt.Sprintf(`${join(".", "%s")}`,
   296  					NewStringList([]string{"foo", "bar", "baz"}).String()),
   297  				"foo.bar.baz",
   298  				false,
   299  			},
   300  		},
   301  	})
   302  }
   303  
   304  func TestInterpolateFuncReplace(t *testing.T) {
   305  	testFunction(t, testFunctionConfig{
   306  		Cases: []testFunctionCase{
   307  			// Regular search and replace
   308  			{
   309  				`${replace("hello", "hel", "bel")}`,
   310  				"bello",
   311  				false,
   312  			},
   313  
   314  			// Search string doesn't match
   315  			{
   316  				`${replace("hello", "nope", "bel")}`,
   317  				"hello",
   318  				false,
   319  			},
   320  
   321  			// Regular expression
   322  			{
   323  				`${replace("hello", "/l/", "L")}`,
   324  				"heLLo",
   325  				false,
   326  			},
   327  
   328  			{
   329  				`${replace("helo", "/(l)/", "$1$1")}`,
   330  				"hello",
   331  				false,
   332  			},
   333  
   334  			// Bad regexp
   335  			{
   336  				`${replace("helo", "/(l/", "$1$1")}`,
   337  				nil,
   338  				true,
   339  			},
   340  		},
   341  	})
   342  }
   343  
   344  func TestInterpolateFuncLength(t *testing.T) {
   345  	testFunction(t, testFunctionConfig{
   346  		Cases: []testFunctionCase{
   347  			// Raw strings
   348  			{
   349  				`${length("")}`,
   350  				"0",
   351  				false,
   352  			},
   353  			{
   354  				`${length("a")}`,
   355  				"1",
   356  				false,
   357  			},
   358  			{
   359  				`${length(" ")}`,
   360  				"1",
   361  				false,
   362  			},
   363  			{
   364  				`${length(" a ,")}`,
   365  				"4",
   366  				false,
   367  			},
   368  			{
   369  				`${length("aaa")}`,
   370  				"3",
   371  				false,
   372  			},
   373  
   374  			// Lists
   375  			{
   376  				`${length(split(",", "a"))}`,
   377  				"1",
   378  				false,
   379  			},
   380  			{
   381  				`${length(split(",", "foo,"))}`,
   382  				"2",
   383  				false,
   384  			},
   385  			{
   386  				`${length(split(",", ",foo,"))}`,
   387  				"3",
   388  				false,
   389  			},
   390  			{
   391  				`${length(split(",", "foo,bar"))}`,
   392  				"2",
   393  				false,
   394  			},
   395  			{
   396  				`${length(split(".", "one.two.three.four.five"))}`,
   397  				"5",
   398  				false,
   399  			},
   400  		},
   401  	})
   402  }
   403  
   404  func TestInterpolateFuncSplit(t *testing.T) {
   405  	testFunction(t, testFunctionConfig{
   406  		Cases: []testFunctionCase{
   407  			{
   408  				`${split(",")}`,
   409  				nil,
   410  				true,
   411  			},
   412  
   413  			{
   414  				`${split(",", "")}`,
   415  				NewStringList([]string{""}).String(),
   416  				false,
   417  			},
   418  
   419  			{
   420  				`${split(",", "foo")}`,
   421  				NewStringList([]string{"foo"}).String(),
   422  				false,
   423  			},
   424  
   425  			{
   426  				`${split(",", ",,,")}`,
   427  				NewStringList([]string{"", "", "", ""}).String(),
   428  				false,
   429  			},
   430  
   431  			{
   432  				`${split(",", "foo,")}`,
   433  				NewStringList([]string{"foo", ""}).String(),
   434  				false,
   435  			},
   436  
   437  			{
   438  				`${split(",", ",foo,")}`,
   439  				NewStringList([]string{"", "foo", ""}).String(),
   440  				false,
   441  			},
   442  
   443  			{
   444  				`${split(".", "foo.bar.baz")}`,
   445  				NewStringList([]string{"foo", "bar", "baz"}).String(),
   446  				false,
   447  			},
   448  		},
   449  	})
   450  }
   451  
   452  func TestInterpolateFuncLookup(t *testing.T) {
   453  	testFunction(t, testFunctionConfig{
   454  		Vars: map[string]ast.Variable{
   455  			"var.foo.bar": ast.Variable{
   456  				Value: "baz",
   457  				Type:  ast.TypeString,
   458  			},
   459  		},
   460  		Cases: []testFunctionCase{
   461  			{
   462  				`${lookup("foo", "bar")}`,
   463  				"baz",
   464  				false,
   465  			},
   466  
   467  			// Invalid key
   468  			{
   469  				`${lookup("foo", "baz")}`,
   470  				nil,
   471  				true,
   472  			},
   473  
   474  			// Too many args
   475  			{
   476  				`${lookup("foo", "bar", "baz")}`,
   477  				nil,
   478  				true,
   479  			},
   480  		},
   481  	})
   482  }
   483  
   484  func TestInterpolateFuncKeys(t *testing.T) {
   485  	testFunction(t, testFunctionConfig{
   486  		Vars: map[string]ast.Variable{
   487  			"var.foo.bar": ast.Variable{
   488  				Value: "baz",
   489  				Type:  ast.TypeString,
   490  			},
   491  			"var.foo.qux": ast.Variable{
   492  				Value: "quack",
   493  				Type:  ast.TypeString,
   494  			},
   495  			"var.str": ast.Variable{
   496  				Value: "astring",
   497  				Type:  ast.TypeString,
   498  			},
   499  		},
   500  		Cases: []testFunctionCase{
   501  			{
   502  				`${keys("foo")}`,
   503  				NewStringList([]string{"bar", "qux"}).String(),
   504  				false,
   505  			},
   506  
   507  			// Invalid key
   508  			{
   509  				`${keys("not")}`,
   510  				nil,
   511  				true,
   512  			},
   513  
   514  			// Too many args
   515  			{
   516  				`${keys("foo", "bar")}`,
   517  				nil,
   518  				true,
   519  			},
   520  
   521  			// Not a map
   522  			{
   523  				`${keys("str")}`,
   524  				nil,
   525  				true,
   526  			},
   527  		},
   528  	})
   529  }
   530  
   531  func TestInterpolateFuncValues(t *testing.T) {
   532  	testFunction(t, testFunctionConfig{
   533  		Vars: map[string]ast.Variable{
   534  			"var.foo.bar": ast.Variable{
   535  				Value: "quack",
   536  				Type:  ast.TypeString,
   537  			},
   538  			"var.foo.qux": ast.Variable{
   539  				Value: "baz",
   540  				Type:  ast.TypeString,
   541  			},
   542  			"var.str": ast.Variable{
   543  				Value: "astring",
   544  				Type:  ast.TypeString,
   545  			},
   546  		},
   547  		Cases: []testFunctionCase{
   548  			{
   549  				`${values("foo")}`,
   550  				NewStringList([]string{"quack", "baz"}).String(),
   551  				false,
   552  			},
   553  
   554  			// Invalid key
   555  			{
   556  				`${values("not")}`,
   557  				nil,
   558  				true,
   559  			},
   560  
   561  			// Too many args
   562  			{
   563  				`${values("foo", "bar")}`,
   564  				nil,
   565  				true,
   566  			},
   567  
   568  			// Not a map
   569  			{
   570  				`${values("str")}`,
   571  				nil,
   572  				true,
   573  			},
   574  		},
   575  	})
   576  }
   577  
   578  func TestInterpolateFuncElement(t *testing.T) {
   579  	testFunction(t, testFunctionConfig{
   580  		Cases: []testFunctionCase{
   581  			{
   582  				fmt.Sprintf(`${element("%s", "1")}`,
   583  					NewStringList([]string{"foo", "baz"}).String()),
   584  				"baz",
   585  				false,
   586  			},
   587  
   588  			{
   589  				fmt.Sprintf(`${element("%s", "0")}`,
   590  					NewStringList([]string{"foo"}).String()),
   591  				"foo",
   592  				false,
   593  			},
   594  
   595  			// Invalid index should wrap vs. out-of-bounds
   596  			{
   597  				fmt.Sprintf(`${element("%s", "2")}`,
   598  					NewStringList([]string{"foo", "baz"}).String()),
   599  				"foo",
   600  				false,
   601  			},
   602  
   603  			// Too many args
   604  			{
   605  				fmt.Sprintf(`${element("%s", "0", "2")}`,
   606  					NewStringList([]string{"foo", "baz"}).String()),
   607  				nil,
   608  				true,
   609  			},
   610  		},
   611  	})
   612  }
   613  
   614  func TestInterpolateFuncBase64Encode(t *testing.T) {
   615  	testFunction(t, testFunctionConfig{
   616  		Cases: []testFunctionCase{
   617  			// Regular base64 encoding
   618  			{
   619  				`${base64encode("abc123!?$*&()'-=@~")}`,
   620  				"YWJjMTIzIT8kKiYoKSctPUB+",
   621  				false,
   622  			},
   623  		},
   624  	})
   625  }
   626  
   627  func TestInterpolateFuncBase64Decode(t *testing.T) {
   628  	testFunction(t, testFunctionConfig{
   629  		Cases: []testFunctionCase{
   630  			// Regular base64 decoding
   631  			{
   632  				`${base64decode("YWJjMTIzIT8kKiYoKSctPUB+")}`,
   633  				"abc123!?$*&()'-=@~",
   634  				false,
   635  			},
   636  
   637  			// Invalid base64 data decoding
   638  			{
   639  				`${base64decode("this-is-an-invalid-base64-data")}`,
   640  				nil,
   641  				true,
   642  			},
   643  		},
   644  	})
   645  }
   646  
   647  type testFunctionConfig struct {
   648  	Cases []testFunctionCase
   649  	Vars  map[string]ast.Variable
   650  }
   651  
   652  type testFunctionCase struct {
   653  	Input  string
   654  	Result interface{}
   655  	Error  bool
   656  }
   657  
   658  func testFunction(t *testing.T, config testFunctionConfig) {
   659  	for i, tc := range config.Cases {
   660  		ast, err := lang.Parse(tc.Input)
   661  		if err != nil {
   662  			t.Fatalf("Case #%d: input: %#v\nerr: %s", i, tc.Input, err)
   663  		}
   664  
   665  		out, _, err := lang.Eval(ast, langEvalConfig(config.Vars))
   666  		if err != nil != tc.Error {
   667  			t.Fatalf("Case #%d:\ninput: %#v\nerr: %s", i, tc.Input, err)
   668  		}
   669  
   670  		if !reflect.DeepEqual(out, tc.Result) {
   671  			t.Fatalf(
   672  				"%d: bad output for input: %s\n\nOutput: %#v\nExpected: %#v",
   673  				i, tc.Input, out, tc.Result)
   674  		}
   675  	}
   676  }