cleanup comments for nicer godocs

This commit is contained in:
Kristin Laemmert 2018-05-23 09:38:12 -07:00 committed by Martin Atkins
parent 9aa9b18658
commit eb1d8b7909
9 changed files with 368 additions and 83 deletions

View File

@ -1,9 +1,15 @@
package funcs package funcs
import ( import (
"crypto/md5"
"crypto/rsa"
"crypto/sha1"
"crypto/sha256" "crypto/sha256"
"crypto/sha512" "crypto/sha512"
"crypto/x509"
"encoding/base64" "encoding/base64"
"encoding/hex"
"encoding/pem"
"fmt" "fmt"
uuid "github.com/hashicorp/go-uuid" uuid "github.com/hashicorp/go-uuid"
@ -25,8 +31,8 @@ var UUIDFunc = function.New(&function.Spec{
}, },
}) })
// Base64Sha256Func constructs a function that computes the SHA256 hash of a given string and encodes it with // Base64Sha256Func constructs a function that computes the SHA256 hash of a given string
// Base64. // and encodes it with Base64.
var Base64Sha256Func = function.New(&function.Spec{ var Base64Sha256Func = function.New(&function.Spec{
Params: []function.Parameter{ Params: []function.Parameter{
{ {
@ -44,8 +50,8 @@ var Base64Sha256Func = function.New(&function.Spec{
}, },
}) })
// Base64Sha512Func constructs a function that computes the SHA256 hash of a given string and encodes it with // Base64Sha512Func constructs a function that computes the SHA256 hash of a given string
// Base64. // and encodes it with Base64.
var Base64Sha512Func = function.New(&function.Spec{ var Base64Sha512Func = function.New(&function.Spec{
Params: []function.Parameter{ Params: []function.Parameter{
{ {
@ -101,6 +107,89 @@ var BcryptFunc = function.New(&function.Spec{
}, },
}) })
// Md5Func constructs a function that computes the MD5 hash of a given string and encodes it with hexadecimal digits.
var Md5Func = function.New(&function.Spec{
Params: []function.Parameter{
{
Name: "str",
Type: cty.String,
},
},
Type: function.StaticReturnType(cty.String),
Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) {
s := args[0].AsString()
h := md5.New()
h.Write([]byte(s))
hash := hex.EncodeToString(h.Sum(nil))
return cty.StringVal(hash), nil
},
})
// RsaDecryptFunc constructs a function that decrypts an RSA-encrypted ciphertext.
var RsaDecryptFunc = function.New(&function.Spec{
Params: []function.Parameter{
{
Name: "ciphertext",
Type: cty.String,
},
{
Name: "privatekey",
Type: cty.String,
},
},
Type: function.StaticReturnType(cty.String),
Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) {
s := args[0].AsString()
key := args[1].AsString()
b, err := base64.StdEncoding.DecodeString(s)
if err != nil {
return cty.UnknownVal(cty.String), fmt.Errorf("Failed to decode input %q: cipher text must be base64-encoded", key)
}
block, _ := pem.Decode([]byte(key))
if block == nil {
return cty.UnknownVal(cty.String), fmt.Errorf("Failed to read key %q: no key found", key)
}
if block.Headers["Proc-Type"] == "4,ENCRYPTED" {
return cty.UnknownVal(cty.String), fmt.Errorf(
"Failed to read key %q: password protected keys are\n"+
"not supported. Please decrypt the key prior to use.", key)
}
x509Key, err := x509.ParsePKCS1PrivateKey(block.Bytes)
if err != nil {
return cty.UnknownVal(cty.String), err
}
out, err := rsa.DecryptPKCS1v15(nil, x509Key, b)
if err != nil {
return cty.UnknownVal(cty.String), err
}
return cty.StringVal(string(out)), nil
},
})
// Sha1Func contructs a function that computes the SHA1 hash of a given string
// and encodes it with hexadecimal digits.
var Sha1Func = function.New(&function.Spec{
Params: []function.Parameter{
{
Name: "str",
Type: cty.String,
},
},
Type: function.StaticReturnType(cty.String),
Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) {
s := args[0].AsString()
h := sha1.New()
h.Write([]byte(s))
hash := hex.EncodeToString(h.Sum(nil))
return cty.StringVal(hash), nil
},
})
// UUID generates and returns a Type-4 UUID in the standard hexadecimal string // UUID generates and returns a Type-4 UUID in the standard hexadecimal string
// format. // format.
// //
@ -111,30 +200,28 @@ func UUID() (cty.Value, error) {
return UUIDFunc.Call(nil) return UUIDFunc.Call(nil)
} }
// Base64sha256 computes the SHA256 hash of a given string and encodes it with // Base64Sha256 computes the SHA256 hash of a given string and encodes it with
// Base64. // Base64.
// //
// The given string is first encoded as UTF-8 and then the SHA256 algorithm is applied // The given string is first encoded as UTF-8 and then the SHA256 algorithm is applied
// as defined in [RFC 4634](https://tools.ietf.org/html/rfc4634). The raw hash is // as defined in RFC 4634. The raw hash is then encoded with Base64 before returning.
// then encoded with Base64 before returning. Terraform uses the "standard" Base64 // Terraform uses the "standard" Base64 alphabet as defined in RFC 4648 section 4.
// alphabet as defined in [RFC 4648 section 4](https://tools.ietf.org/html/rfc4648#section-4).
func Base64Sha256(str cty.Value) (cty.Value, error) { func Base64Sha256(str cty.Value) (cty.Value, error) {
return Base64Sha256Func.Call([]cty.Value{str}) return Base64Sha256Func.Call([]cty.Value{str})
} }
// Base64sha512 computes the SHA512 hash of a given string and encodes it with // Base64Sha512 computes the SHA512 hash of a given string and encodes it with
// Base64. // Base64.
// //
// The given string is first encoded as UTF-8 and then the SHA256 algorithm is applied // The given string is first encoded as UTF-8 and then the SHA256 algorithm is applied
// as defined in [RFC 4634](https://tools.ietf.org/html/rfc4634). The raw hash is // as defined in RFC 4634. The raw hash is then encoded with Base64 before returning.
// then encoded with Base64 before returning. Terraform uses the "standard" Base64 // Terraform uses the "standard" Base64 alphabet as defined in RFC 4648 section 4
// alphabet as defined in [RFC 4648 section 4](https://tools.ietf.org/html/rfc4648#section-4).
func Base64Sha512(str cty.Value) (cty.Value, error) { func Base64Sha512(str cty.Value) (cty.Value, error) {
return Base64Sha512Func.Call([]cty.Value{str}) return Base64Sha512Func.Call([]cty.Value{str})
} }
// Bcrypt computes a hash of the given string using the Blowfish cipher, // Bcrypt computes a hash of the given string using the Blowfish cipher,
// returning a string in the Modular Crypt Format(https://passlib.readthedocs.io/en/stable/modular_crypt_format.html) // returning a string in the Modular Crypt Format
// usually expected in the shadow password file on many Unix systems. // usually expected in the shadow password file on many Unix systems.
func Bcrypt(str cty.Value, cost ...cty.Value) (cty.Value, error) { func Bcrypt(str cty.Value, cost ...cty.Value) (cty.Value, error) {
args := make([]cty.Value, len(cost)+1) args := make([]cty.Value, len(cost)+1)
@ -142,3 +229,19 @@ func Bcrypt(str cty.Value, cost ...cty.Value) (cty.Value, error) {
copy(args[1:], cost) copy(args[1:], cost)
return BcryptFunc.Call(args) return BcryptFunc.Call(args)
} }
// Md5 computes the MD5 hash of a given string and encodes it with hexadecimal digits.
func Md5(str cty.Value) (cty.Value, error) {
return Md5Func.Call([]cty.Value{str})
}
// RsaDecrypt decrypts an RSA-encrypted ciphertext, returning the corresponding
// cleartext.
func RsaDecrypt(ciphertext, privatekey cty.Value) (cty.Value, error) {
return RsaDecryptFunc.Call([]cty.Value{ciphertext, privatekey})
}
// Sha1 computes the SHA1 hash of a given string and encodes it with hexadecimal digits.
func Sha1(str cty.Value) (cty.Value, error) {
return Sha1Func.Call([]cty.Value{str})
}

View File

@ -45,11 +45,9 @@ func TestBase64Sha256(t *testing.T) {
t.Fatal("succeeded; want error") t.Fatal("succeeded; want error")
} }
return return
} else { } else if err != nil {
if err != nil {
t.Fatalf("unexpected error: %s", err) t.Fatalf("unexpected error: %s", err)
} }
}
if !got.RawEquals(test.Want) { if !got.RawEquals(test.Want) {
t.Errorf("wrong result\ngot: %#v\nwant: %#v", got, test.Want) t.Errorf("wrong result\ngot: %#v\nwant: %#v", got, test.Want)
@ -83,11 +81,9 @@ func TestBase64Sha512(t *testing.T) {
t.Fatal("succeeded; want error") t.Fatal("succeeded; want error")
} }
return return
} else { } else if err != nil {
if err != nil {
t.Fatalf("unexpected error: %s", err) t.Fatalf("unexpected error: %s", err)
} }
}
if !got.RawEquals(test.Want) { if !got.RawEquals(test.Want) {
t.Errorf("wrong result\ngot: %#v\nwant: %#v", got, test.Want) t.Errorf("wrong result\ngot: %#v\nwant: %#v", got, test.Want)
@ -125,3 +121,211 @@ func TestBcrypt(t *testing.T) {
t.Fatal("succeeded; want error") t.Fatal("succeeded; want error")
} }
} }
func TestMd5(t *testing.T) {
tests := []struct {
String cty.Value
Want cty.Value
Err bool
}{
{
cty.StringVal("tada"),
cty.StringVal("ce47d07243bb6eaf5e1322c81baf9bbf"),
false,
},
{ // Confirm that we're not trimming any whitespaces
cty.StringVal(" tada "),
cty.StringVal("aadf191a583e53062de2d02c008141c4"),
false,
},
{ // We accept empty string too
cty.StringVal(""),
cty.StringVal("d41d8cd98f00b204e9800998ecf8427e"),
false,
},
}
for _, test := range tests {
t.Run(fmt.Sprintf("md5(%#v)", test.String), func(t *testing.T) {
got, err := Md5(test.String)
if test.Err {
if err == nil {
t.Fatal("succeeded; want error")
}
return
} else if err != nil {
t.Fatalf("unexpected error: %s", err)
}
if !got.RawEquals(test.Want) {
t.Errorf("wrong result\ngot: %#v\nwant: %#v", got, test.Want)
}
})
}
}
func TestRsaDecrypt(t *testing.T) {
tests := []struct {
Ciphertext cty.Value
Privatekey cty.Value
Want cty.Value
Err bool
}{
// Base-64 encoded cipher decrypts correctly
{
cty.StringVal(CipherBase64),
cty.StringVal(PrivateKey),
cty.StringVal("message"),
false,
},
// Wrong key
{
cty.StringVal(CipherBase64),
cty.StringVal(WrongPrivateKey),
cty.UnknownVal(cty.String),
true,
},
// Bad key
{
cty.StringVal(CipherBase64),
cty.StringVal("bad key"),
cty.UnknownVal(cty.String),
true,
},
// Empty key
{
cty.StringVal(CipherBase64),
cty.StringVal(""),
cty.UnknownVal(cty.String),
true,
},
// Bad cipher
{
cty.StringVal("bad cipher"),
cty.StringVal(PrivateKey),
cty.UnknownVal(cty.String),
true,
},
// Empty cipher
{
cty.StringVal(""),
cty.StringVal(PrivateKey),
cty.UnknownVal(cty.String),
true,
},
}
for _, test := range tests {
t.Run(fmt.Sprintf("RsaDecrypt(%#v, %#v)", test.Ciphertext, test.Privatekey), func(t *testing.T) {
got, err := RsaDecrypt(test.Ciphertext, test.Privatekey)
if test.Err {
if err == nil {
t.Fatal("succeeded; want error")
}
return
} else if err != nil {
t.Fatalf("unexpected error: %s", err)
}
if !got.RawEquals(test.Want) {
t.Errorf("wrong result\ngot: %#v\nwant: %#v", got, test.Want)
}
})
}
}
func TestSha1(t *testing.T) {
tests := []struct {
String cty.Value
Want cty.Value
Err bool
}{
{
cty.StringVal("test"),
cty.StringVal("a94a8fe5ccb19ba61c4c0873d391e987982fbbd3"),
false,
},
}
for _, test := range tests {
t.Run(fmt.Sprintf("sha1(%#v)", test.String), func(t *testing.T) {
got, err := Sha1(test.String)
if test.Err {
if err == nil {
t.Fatal("succeeded; want error")
}
return
} else if err != nil {
t.Fatalf("unexpected error: %s", err)
}
if !got.RawEquals(test.Want) {
t.Errorf("wrong result\ngot: %#v\nwant: %#v", got, test.Want)
}
})
}
}
const (
CipherBase64 = "eczGaDhXDbOFRZGhjx2etVzWbRqWDlmq0bvNt284JHVbwCgObiuyX9uV0LSAMY707IEgMkExJqXmsB4OWKxvB7epRB9G/3+F+pcrQpODlDuL9oDUAsa65zEpYF0Wbn7Oh7nrMQncyUPpyr9WUlALl0gRWytOA23S+y5joa4M34KFpawFgoqTu/2EEH4Xl1zo+0fy73fEto+nfkUY+meuyGZ1nUx/+DljP7ZqxHBFSlLODmtuTMdswUbHbXbWneW51D7Jm7xB8nSdiA2JQNK5+Sg5x8aNfgvFTt/m2w2+qpsyFa5Wjeu6fZmXSl840CA07aXbk9vN4I81WmJyblD/ZA=="
PrivateKey = `
-----BEGIN RSA PRIVATE KEY-----
MIIEowIBAAKCAQEAgUElV5mwqkloIrM8ZNZ72gSCcnSJt7+/Usa5G+D15YQUAdf9
c1zEekTfHgDP+04nw/uFNFaE5v1RbHaPxhZYVg5ZErNCa/hzn+x10xzcepeS3KPV
Xcxae4MR0BEegvqZqJzN9loXsNL/c3H/B+2Gle3hTxjlWFb3F5qLgR+4Mf4ruhER
1v6eHQa/nchi03MBpT4UeJ7MrL92hTJYLdpSyCqmr8yjxkKJDVC2uRrr+sTSxfh7
r6v24u/vp/QTmBIAlNPgadVAZw17iNNb7vjV7Gwl/5gHXonCUKURaV++dBNLrHIZ
pqcAM8wHRph8mD1EfL9hsz77pHewxolBATV+7QIDAQABAoIBAC1rK+kFW3vrAYm3
+8/fQnQQw5nec4o6+crng6JVQXLeH32qXShNf8kLLG/Jj0vaYcTPPDZw9JCKkTMQ
0mKj9XR/5DLbBMsV6eNXXuvJJ3x4iKW5eD9WkLD4FKlNarBRyO7j8sfPTqXW7uat
NxWdFH7YsSRvNh/9pyQHLWA5OituidMrYbc3EUx8B1GPNyJ9W8Q8znNYLfwYOjU4
Wv1SLE6qGQQH9Q0WzA2WUf8jklCYyMYTIywAjGb8kbAJlKhmj2t2Igjmqtwt1PYc
pGlqbtQBDUiWXt5S4YX/1maIQ/49yeNUajjpbJiH3DbhJbHwFTzP3pZ9P9GHOzlG
kYR+wSECgYEAw/Xida8kSv8n86V3qSY/I+fYQ5V+jDtXIE+JhRnS8xzbOzz3v0WS
Oo5H+o4nJx5eL3Ghb3Gcm0Jn46dHrxinHbm+3RjXv/X6tlbxIYjRSQfHOTSMCTvd
qcliF5vC6RCLXuc7R+IWR1Ky6eDEZGtrvt3DyeYABsp9fRUFR/6NluUCgYEAqNsw
1aSl7WJa27F0DoJdlU9LWerpXcazlJcIdOz/S9QDmSK3RDQTdqfTxRmrxiYI9LEs
mkOkvzlnnOBMpnZ3ZOU5qIRfprecRIi37KDAOHWGnlC0EWGgl46YLb7/jXiWf0AG
Y+DfJJNd9i6TbIDWu8254/erAS6bKMhW/3q7f2kCgYAZ7Id/BiKJAWRpqTRBXlvw
BhXoKvjI2HjYP21z/EyZ+PFPzur/lNaZhIUlMnUfibbwE9pFggQzzf8scM7c7Sf+
mLoVSdoQ/Rujz7CqvQzi2nKSsM7t0curUIb3lJWee5/UeEaxZcmIufoNUrzohAWH
BJOIPDM4ssUTLRq7wYM9uQKBgHCBau5OP8gE6mjKuXsZXWUoahpFLKwwwmJUp2vQ
pOFPJ/6WZOlqkTVT6QPAcPUbTohKrF80hsZqZyDdSfT3peFx4ZLocBrS56m6NmHR
UYHMvJ8rQm76T1fryHVidz85g3zRmfBeWg8yqT5oFg4LYgfLsPm1gRjOhs8LfPvI
OLlRAoGBAIZ5Uv4Z3s8O7WKXXUe/lq6j7vfiVkR1NW/Z/WLKXZpnmvJ7FgxN4e56
RXT7GwNQHIY8eDjDnsHxzrxd+raOxOZeKcMHj3XyjCX3NHfTscnsBPAGYpY/Wxzh
T8UYnFu6RzkixElTf2rseEav7rkdKkI3LAeIZy7B0HulKKsmqVQ7
-----END RSA PRIVATE KEY-----
`
WrongPrivateKey = `
-----BEGIN RSA PRIVATE KEY-----
MIIEowIBAAKCAQEAlrCgnEVgmNKCq7KPc+zUU5IrxPu1ClMNJS7RTsTPEkbwe5SB
p+6V6WtCbD/X/lDRRGbOENChh1Phulb7lViqgrdpHydgsrKoS5ah3DfSIxLFLE00
9Yo4TCYwgw6+s59j16ZAFVinaQ9l6Kmrb2ll136hMrz8QKh+qw+onOLd38WFgm+W
ZtUqSXf2LANzfzzy4OWFNyFqKaCAolSkPdTS9Nz+svtScvp002DQp8OdP1AgPO+l
o5N3M38Fftapwg0pCtJ5Zq0NRWIXEonXiTEMA6zy3gEZVOmDxoIFUWnmrqlMJLFy
5S6LDrHSdqJhCxDK6WRZj43X9j8spktk3eGhMwIDAQABAoIBAAem8ID/BOi9x+Tw
LFi2rhGQWqimH4tmrEQ3HGnjlKBY+d1MrUjZ1MMFr1nP5CgF8pqGnfA8p/c3Sz8r
K5tp5T6+EZiDZ2WrrOApxg5ox0MAsQKO6SGO40z6o3wEQ6rbbTaGOrraxaWQIpyu
AQanU4Sd6ZGqByVBaS1GnklZO+shCHqw73b7g1cpLEmFzcYnKHYHlUUIsstMe8E1
BaCY0CH7JbWBjcbiTnBVwIRZuu+EjGiQuhTilYL2OWqoMVg1WU0L2IFpR8lkf/2W
SBx5J6xhwbBGASOpM+qidiN580GdPzGhWYSqKGroHEzBm6xPSmV1tadNA26WFG4p
pthLiAECgYEA5BsPRpNYJAQLu5B0N7mj9eEp0HABVEgL/MpwiImjaKdAwp78HM64
IuPvJxs7r+xESiIz4JyjR8zrQjYOCKJsARYkmNlEuAz0SkHabCw1BdEBwUhjUGVB
efoERK6GxfAoNqmSDwsOvHFOtsmDIlbHmg7G2rUxNVpeou415BSB0B8CgYEAqR4J
YHKk2Ibr9rU+rBU33TcdTGw0aAkFNAVeqM9j0haWuFXmV3RArgoy09lH+2Ha6z/g
fTX2xSDAWV7QUlLOlBRIhurPAo2jO2yCrGHPZcWiugstrR2hTTInigaSnCmK3i7F
6sYmL3S7K01IcVNxSlWvGijtClT92Cl2WUCTfG0CgYAiEjyk4QtQTd5mxLvnOu5X
oqs5PBGmwiAwQRiv/EcRMbJFn7Oupd3xMDSflbzDmTnWDOfMy/jDl8MoH6TW+1PA
kcsjnYhbKWwvz0hN0giVdtOZSDO1ZXpzOrn6fEsbM7T9/TQY1SD9WrtUKCNTNL0Z
sM1ZC6lu+7GZCpW4HKwLJwKBgQCRT0yxQXBg1/UxwuO5ynV4rx2Oh76z0WRWIXMH
S0MyxdP1SWGkrS/SGtM3cg/GcHtA/V6vV0nUcWK0p6IJyjrTw2XZ/zGluPuTWJYi
9dvVT26Vunshrz7kbH7KuwEICy3V4IyQQHeY+QzFlR70uMS0IVFWAepCoWqHbIDT
CYhwNQKBgGPcLXmjpGtkZvggl0aZr9LsvCTckllSCFSI861kivL/rijdNoCHGxZv
dfDkLTLcz9Gk41rD9Gxn/3sqodnTAc3Z2PxFnzg1Q/u3+x6YAgBwI/g/jE2xutGW
H7CurtMwALQ/n/6LUKFmjRZjqbKX9SO2QSaC3grd6sY9Tu+bZjLe
-----END RSA PRIVATE KEY-----
`
)

