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