github.com/jsoriano/terraform@v0.6.7-0.20151026070445-8b70867fdd95/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 TestInterpolateFuncCidrHost(t *testing.T) {
    42  	testFunction(t, testFunctionConfig{
    43  		Cases: []testFunctionCase{
    44  			{
    45  				`${cidrhost("192.168.1.0/24", 5)}`,
    46  				"192.168.1.5",
    47  				false,
    48  			},
    49  			{
    50  				`${cidrhost("192.168.1.0/30", 255)}`,
    51  				nil,
    52  				true, // 255 doesn't fit in two bits
    53  			},
    54  			{
    55  				`${cidrhost("not-a-cidr", 6)}`,
    56  				nil,
    57  				true, // not a valid CIDR mask
    58  			},
    59  			{
    60  				`${cidrhost("10.256.0.0/8", 6)}`,
    61  				nil,
    62  				true, // can't have an octet >255
    63  			},
    64  		},
    65  	})
    66  }
    67  
    68  func TestInterpolateFuncCidrNetmask(t *testing.T) {
    69  	testFunction(t, testFunctionConfig{
    70  		Cases: []testFunctionCase{
    71  			{
    72  				`${cidrnetmask("192.168.1.0/24")}`,
    73  				"255.255.255.0",
    74  				false,
    75  			},
    76  			{
    77  				`${cidrnetmask("192.168.1.0/32")}`,
    78  				"255.255.255.255",
    79  				false,
    80  			},
    81  			{
    82  				`${cidrnetmask("0.0.0.0/0")}`,
    83  				"0.0.0.0",
    84  				false,
    85  			},
    86  			{
    87  				// This doesn't really make sense for IPv6 networks
    88  				// but it ought to do something sensible anyway.
    89  				`${cidrnetmask("1::/64")}`,
    90  				"ffff:ffff:ffff:ffff::",
    91  				false,
    92  			},
    93  			{
    94  				`${cidrnetmask("not-a-cidr")}`,
    95  				nil,
    96  				true, // not a valid CIDR mask
    97  			},
    98  			{
    99  				`${cidrnetmask("10.256.0.0/8")}`,
   100  				nil,
   101  				true, // can't have an octet >255
   102  			},
   103  		},
   104  	})
   105  }
   106  
   107  func TestInterpolateFuncCidrSubnet(t *testing.T) {
   108  	testFunction(t, testFunctionConfig{
   109  		Cases: []testFunctionCase{
   110  			{
   111  				`${cidrsubnet("192.168.2.0/20", 4, 6)}`,
   112  				"192.168.6.0/24",
   113  				false,
   114  			},
   115  			{
   116  				`${cidrsubnet("fe80::/48", 16, 6)}`,
   117  				"fe80:0:0:6::/64",
   118  				false,
   119  			},
   120  			{
   121  				// IPv4 address encoded in IPv6 syntax gets normalized
   122  				`${cidrsubnet("::ffff:192.168.0.0/112", 8, 6)}`,
   123  				"192.168.6.0/24",
   124  				false,
   125  			},
   126  			{
   127  				`${cidrsubnet("192.168.0.0/30", 4, 6)}`,
   128  				nil,
   129  				true, // not enough bits left
   130  			},
   131  			{
   132  				`${cidrsubnet("192.168.0.0/16", 2, 16)}`,
   133  				nil,
   134  				true, // can't encode 16 in 2 bits
   135  			},
   136  			{
   137  				`${cidrsubnet("not-a-cidr", 4, 6)}`,
   138  				nil,
   139  				true, // not a valid CIDR mask
   140  			},
   141  			{
   142  				`${cidrsubnet("10.256.0.0/8", 4, 6)}`,
   143  				nil,
   144  				true, // can't have an octet >255
   145  			},
   146  		},
   147  	})
   148  }
   149  
   150  func TestInterpolateFuncDeprecatedConcat(t *testing.T) {
   151  	testFunction(t, testFunctionConfig{
   152  		Cases: []testFunctionCase{
   153  			{
   154  				`${concat("foo", "bar")}`,
   155  				"foobar",
   156  				false,
   157  			},
   158  
   159  			{
   160  				`${concat("foo")}`,
   161  				"foo",
   162  				false,
   163  			},
   164  
   165  			{
   166  				`${concat()}`,
   167  				nil,
   168  				true,
   169  			},
   170  		},
   171  	})
   172  }
   173  
   174  func TestInterpolateFuncConcat(t *testing.T) {
   175  	testFunction(t, testFunctionConfig{
   176  		Cases: []testFunctionCase{
   177  			// String + list
   178  			{
   179  				`${concat("a", split(",", "b,c"))}`,
   180  				NewStringList([]string{"a", "b", "c"}).String(),
   181  				false,
   182  			},
   183  
   184  			// List + string
   185  			{
   186  				`${concat(split(",", "a,b"), "c")}`,
   187  				NewStringList([]string{"a", "b", "c"}).String(),
   188  				false,
   189  			},
   190  
   191  			// Single list
   192  			{
   193  				`${concat(split(",", ",foo,"))}`,
   194  				NewStringList([]string{"", "foo", ""}).String(),
   195  				false,
   196  			},
   197  			{
   198  				`${concat(split(",", "a,b,c"))}`,
   199  				NewStringList([]string{"a", "b", "c"}).String(),
   200  				false,
   201  			},
   202  
   203  			// Two lists
   204  			{
   205  				`${concat(split(",", "a,b,c"), split(",", "d,e"))}`,
   206  				NewStringList([]string{"a", "b", "c", "d", "e"}).String(),
   207  				false,
   208  			},
   209  			// Two lists with different separators
   210  			{
   211  				`${concat(split(",", "a,b,c"), split(" ", "d e"))}`,
   212  				NewStringList([]string{"a", "b", "c", "d", "e"}).String(),
   213  				false,
   214  			},
   215  
   216  			// More lists
   217  			{
   218  				`${concat(split(",", "a,b"), split(",", "c,d"), split(",", "e,f"), split(",", "0,1"))}`,
   219  				NewStringList([]string{"a", "b", "c", "d", "e", "f", "0", "1"}).String(),
   220  				false,
   221  			},
   222  		},
   223  	})
   224  }
   225  
   226  func TestInterpolateFuncFile(t *testing.T) {
   227  	tf, err := ioutil.TempFile("", "tf")
   228  	if err != nil {
   229  		t.Fatalf("err: %s", err)
   230  	}
   231  	path := tf.Name()
   232  	tf.Write([]byte("foo"))
   233  	tf.Close()
   234  	defer os.Remove(path)
   235  
   236  	testFunction(t, testFunctionConfig{
   237  		Cases: []testFunctionCase{
   238  			{
   239  				fmt.Sprintf(`${file("%s")}`, path),
   240  				"foo",
   241  				false,
   242  			},
   243  
   244  			// Invalid path
   245  			{
   246  				`${file("/i/dont/exist")}`,
   247  				nil,
   248  				true,
   249  			},
   250  
   251  			// Too many args
   252  			{
   253  				`${file("foo", "bar")}`,
   254  				nil,
   255  				true,
   256  			},
   257  		},
   258  	})
   259  }
   260  
   261  func TestInterpolateFuncFormat(t *testing.T) {
   262  	testFunction(t, testFunctionConfig{
   263  		Cases: []testFunctionCase{
   264  			{
   265  				`${format("hello")}`,
   266  				"hello",
   267  				false,
   268  			},
   269  
   270  			{
   271  				`${format("hello %s", "world")}`,
   272  				"hello world",
   273  				false,
   274  			},
   275  
   276  			{
   277  				`${format("hello %d", 42)}`,
   278  				"hello 42",
   279  				false,
   280  			},
   281  
   282  			{
   283  				`${format("hello %05d", 42)}`,
   284  				"hello 00042",
   285  				false,
   286  			},
   287  
   288  			{
   289  				`${format("hello %05d", 12345)}`,
   290  				"hello 12345",
   291  				false,
   292  			},
   293  		},
   294  	})
   295  }
   296  
   297  func TestInterpolateFuncFormatList(t *testing.T) {
   298  	testFunction(t, testFunctionConfig{
   299  		Cases: []testFunctionCase{
   300  			// formatlist requires at least one list
   301  			{
   302  				`${formatlist("hello")}`,
   303  				nil,
   304  				true,
   305  			},
   306  			{
   307  				`${formatlist("hello %s", "world")}`,
   308  				nil,
   309  				true,
   310  			},
   311  			// formatlist applies to each list element in turn
   312  			{
   313  				`${formatlist("<%s>", split(",", "A,B"))}`,
   314  				NewStringList([]string{"<A>", "<B>"}).String(),
   315  				false,
   316  			},
   317  			// formatlist repeats scalar elements
   318  			{
   319  				`${join(", ", formatlist("%s=%s", "x", split(",", "A,B,C")))}`,
   320  				"x=A, x=B, x=C",
   321  				false,
   322  			},
   323  			// Multiple lists are walked in parallel
   324  			{
   325  				`${join(", ", formatlist("%s=%s", split(",", "A,B,C"), split(",", "1,2,3")))}`,
   326  				"A=1, B=2, C=3",
   327  				false,
   328  			},
   329  			// Mismatched list lengths generate an error
   330  			{
   331  				`${formatlist("%s=%2s", split(",", "A,B,C,D"), split(",", "1,2,3"))}`,
   332  				nil,
   333  				true,
   334  			},
   335  			// Works with lists of length 1 [GH-2240]
   336  			{
   337  				`${formatlist("%s.id", split(",", "demo-rest-elb"))}`,
   338  				NewStringList([]string{"demo-rest-elb.id"}).String(),
   339  				false,
   340  			},
   341  		},
   342  	})
   343  }
   344  
   345  func TestInterpolateFuncIndex(t *testing.T) {
   346  	testFunction(t, testFunctionConfig{
   347  		Cases: []testFunctionCase{
   348  			{
   349  				`${index("test", "")}`,
   350  				nil,
   351  				true,
   352  			},
   353  
   354  			{
   355  				fmt.Sprintf(`${index("%s", "foo")}`,
   356  					NewStringList([]string{"notfoo", "stillnotfoo", "bar"}).String()),
   357  				nil,
   358  				true,
   359  			},
   360  
   361  			{
   362  				fmt.Sprintf(`${index("%s", "foo")}`,
   363  					NewStringList([]string{"foo"}).String()),
   364  				"0",
   365  				false,
   366  			},
   367  
   368  			{
   369  				fmt.Sprintf(`${index("%s", "bar")}`,
   370  					NewStringList([]string{"foo", "spam", "bar", "eggs"}).String()),
   371  				"2",
   372  				false,
   373  			},
   374  		},
   375  	})
   376  }
   377  
   378  func TestInterpolateFuncJoin(t *testing.T) {
   379  	testFunction(t, testFunctionConfig{
   380  		Cases: []testFunctionCase{
   381  			{
   382  				`${join(",")}`,
   383  				nil,
   384  				true,
   385  			},
   386  
   387  			{
   388  				fmt.Sprintf(`${join(",", "%s")}`,
   389  					NewStringList([]string{"foo"}).String()),
   390  				"foo",
   391  				false,
   392  			},
   393  
   394  			/*
   395  				TODO
   396  				{
   397  					`${join(",", "foo", "bar")}`,
   398  					"foo,bar",
   399  					false,
   400  				},
   401  			*/
   402  
   403  			{
   404  				fmt.Sprintf(`${join(".", "%s")}`,
   405  					NewStringList([]string{"foo", "bar", "baz"}).String()),
   406  				"foo.bar.baz",
   407  				false,
   408  			},
   409  		},
   410  	})
   411  }
   412  
   413  func TestInterpolateFuncReplace(t *testing.T) {
   414  	testFunction(t, testFunctionConfig{
   415  		Cases: []testFunctionCase{
   416  			// Regular search and replace
   417  			{
   418  				`${replace("hello", "hel", "bel")}`,
   419  				"bello",
   420  				false,
   421  			},
   422  
   423  			// Search string doesn't match
   424  			{
   425  				`${replace("hello", "nope", "bel")}`,
   426  				"hello",
   427  				false,
   428  			},
   429  
   430  			// Regular expression
   431  			{
   432  				`${replace("hello", "/l/", "L")}`,
   433  				"heLLo",
   434  				false,
   435  			},
   436  
   437  			{
   438  				`${replace("helo", "/(l)/", "$1$1")}`,
   439  				"hello",
   440  				false,
   441  			},
   442  
   443  			// Bad regexp
   444  			{
   445  				`${replace("helo", "/(l/", "$1$1")}`,
   446  				nil,
   447  				true,
   448  			},
   449  		},
   450  	})
   451  }
   452  
   453  func TestInterpolateFuncLength(t *testing.T) {
   454  	testFunction(t, testFunctionConfig{
   455  		Cases: []testFunctionCase{
   456  			// Raw strings
   457  			{
   458  				`${length("")}`,
   459  				"0",
   460  				false,
   461  			},
   462  			{
   463  				`${length("a")}`,
   464  				"1",
   465  				false,
   466  			},
   467  			{
   468  				`${length(" ")}`,
   469  				"1",
   470  				false,
   471  			},
   472  			{
   473  				`${length(" a ,")}`,
   474  				"4",
   475  				false,
   476  			},
   477  			{
   478  				`${length("aaa")}`,
   479  				"3",
   480  				false,
   481  			},
   482  
   483  			// Lists
   484  			{
   485  				`${length(split(",", "a"))}`,
   486  				"1",
   487  				false,
   488  			},
   489  			{
   490  				`${length(split(",", "foo,"))}`,
   491  				"2",
   492  				false,
   493  			},
   494  			{
   495  				`${length(split(",", ",foo,"))}`,
   496  				"3",
   497  				false,
   498  			},
   499  			{
   500  				`${length(split(",", "foo,bar"))}`,
   501  				"2",
   502  				false,
   503  			},
   504  			{
   505  				`${length(split(".", "one.two.three.four.five"))}`,
   506  				"5",
   507  				false,
   508  			},
   509  		},
   510  	})
   511  }
   512  
   513  func TestInterpolateFuncSplit(t *testing.T) {
   514  	testFunction(t, testFunctionConfig{
   515  		Cases: []testFunctionCase{
   516  			{
   517  				`${split(",")}`,
   518  				nil,
   519  				true,
   520  			},
   521  
   522  			{
   523  				`${split(",", "")}`,
   524  				NewStringList([]string{""}).String(),
   525  				false,
   526  			},
   527  
   528  			{
   529  				`${split(",", "foo")}`,
   530  				NewStringList([]string{"foo"}).String(),
   531  				false,
   532  			},
   533  
   534  			{
   535  				`${split(",", ",,,")}`,
   536  				NewStringList([]string{"", "", "", ""}).String(),
   537  				false,
   538  			},
   539  
   540  			{
   541  				`${split(",", "foo,")}`,
   542  				NewStringList([]string{"foo", ""}).String(),
   543  				false,
   544  			},
   545  
   546  			{
   547  				`${split(",", ",foo,")}`,
   548  				NewStringList([]string{"", "foo", ""}).String(),
   549  				false,
   550  			},
   551  
   552  			{
   553  				`${split(".", "foo.bar.baz")}`,
   554  				NewStringList([]string{"foo", "bar", "baz"}).String(),
   555  				false,
   556  			},
   557  		},
   558  	})
   559  }
   560  
   561  func TestInterpolateFuncLookup(t *testing.T) {
   562  	testFunction(t, testFunctionConfig{
   563  		Vars: map[string]ast.Variable{
   564  			"var.foo.bar": ast.Variable{
   565  				Value: "baz",
   566  				Type:  ast.TypeString,
   567  			},
   568  		},
   569  		Cases: []testFunctionCase{
   570  			{
   571  				`${lookup("foo", "bar")}`,
   572  				"baz",
   573  				false,
   574  			},
   575  
   576  			// Invalid key
   577  			{
   578  				`${lookup("foo", "baz")}`,
   579  				nil,
   580  				true,
   581  			},
   582  
   583  			// Too many args
   584  			{
   585  				`${lookup("foo", "bar", "baz")}`,
   586  				nil,
   587  				true,
   588  			},
   589  		},
   590  	})
   591  }
   592  
   593  func TestInterpolateFuncKeys(t *testing.T) {
   594  	testFunction(t, testFunctionConfig{
   595  		Vars: map[string]ast.Variable{
   596  			"var.foo.bar": ast.Variable{
   597  				Value: "baz",
   598  				Type:  ast.TypeString,
   599  			},
   600  			"var.foo.qux": ast.Variable{
   601  				Value: "quack",
   602  				Type:  ast.TypeString,
   603  			},
   604  			"var.str": ast.Variable{
   605  				Value: "astring",
   606  				Type:  ast.TypeString,
   607  			},
   608  		},
   609  		Cases: []testFunctionCase{
   610  			{
   611  				`${keys("foo")}`,
   612  				NewStringList([]string{"bar", "qux"}).String(),
   613  				false,
   614  			},
   615  
   616  			// Invalid key
   617  			{
   618  				`${keys("not")}`,
   619  				nil,
   620  				true,
   621  			},
   622  
   623  			// Too many args
   624  			{
   625  				`${keys("foo", "bar")}`,
   626  				nil,
   627  				true,
   628  			},
   629  
   630  			// Not a map
   631  			{
   632  				`${keys("str")}`,
   633  				nil,
   634  				true,
   635  			},
   636  		},
   637  	})
   638  }
   639  
   640  func TestInterpolateFuncValues(t *testing.T) {
   641  	testFunction(t, testFunctionConfig{
   642  		Vars: map[string]ast.Variable{
   643  			"var.foo.bar": ast.Variable{
   644  				Value: "quack",
   645  				Type:  ast.TypeString,
   646  			},
   647  			"var.foo.qux": ast.Variable{
   648  				Value: "baz",
   649  				Type:  ast.TypeString,
   650  			},
   651  			"var.str": ast.Variable{
   652  				Value: "astring",
   653  				Type:  ast.TypeString,
   654  			},
   655  		},
   656  		Cases: []testFunctionCase{
   657  			{
   658  				`${values("foo")}`,
   659  				NewStringList([]string{"quack", "baz"}).String(),
   660  				false,
   661  			},
   662  
   663  			// Invalid key
   664  			{
   665  				`${values("not")}`,
   666  				nil,
   667  				true,
   668  			},
   669  
   670  			// Too many args
   671  			{
   672  				`${values("foo", "bar")}`,
   673  				nil,
   674  				true,
   675  			},
   676  
   677  			// Not a map
   678  			{
   679  				`${values("str")}`,
   680  				nil,
   681  				true,
   682  			},
   683  		},
   684  	})
   685  }
   686  
   687  func TestInterpolateFuncElement(t *testing.T) {
   688  	testFunction(t, testFunctionConfig{
   689  		Cases: []testFunctionCase{
   690  			{
   691  				fmt.Sprintf(`${element("%s", "1")}`,
   692  					NewStringList([]string{"foo", "baz"}).String()),
   693  				"baz",
   694  				false,
   695  			},
   696  
   697  			{
   698  				fmt.Sprintf(`${element("%s", "0")}`,
   699  					NewStringList([]string{"foo"}).String()),
   700  				"foo",
   701  				false,
   702  			},
   703  
   704  			// Invalid index should wrap vs. out-of-bounds
   705  			{
   706  				fmt.Sprintf(`${element("%s", "2")}`,
   707  					NewStringList([]string{"foo", "baz"}).String()),
   708  				"foo",
   709  				false,
   710  			},
   711  
   712  			// Too many args
   713  			{
   714  				fmt.Sprintf(`${element("%s", "0", "2")}`,
   715  					NewStringList([]string{"foo", "baz"}).String()),
   716  				nil,
   717  				true,
   718  			},
   719  		},
   720  	})
   721  }
   722  
   723  func TestInterpolateFuncBase64Encode(t *testing.T) {
   724  	testFunction(t, testFunctionConfig{
   725  		Cases: []testFunctionCase{
   726  			// Regular base64 encoding
   727  			{
   728  				`${base64encode("abc123!?$*&()'-=@~")}`,
   729  				"YWJjMTIzIT8kKiYoKSctPUB+",
   730  				false,
   731  			},
   732  		},
   733  	})
   734  }
   735  
   736  func TestInterpolateFuncBase64Decode(t *testing.T) {
   737  	testFunction(t, testFunctionConfig{
   738  		Cases: []testFunctionCase{
   739  			// Regular base64 decoding
   740  			{
   741  				`${base64decode("YWJjMTIzIT8kKiYoKSctPUB+")}`,
   742  				"abc123!?$*&()'-=@~",
   743  				false,
   744  			},
   745  
   746  			// Invalid base64 data decoding
   747  			{
   748  				`${base64decode("this-is-an-invalid-base64-data")}`,
   749  				nil,
   750  				true,
   751  			},
   752  		},
   753  	})
   754  }
   755  
   756  func TestInterpolateFuncLower(t *testing.T) {
   757  	testFunction(t, testFunctionConfig{
   758  		Cases: []testFunctionCase{
   759  			{
   760  				`${lower("HELLO")}`,
   761  				"hello",
   762  				false,
   763  			},
   764  
   765  			{
   766  				`${lower("")}`,
   767  				"",
   768  				false,
   769  			},
   770  
   771  			{
   772  				`${lower()}`,
   773  				nil,
   774  				true,
   775  			},
   776  		},
   777  	})
   778  }
   779  
   780  func TestInterpolateFuncUpper(t *testing.T) {
   781  	testFunction(t, testFunctionConfig{
   782  		Cases: []testFunctionCase{
   783  			{
   784  				`${upper("hello")}`,
   785  				"HELLO",
   786  				false,
   787  			},
   788  
   789  			{
   790  				`${upper("")}`,
   791  				"",
   792  				false,
   793  			},
   794  
   795  			{
   796  				`${upper()}`,
   797  				nil,
   798  				true,
   799  			},
   800  		},
   801  	})
   802  }
   803  
   804  type testFunctionConfig struct {
   805  	Cases []testFunctionCase
   806  	Vars  map[string]ast.Variable
   807  }
   808  
   809  type testFunctionCase struct {
   810  	Input  string
   811  	Result interface{}
   812  	Error  bool
   813  }
   814  
   815  func testFunction(t *testing.T, config testFunctionConfig) {
   816  	for i, tc := range config.Cases {
   817  		ast, err := lang.Parse(tc.Input)
   818  		if err != nil {
   819  			t.Fatalf("Case #%d: input: %#v\nerr: %s", i, tc.Input, err)
   820  		}
   821  
   822  		out, _, err := lang.Eval(ast, langEvalConfig(config.Vars))
   823  		if err != nil != tc.Error {
   824  			t.Fatalf("Case #%d:\ninput: %#v\nerr: %s", i, tc.Input, err)
   825  		}
   826  
   827  		if !reflect.DeepEqual(out, tc.Result) {
   828  			t.Fatalf(
   829  				"%d: bad output for input: %s\n\nOutput: %#v\nExpected: %#v",
   830  				i, tc.Input, out, tc.Result)
   831  		}
   832  	}
   833  }