View File

@ -46,19 +46,17 @@ var TimeAddFunc = function.New(&function.Spec{
// Timestamp returns a string representation of the current date and time. // Timestamp returns a string representation of the current date and time.
// //
// In the Terraform language, timestamps are conventionally represented as // In the Terraform language, timestamps are conventionally represented as
// strings using [RFC 3339](https://tools.ietf.org/html/rfc3339) // strings using RFC 3339 "Date and Time format" syntax, and so timestamp
// "Date and Time format" syntax, and so `timestamp` returns a string // returns a string in this format.
// in this format.
func Timestamp() (cty.Value, error) { func Timestamp() (cty.Value, error) {
return TimestampFunc.Call([]cty.Value{}) return TimestampFunc.Call([]cty.Value{})
} }
// Timeadd adds a duration to a timestamp, returning a new timestamp. // TimeAdd adds a duration to a timestamp, returning a new timestamp.
// //
// In the Terraform language, timestamps are conventionally represented as // In the Terraform language, timestamps are conventionally represented as
// strings using [RFC 3339](https://tools.ietf.org/html/rfc3339) // strings using RFC 3339 "Date and Time format" syntax. Timeadd requires
// "Date and Time format" syntax. `timeadd` requires the `timestamp` argument // the timestamp argument to be a string conforming to this syntax.
// to be a string conforming to this syntax.
// //
// `duration` is a string representation of a time difference, consisting of // `duration` is a string representation of a time difference, consisting of
// sequences of number and unit pairs, like `"1.5h"` or `1h30m`. The accepted // sequences of number and unit pairs, like `"1.5h"` or `1h30m`. The accepted

View File

@ -73,11 +73,9 @@ func TestTimeadd(t *testing.T) {
t.Fatal("succeeded; want error") t.Fatal("succeeded; want error")
} }
return return
} else { } else if err != nil {
if err != nil {
t.Fatalf("unexpected error: %s", err) t.Fatalf("unexpected error: %s", err)
} }
}
if !got.RawEquals(test.Want) { if !got.RawEquals(test.Want) {
t.Errorf("wrong result\ngot: %#v\nwant: %#v", got, test.Want) t.Errorf("wrong result\ngot: %#v\nwant: %#v", got, test.Want)

View File

@ -89,8 +89,7 @@ var URLEncodeFunc = function.New(&function.Spec{
// Base64Decode decodes a string containing a base64 sequence. // Base64Decode decodes a string containing a base64 sequence.
// //
// Terraform uses the "standard" Base64 alphabet as defined in // Terraform uses the "standard" Base64 alphabet as defined in RFC 4648 section 4.
// [RFC 4648 section 4](https://tools.ietf.org/html/rfc4648#section-4).
// //
// Strings in the Terraform language are sequences of unicode characters rather // Strings in the Terraform language are sequences of unicode characters rather
// than bytes, so this function will also interpret the resulting bytes as // than bytes, so this function will also interpret the resulting bytes as
@ -102,8 +101,7 @@ func Base64Decode(str cty.Value) (cty.Value, error) {
// Base64Encode applies Base64 encoding to a string. // Base64Encode applies Base64 encoding to a string.
// //
// Terraform uses the "standard" Base64 alphabet as defined in // Terraform uses the "standard" Base64 alphabet as defined in RFC 4648 section 4.
// [RFC 4648 section 4](https://tools.ietf.org/html/rfc4648#section-4).
// //
// Strings in the Terraform language are sequences of unicode characters rather // Strings in the Terraform language are sequences of unicode characters rather
// than bytes, so this function will first encode the characters from the string // than bytes, so this function will first encode the characters from the string
@ -112,11 +110,11 @@ func Base64Encode(str cty.Value) (cty.Value, error) {
return Base64EncodeFunc.Call([]cty.Value{str}) return Base64EncodeFunc.Call([]cty.Value{str})
} }
// Base64gzip compresses a string with gzip and then encodes the result in // Base64Gzip compresses a string with gzip and then encodes the result in
// Base64 encoding. // Base64 encoding.
// //
// Terraform uses the "standard" Base64 alphabet as defined in // Terraform uses the "standard" Base64 alphabet as defined in RFC 4648 section 4.
// [RFC 4648 section 4](https://tools.ietf.org/html/rfc4648#section-4) //
// Strings in the Terraform language are sequences of unicode characters rather // Strings in the Terraform language are sequences of unicode characters rather
// than bytes, so this function will first encode the characters from the string // than bytes, so this function will first encode the characters from the string
// as UTF-8, then apply gzip compression, and then finally apply Base64 encoding. // as UTF-8, then apply gzip compression, and then finally apply Base64 encoding.
@ -124,12 +122,11 @@ func Base64Gzip(str cty.Value) (cty.Value, error) {
return Base64GzipFunc.Call([]cty.Value{str}) return Base64GzipFunc.Call([]cty.Value{str})
} }
// UrlEncode applies URL encoding to a given string. // URLEncode applies URL encoding to a given string.
// //
// This function identifies characters in the given string that would have a // This function identifies characters in the given string that would have a
// special meaning when included as a query string argument in a URL and // special meaning when included as a query string argument in a URL and
// escapes them using // escapes them using RFC 3986 "percent encoding".
// [RFC 3986 "percent encoding"](https://tools.ietf.org/html/rfc3986#section-2.1).
// //
// If the given string contains non-ASCII characters, these are first encoded as // If the given string contains non-ASCII characters, these are first encoded as
// UTF-8 and then percent encoding is applied separately to each UTF-8 byte. // UTF-8 and then percent encoding is applied separately to each UTF-8 byte.

View File

@ -34,11 +34,9 @@ func TestBase64Decode(t *testing.T) {
t.Fatal("succeeded; want error") t.Fatal("succeeded; want error")
} }
return return
} else { } else if err != nil {
if err != nil {
t.Fatalf("unexpected error: %s", err) t.Fatalf("unexpected error: %s", err)
} }
}
if !got.RawEquals(test.Want) { if !got.RawEquals(test.Want) {
t.Errorf("wrong result\ngot: %#v\nwant: %#v", got, test.Want) t.Errorf("wrong result\ngot: %#v\nwant: %#v", got, test.Want)
@ -69,11 +67,9 @@ func TestBase64Encode(t *testing.T) {
t.Fatal("succeeded; want error") t.Fatal("succeeded; want error")
} }
return return
} else { } else if err != nil {
if err != nil {
t.Fatalf("unexpected error: %s", err) t.Fatalf("unexpected error: %s", err)
} }
}
if !got.RawEquals(test.Want) { if !got.RawEquals(test.Want) {
t.Errorf("wrong result\ngot: %#v\nwant: %#v", got, test.Want) t.Errorf("wrong result\ngot: %#v\nwant: %#v", got, test.Want)
@ -104,11 +100,9 @@ func TestBase64Gzip(t *testing.T) {
t.Fatal("succeeded; want error") t.Fatal("succeeded; want error")
} }
return return
} else { } else if err != nil {
if err != nil {
t.Fatalf("unexpected error: %s", err) t.Fatalf("unexpected error: %s", err)
} }
}
if !got.RawEquals(test.Want) { if !got.RawEquals(test.Want) {
t.Errorf("wrong result\ngot: %#v\nwant: %#v", got, test.Want) t.Errorf("wrong result\ngot: %#v\nwant: %#v", got, test.Want)
@ -154,11 +148,9 @@ func TestURLEncode(t *testing.T) {
t.Fatal("succeeded; want error") t.Fatal("succeeded; want error")
} }
return return
} else { } else if err != nil {
if err != nil {
t.Fatalf("unexpected error: %s", err) t.Fatalf("unexpected error: %s", err)
} }
}
if !got.RawEquals(test.Want) { if !got.RawEquals(test.Want) {
t.Errorf("wrong result\ngot: %#v\nwant: %#v", got, test.Want) t.Errorf("wrong result\ngot: %#v\nwant: %#v", got, test.Want)

View File

@ -63,7 +63,8 @@ func MakeFileFunc(baseDir string, encBase64 bool) function.Function {
}) })
} }
// BasenameFunc constructs a function that takes a string containing a filesystem path and removes all except the last portion from it. // BasenameFunc constructs a function that takes a string containing a filesystem path
// and removes all except the last portion from it.
var BasenameFunc = function.New(&function.Spec{ var BasenameFunc = function.New(&function.Spec{
Params: []function.Parameter{ Params: []function.Parameter{
{ {
@ -77,7 +78,8 @@ var BasenameFunc = function.New(&function.Spec{
}, },
}) })
// DirnameFunc constructs a function that takes a string containing a filesystem path and removes the last portion from it. // DirnameFunc constructs a function that takes a string containing a filesystem path
// and removes the last portion from it.
var DirnameFunc = function.New(&function.Spec{ var DirnameFunc = function.New(&function.Spec{
Params: []function.Parameter{ Params: []function.Parameter{
{ {

View File

@ -41,11 +41,9 @@ func TestFile(t *testing.T) {
t.Fatal("succeeded; want error") t.Fatal("succeeded; want error")
} }
return return
} else { } else if err != nil {
if err != nil {
t.Fatalf("unexpected error: %s", err) t.Fatalf("unexpected error: %s", err)
} }
}
if !got.RawEquals(test.Want) { if !got.RawEquals(test.Want) {
t.Errorf("wrong result\ngot: %#v\nwant: %#v", got, test.Want) t.Errorf("wrong result\ngot: %#v\nwant: %#v", got, test.Want)
@ -86,11 +84,9 @@ func TestFileBase64(t *testing.T) {
t.Fatal("succeeded; want error") t.Fatal("succeeded; want error")
} }
return return
} else { } else if err != nil {
if err != nil {
t.Fatalf("unexpected error: %s", err) t.Fatalf("unexpected error: %s", err)
} }
}
if !got.RawEquals(test.Want) { if !got.RawEquals(test.Want) {
t.Errorf("wrong result\ngot: %#v\nwant: %#v", got, test.Want) t.Errorf("wrong result\ngot: %#v\nwant: %#v", got, test.Want)
@ -131,11 +127,9 @@ func TestBasename(t *testing.T) {
t.Fatal("succeeded; want error") t.Fatal("succeeded; want error")
} }
return return
} else { } else if err != nil {
if err != nil {
t.Fatalf("unexpected error: %s", err) t.Fatalf("unexpected error: %s", err)
} }
}
if !got.RawEquals(test.Want) { if !got.RawEquals(test.Want) {
t.Errorf("wrong result\ngot: %#v\nwant: %#v", got, test.Want) t.Errorf("wrong result\ngot: %#v\nwant: %#v", got, test.Want)
@ -181,11 +175,9 @@ func TestDirname(t *testing.T) {
t.Fatal("succeeded; want error") t.Fatal("succeeded; want error")
} }
return return
} else { } else if err != nil {
if err != nil {
t.Fatalf("unexpected error: %s", err) t.Fatalf("unexpected error: %s", err)
} }
}
if !got.RawEquals(test.Want) { if !got.RawEquals(test.Want) {
t.Errorf("wrong result\ngot: %#v\nwant: %#v", got, test.Want) t.Errorf("wrong result\ngot: %#v\nwant: %#v", got, test.Want)
@ -236,11 +228,9 @@ func TestPathExpand(t *testing.T) {
t.Fatal("succeeded; want error") t.Fatal("succeeded; want error")
} }
return return
} else { } else if err != nil {
if err != nil {
t.Fatalf("unexpected error: %s", err) t.Fatalf("unexpected error: %s", err)
} }
}
if !got.RawEquals(test.Want) { if !got.RawEquals(test.Want) {
t.Errorf("wrong result\ngot: %#v\nwant: %#v", got, test.Want) t.Errorf("wrong result\ngot: %#v\nwant: %#v", got, test.Want)

View File

@ -11,6 +11,7 @@ import (
) )
var impureFunctions = []string{ var impureFunctions = []string{
"bcrypt",
"timestamp", "timestamp",
"uuid", "uuid",
} }
@ -71,13 +72,13 @@ func (s *Scope) Functions() map[string]function.Function {
"lower": stdlib.LowerFunc, "lower": stdlib.LowerFunc,
"map": unimplFunc, // TODO "map": unimplFunc, // TODO
"max": stdlib.MaxFunc, "max": stdlib.MaxFunc,
"md5": unimplFunc, // TODO "md5": funcs.Md5Func,
"merge": unimplFunc, // TODO "merge": unimplFunc, // TODO
"min": stdlib.MinFunc, "min": stdlib.MinFunc,
"pathexpand": funcs.PathExpandFunc, "pathexpand": funcs.PathExpandFunc,
"pow": unimplFunc, // TODO "pow": unimplFunc, // TODO
"replace": unimplFunc, // TODO "replace": unimplFunc, // TODO
"rsadecrypt": unimplFunc, // TODO "rsadecrypt": funcs.RsaDecryptFunc,
"sha1": unimplFunc, // TODO "sha1": unimplFunc, // TODO
"sha256": unimplFunc, // TODO "sha256": unimplFunc, // TODO
"sha512": unimplFunc, // TODO "sha512": unimplFunc, // TODO
@ -92,7 +93,7 @@ func (s *Scope) Functions() map[string]function.Function {
"transpose": unimplFunc, // TODO "transpose": unimplFunc, // TODO
"trimspace": unimplFunc, // TODO "trimspace": unimplFunc, // TODO
"upper": stdlib.UpperFunc, "upper": stdlib.UpperFunc,
"urlencode": funcs.UrlEncodeFunc, "urlencode": funcs.URLEncodeFunc,
"uuid": funcs.UUIDFunc, "uuid": funcs.UUIDFunc,
"values": unimplFunc, // TODO "values": unimplFunc, // TODO
"zipmap": unimplFunc, // TODO "zipmap": unimplFunc, // TODO