aboutsummaryrefslogtreecommitdiffhomepage
path: root/vendor/github.com/hashicorp/terraform/config/interpolate_funcs.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/hashicorp/terraform/config/interpolate_funcs.go')
-rw-r--r--vendor/github.com/hashicorp/terraform/config/interpolate_funcs.go429
1 files changed, 364 insertions, 65 deletions
diff --git a/vendor/github.com/hashicorp/terraform/config/interpolate_funcs.go b/vendor/github.com/hashicorp/terraform/config/interpolate_funcs.go
index a298cf2..421edb0 100644
--- a/vendor/github.com/hashicorp/terraform/config/interpolate_funcs.go
+++ b/vendor/github.com/hashicorp/terraform/config/interpolate_funcs.go
@@ -1,17 +1,23 @@
1package config 1package config
2 2
3import ( 3import (
4 "bytes"
5 "compress/gzip"
4 "crypto/md5" 6 "crypto/md5"
7 "crypto/rsa"
5 "crypto/sha1" 8 "crypto/sha1"
6 "crypto/sha256" 9 "crypto/sha256"
7 "crypto/sha512" 10 "crypto/sha512"
11 "crypto/x509"
8 "encoding/base64" 12 "encoding/base64"
9 "encoding/hex" 13 "encoding/hex"
10 "encoding/json" 14 "encoding/json"
15 "encoding/pem"
11 "fmt" 16 "fmt"
12 "io/ioutil" 17 "io/ioutil"
13 "math" 18 "math"
14 "net" 19 "net"
20 "net/url"
15 "path/filepath" 21 "path/filepath"
16 "regexp" 22 "regexp"
17 "sort" 23 "sort"
@@ -55,59 +61,74 @@ func listVariableValueToStringSlice(values []ast.Variable) ([]string, error) {
55// Funcs is the mapping of built-in functions for configuration. 61// Funcs is the mapping of built-in functions for configuration.
56func Funcs() map[string]ast.Function { 62func Funcs() map[string]ast.Function {
57 return map[string]ast.Function{ 63 return map[string]ast.Function{
58 "basename": interpolationFuncBasename(), 64 "abs": interpolationFuncAbs(),
59 "base64decode": interpolationFuncBase64Decode(), 65 "basename": interpolationFuncBasename(),
60 "base64encode": interpolationFuncBase64Encode(), 66 "base64decode": interpolationFuncBase64Decode(),
61 "base64sha256": interpolationFuncBase64Sha256(), 67 "base64encode": interpolationFuncBase64Encode(),
62 "base64sha512": interpolationFuncBase64Sha512(), 68 "base64gzip": interpolationFuncBase64Gzip(),
63 "bcrypt": interpolationFuncBcrypt(), 69 "base64sha256": interpolationFuncBase64Sha256(),
64 "ceil": interpolationFuncCeil(), 70 "base64sha512": interpolationFuncBase64Sha512(),
65 "chomp": interpolationFuncChomp(), 71 "bcrypt": interpolationFuncBcrypt(),
66 "cidrhost": interpolationFuncCidrHost(), 72 "ceil": interpolationFuncCeil(),
67 "cidrnetmask": interpolationFuncCidrNetmask(), 73 "chomp": interpolationFuncChomp(),
68 "cidrsubnet": interpolationFuncCidrSubnet(), 74 "cidrhost": interpolationFuncCidrHost(),
69 "coalesce": interpolationFuncCoalesce(), 75 "cidrnetmask": interpolationFuncCidrNetmask(),
70 "coalescelist": interpolationFuncCoalesceList(), 76 "cidrsubnet": interpolationFuncCidrSubnet(),
71 "compact": interpolationFuncCompact(), 77 "coalesce": interpolationFuncCoalesce(),
72 "concat": interpolationFuncConcat(), 78 "coalescelist": interpolationFuncCoalesceList(),
73 "contains": interpolationFuncContains(), 79 "compact": interpolationFuncCompact(),
74 "dirname": interpolationFuncDirname(), 80 "concat": interpolationFuncConcat(),
75 "distinct": interpolationFuncDistinct(), 81 "contains": interpolationFuncContains(),
76 "element": interpolationFuncElement(), 82 "dirname": interpolationFuncDirname(),
77 "file": interpolationFuncFile(), 83 "distinct": interpolationFuncDistinct(),
78 "matchkeys": interpolationFuncMatchKeys(), 84 "element": interpolationFuncElement(),
79 "floor": interpolationFuncFloor(), 85 "chunklist": interpolationFuncChunklist(),
80 "format": interpolationFuncFormat(), 86 "file": interpolationFuncFile(),
81 "formatlist": interpolationFuncFormatList(), 87 "filebase64sha256": interpolationFuncMakeFileHash(interpolationFuncBase64Sha256()),
82 "index": interpolationFuncIndex(), 88 "filebase64sha512": interpolationFuncMakeFileHash(interpolationFuncBase64Sha512()),
83 "join": interpolationFuncJoin(), 89 "filemd5": interpolationFuncMakeFileHash(interpolationFuncMd5()),
84 "jsonencode": interpolationFuncJSONEncode(), 90 "filesha1": interpolationFuncMakeFileHash(interpolationFuncSha1()),
85 "length": interpolationFuncLength(), 91 "filesha256": interpolationFuncMakeFileHash(interpolationFuncSha256()),
86 "list": interpolationFuncList(), 92 "filesha512": interpolationFuncMakeFileHash(interpolationFuncSha512()),
87 "log": interpolationFuncLog(), 93 "matchkeys": interpolationFuncMatchKeys(),
88 "lower": interpolationFuncLower(), 94 "flatten": interpolationFuncFlatten(),
89 "map": interpolationFuncMap(), 95 "floor": interpolationFuncFloor(),
90 "max": interpolationFuncMax(), 96 "format": interpolationFuncFormat(),
91 "md5": interpolationFuncMd5(), 97 "formatlist": interpolationFuncFormatList(),
92 "merge": interpolationFuncMerge(), 98 "indent": interpolationFuncIndent(),
93 "min": interpolationFuncMin(), 99 "index": interpolationFuncIndex(),
94 "pathexpand": interpolationFuncPathExpand(), 100 "join": interpolationFuncJoin(),
95 "pow": interpolationFuncPow(), 101 "jsonencode": interpolationFuncJSONEncode(),
96 "uuid": interpolationFuncUUID(), 102 "length": interpolationFuncLength(),
97 "replace": interpolationFuncReplace(), 103 "list": interpolationFuncList(),
98 "sha1": interpolationFuncSha1(), 104 "log": interpolationFuncLog(),
99 "sha256": interpolationFuncSha256(), 105 "lower": interpolationFuncLower(),
100 "sha512": interpolationFuncSha512(), 106 "map": interpolationFuncMap(),
101 "signum": interpolationFuncSignum(), 107 "max": interpolationFuncMax(),
102 "slice": interpolationFuncSlice(), 108 "md5": interpolationFuncMd5(),
103 "sort": interpolationFuncSort(), 109 "merge": interpolationFuncMerge(),
104 "split": interpolationFuncSplit(), 110 "min": interpolationFuncMin(),
105 "substr": interpolationFuncSubstr(), 111 "pathexpand": interpolationFuncPathExpand(),
106 "timestamp": interpolationFuncTimestamp(), 112 "pow": interpolationFuncPow(),
107 "title": interpolationFuncTitle(), 113 "uuid": interpolationFuncUUID(),
108 "trimspace": interpolationFuncTrimSpace(), 114 "replace": interpolationFuncReplace(),
109 "upper": interpolationFuncUpper(), 115 "rsadecrypt": interpolationFuncRsaDecrypt(),
110 "zipmap": interpolationFuncZipMap(), 116 "sha1": interpolationFuncSha1(),
117 "sha256": interpolationFuncSha256(),
118 "sha512": interpolationFuncSha512(),
119 "signum": interpolationFuncSignum(),
120 "slice": interpolationFuncSlice(),
121 "sort": interpolationFuncSort(),
122 "split": interpolationFuncSplit(),
123 "substr": interpolationFuncSubstr(),
124 "timestamp": interpolationFuncTimestamp(),
125 "timeadd": interpolationFuncTimeAdd(),
126 "title": interpolationFuncTitle(),
127 "transpose": interpolationFuncTranspose(),
128 "trimspace": interpolationFuncTrimSpace(),
129 "upper": interpolationFuncUpper(),
130 "urlencode": interpolationFuncURLEncode(),
131 "zipmap": interpolationFuncZipMap(),
111 } 132 }
112} 133}
113 134
@@ -669,6 +690,21 @@ func interpolationFuncFormatList() ast.Function {
669 } 690 }
670} 691}
671 692
693// interpolationFuncIndent indents a multi-line string with the
694// specified number of spaces
695func interpolationFuncIndent() ast.Function {
696 return ast.Function{
697 ArgTypes: []ast.Type{ast.TypeInt, ast.TypeString},
698 ReturnType: ast.TypeString,
699 Callback: func(args []interface{}) (interface{}, error) {
700 spaces := args[0].(int)
701 data := args[1].(string)
702 pad := strings.Repeat(" ", spaces)
703 return strings.Replace(data, "\n", "\n"+pad, -1), nil
704 },
705 }
706}
707
672// interpolationFuncIndex implements the "index" function that allows one to 708// interpolationFuncIndex implements the "index" function that allows one to
673// find the index of a specific element in a list 709// find the index of a specific element in a list
674func interpolationFuncIndex() ast.Function { 710func interpolationFuncIndex() ast.Function {
@@ -823,8 +859,7 @@ func interpolationFuncJoin() ast.Function {
823} 859}
824 860
825// interpolationFuncJSONEncode implements the "jsonencode" function that encodes 861// interpolationFuncJSONEncode implements the "jsonencode" function that encodes
826// a string, list, or map as its JSON representation. For now, values in the 862// a string, list, or map as its JSON representation.
827// list or map may only be strings.
828func interpolationFuncJSONEncode() ast.Function { 863func interpolationFuncJSONEncode() ast.Function {
829 return ast.Function{ 864 return ast.Function{
830 ArgTypes: []ast.Type{ast.TypeAny}, 865 ArgTypes: []ast.Type{ast.TypeAny},
@@ -837,28 +872,36 @@ func interpolationFuncJSONEncode() ast.Function {
837 toEncode = typedArg 872 toEncode = typedArg
838 873
839 case []ast.Variable: 874 case []ast.Variable:
840 // We preallocate the list here. Note that it's important that in
841 // the length 0 case, we have an empty list rather than nil, as
842 // they encode differently.
843 // XXX It would be nice to support arbitrarily nested data here. Is
844 // there an inverse of hil.InterfaceToVariable?
845 strings := make([]string, len(typedArg)) 875 strings := make([]string, len(typedArg))
846 876
847 for i, v := range typedArg { 877 for i, v := range typedArg {
848 if v.Type != ast.TypeString { 878 if v.Type != ast.TypeString {
849 return "", fmt.Errorf("list elements must be strings") 879 variable, _ := hil.InterfaceToVariable(typedArg)
880 toEncode, _ = hil.VariableToInterface(variable)
881
882 jEnc, err := json.Marshal(toEncode)
883 if err != nil {
884 return "", fmt.Errorf("failed to encode JSON data '%s'", toEncode)
885 }
886 return string(jEnc), nil
887
850 } 888 }
851 strings[i] = v.Value.(string) 889 strings[i] = v.Value.(string)
852 } 890 }
853 toEncode = strings 891 toEncode = strings
854 892
855 case map[string]ast.Variable: 893 case map[string]ast.Variable:
856 // XXX It would be nice to support arbitrarily nested data here. Is
857 // there an inverse of hil.InterfaceToVariable?
858 stringMap := make(map[string]string) 894 stringMap := make(map[string]string)
859 for k, v := range typedArg { 895 for k, v := range typedArg {
860 if v.Type != ast.TypeString { 896 if v.Type != ast.TypeString {
861 return "", fmt.Errorf("map values must be strings") 897 variable, _ := hil.InterfaceToVariable(typedArg)
898 toEncode, _ = hil.VariableToInterface(variable)
899
900 jEnc, err := json.Marshal(toEncode)
901 if err != nil {
902 return "", fmt.Errorf("failed to encode JSON data '%s'", toEncode)
903 }
904 return string(jEnc), nil
862 } 905 }
863 stringMap[k] = v.Value.(string) 906 stringMap[k] = v.Value.(string)
864 } 907 }
@@ -1098,6 +1141,56 @@ func interpolationFuncElement() ast.Function {
1098 } 1141 }
1099} 1142}
1100 1143
1144// returns the `list` items chunked by `size`.
1145func interpolationFuncChunklist() ast.Function {
1146 return ast.Function{
1147 ArgTypes: []ast.Type{
1148 ast.TypeList, // inputList
1149 ast.TypeInt, // size
1150 },
1151 ReturnType: ast.TypeList,
1152 Callback: func(args []interface{}) (interface{}, error) {
1153 output := make([]ast.Variable, 0)
1154
1155 values, _ := args[0].([]ast.Variable)
1156 size, _ := args[1].(int)
1157
1158 // errors if size is negative
1159 if size < 0 {
1160 return nil, fmt.Errorf("The size argument must be positive")
1161 }
1162
1163 // if size is 0, returns a list made of the initial list
1164 if size == 0 {
1165 output = append(output, ast.Variable{
1166 Type: ast.TypeList,
1167 Value: values,
1168 })
1169 return output, nil
1170 }
1171
1172 variables := make([]ast.Variable, 0)
1173 chunk := ast.Variable{
1174 Type: ast.TypeList,
1175 Value: variables,
1176 }
1177 l := len(values)
1178 for i, v := range values {
1179 variables = append(variables, v)
1180
1181 // Chunk when index isn't 0, or when reaching the values's length
1182 if (i+1)%size == 0 || (i+1) == l {
1183 chunk.Value = variables
1184 output = append(output, chunk)
1185 variables = make([]ast.Variable, 0)
1186 }
1187 }
1188
1189 return output, nil
1190 },
1191 }
1192}
1193
1101// interpolationFuncKeys implements the "keys" function that yields a list of 1194// interpolationFuncKeys implements the "keys" function that yields a list of
1102// keys of map types within a Terraform configuration. 1195// keys of map types within a Terraform configuration.
1103func interpolationFuncKeys(vs map[string]ast.Variable) ast.Function { 1196func interpolationFuncKeys(vs map[string]ast.Variable) ast.Function {
@@ -1197,6 +1290,32 @@ func interpolationFuncBase64Decode() ast.Function {
1197 } 1290 }
1198} 1291}
1199 1292
1293// interpolationFuncBase64Gzip implements the "gzip" function that allows gzip
1294// compression encoding the result using base64
1295func interpolationFuncBase64Gzip() ast.Function {
1296 return ast.Function{
1297 ArgTypes: []ast.Type{ast.TypeString},
1298 ReturnType: ast.TypeString,
1299 Callback: func(args []interface{}) (interface{}, error) {
1300 s := args[0].(string)
1301
1302 var b bytes.Buffer
1303 gz := gzip.NewWriter(&b)
1304 if _, err := gz.Write([]byte(s)); err != nil {
1305 return "", fmt.Errorf("failed to write gzip raw data: '%s'", s)
1306 }
1307 if err := gz.Flush(); err != nil {
1308 return "", fmt.Errorf("failed to flush gzip writer: '%s'", s)
1309 }
1310 if err := gz.Close(); err != nil {
1311 return "", fmt.Errorf("failed to close gzip writer: '%s'", s)
1312 }
1313
1314 return base64.StdEncoding.EncodeToString(b.Bytes()), nil
1315 },
1316 }
1317}
1318
1200// interpolationFuncLower implements the "lower" function that does 1319// interpolationFuncLower implements the "lower" function that does
1201// string lower casing. 1320// string lower casing.
1202func interpolationFuncLower() ast.Function { 1321func interpolationFuncLower() ast.Function {
@@ -1396,6 +1515,29 @@ func interpolationFuncTimestamp() ast.Function {
1396 } 1515 }
1397} 1516}
1398 1517
1518func interpolationFuncTimeAdd() ast.Function {
1519 return ast.Function{
1520 ArgTypes: []ast.Type{
1521 ast.TypeString, // input timestamp string in RFC3339 format
1522 ast.TypeString, // duration to add to input timestamp that should be parsable by time.ParseDuration
1523 },
1524 ReturnType: ast.TypeString,
1525 Callback: func(args []interface{}) (interface{}, error) {
1526
1527 ts, err := time.Parse(time.RFC3339, args[0].(string))
1528 if err != nil {
1529 return nil, err
1530 }
1531 duration, err := time.ParseDuration(args[1].(string))
1532 if err != nil {
1533 return nil, err
1534 }
1535
1536 return ts.Add(duration).Format(time.RFC3339), nil
1537 },
1538 }
1539}
1540
1399// interpolationFuncTitle implements the "title" function that returns a copy of the 1541// interpolationFuncTitle implements the "title" function that returns a copy of the
1400// string in which first characters of all the words are capitalized. 1542// string in which first characters of all the words are capitalized.
1401func interpolationFuncTitle() ast.Function { 1543func interpolationFuncTitle() ast.Function {
@@ -1441,7 +1583,7 @@ func interpolationFuncSubstr() ast.Function {
1441 return nil, fmt.Errorf("length should be a non-negative integer") 1583 return nil, fmt.Errorf("length should be a non-negative integer")
1442 } 1584 }
1443 1585
1444 if offset > len(str) { 1586 if offset > len(str) || offset < 0 {
1445 return nil, fmt.Errorf("offset cannot be larger than the length of the string") 1587 return nil, fmt.Errorf("offset cannot be larger than the length of the string")
1446 } 1588 }
1447 1589
@@ -1453,3 +1595,160 @@ func interpolationFuncSubstr() ast.Function {
1453 }, 1595 },
1454 } 1596 }
1455} 1597}
1598
1599// Flatten until it's not ast.TypeList
1600func flattener(finalList []ast.Variable, flattenList []ast.Variable) []ast.Variable {
1601 for _, val := range flattenList {
1602 if val.Type == ast.TypeList {
1603 finalList = flattener(finalList, val.Value.([]ast.Variable))
1604 } else {
1605 finalList = append(finalList, val)
1606 }
1607 }
1608 return finalList
1609}
1610
1611// Flatten to single list
1612func interpolationFuncFlatten() ast.Function {
1613 return ast.Function{
1614 ArgTypes: []ast.Type{ast.TypeList},
1615 ReturnType: ast.TypeList,
1616 Variadic: false,
1617 Callback: func(args []interface{}) (interface{}, error) {
1618 inputList := args[0].([]ast.Variable)
1619
1620 var outputList []ast.Variable
1621 return flattener(outputList, inputList), nil
1622 },
1623 }
1624}
1625
1626func interpolationFuncURLEncode() ast.Function {
1627 return ast.Function{
1628 ArgTypes: []ast.Type{ast.TypeString},
1629 ReturnType: ast.TypeString,
1630 Callback: func(args []interface{}) (interface{}, error) {
1631 s := args[0].(string)
1632 return url.QueryEscape(s), nil
1633 },
1634 }
1635}
1636
1637// interpolationFuncTranspose implements the "transpose" function
1638// that converts a map (string,list) to a map (string,list) where
1639// the unique values of the original lists become the keys of the
1640// new map and the keys of the original map become values for the
1641// corresponding new keys.
1642func interpolationFuncTranspose() ast.Function {
1643 return ast.Function{
1644 ArgTypes: []ast.Type{ast.TypeMap},
1645 ReturnType: ast.TypeMap,
1646 Callback: func(args []interface{}) (interface{}, error) {
1647
1648 inputMap := args[0].(map[string]ast.Variable)
1649 outputMap := make(map[string]ast.Variable)
1650 tmpMap := make(map[string][]string)
1651
1652 for inKey, inVal := range inputMap {
1653 if inVal.Type != ast.TypeList {
1654 return nil, fmt.Errorf("transpose requires a map of lists of strings")
1655 }
1656 values := inVal.Value.([]ast.Variable)
1657 for _, listVal := range values {
1658 if listVal.Type != ast.TypeString {
1659 return nil, fmt.Errorf("transpose requires the given map values to be lists of strings")
1660 }
1661 outKey := listVal.Value.(string)
1662 if _, ok := tmpMap[outKey]; !ok {
1663 tmpMap[outKey] = make([]string, 0)
1664 }
1665 outVal := tmpMap[outKey]
1666 outVal = append(outVal, inKey)
1667 sort.Strings(outVal)
1668 tmpMap[outKey] = outVal
1669 }
1670 }
1671
1672 for outKey, outVal := range tmpMap {
1673 values := make([]ast.Variable, 0)
1674 for _, v := range outVal {
1675 values = append(values, ast.Variable{Type: ast.TypeString, Value: v})
1676 }
1677 outputMap[outKey] = ast.Variable{Type: ast.TypeList, Value: values}
1678 }
1679 return outputMap, nil
1680 },
1681 }
1682}
1683
1684// interpolationFuncAbs returns the absolute value of a given float.
1685func interpolationFuncAbs() ast.Function {
1686 return ast.Function{
1687 ArgTypes: []ast.Type{ast.TypeFloat},
1688 ReturnType: ast.TypeFloat,
1689 Callback: func(args []interface{}) (interface{}, error) {
1690 return math.Abs(args[0].(float64)), nil
1691 },
1692 }
1693}
1694
1695// interpolationFuncRsaDecrypt implements the "rsadecrypt" function that does
1696// RSA decryption.
1697func interpolationFuncRsaDecrypt() ast.Function {
1698 return ast.Function{
1699 ArgTypes: []ast.Type{ast.TypeString, ast.TypeString},
1700 ReturnType: ast.TypeString,
1701 Callback: func(args []interface{}) (interface{}, error) {
1702 s := args[0].(string)
1703 key := args[1].(string)
1704
1705 b, err := base64.StdEncoding.DecodeString(s)
1706 if err != nil {
1707 return "", fmt.Errorf("Failed to decode input %q: cipher text must be base64-encoded", s)
1708 }
1709
1710 block, _ := pem.Decode([]byte(key))
1711 if block == nil {
1712 return "", fmt.Errorf("Failed to read key %q: no key found", key)
1713 }
1714 if block.Headers["Proc-Type"] == "4,ENCRYPTED" {
1715 return "", fmt.Errorf(
1716 "Failed to read key %q: password protected keys are\n"+
1717 "not supported. Please decrypt the key prior to use.", key)
1718 }
1719
1720 x509Key, err := x509.ParsePKCS1PrivateKey(block.Bytes)
1721 if err != nil {
1722 return "", err
1723 }
1724
1725 out, err := rsa.DecryptPKCS1v15(nil, x509Key, b)
1726 if err != nil {
1727 return "", err
1728 }
1729
1730 return string(out), nil
1731 },
1732 }
1733}
1734
1735// interpolationFuncMakeFileHash constructs a function that hashes the contents
1736// of a file by combining the implementations of the file(...) function and
1737// a given other function that is assumed to take a single string argument and
1738// return a hash value.
1739func interpolationFuncMakeFileHash(hashFunc ast.Function) ast.Function {
1740 fileFunc := interpolationFuncFile()
1741
1742 return ast.Function{
1743 ArgTypes: []ast.Type{ast.TypeString},
1744 ReturnType: ast.TypeString,
1745 Callback: func(args []interface{}) (interface{}, error) {
1746 filename := args[0].(string)
1747 contents, err := fileFunc.Callback([]interface{}{filename})
1748 if err != nil {
1749 return nil, err
1750 }
1751 return hashFunc.Callback([]interface{}{contents})
1752 },
1753 }
1754}