lang: Expose built-in functions under core:: namespace

This essentially doubles up the registrations for all of the built-in
functions to have both non-namespaced and core::-namespaced versions of
each function.

This is in preparation for later commits introducing other namespaces,
such as a provider:: namespace which could hold functions that were
contributed by providers that are currently in scope.
pull/34394/head
Martin Atkins 4 years ago committed by James Bardin
parent 2574b899e7
commit d1df979115

@ -135,7 +135,7 @@ func MakeTemplateFileFunc(baseDir string, funcsCb func() map[string]function.Fun
givenFuncs := funcsCb() // this callback indirection is to avoid chicken/egg problems
funcs := make(map[string]function.Function, len(givenFuncs))
for name, fn := range givenFuncs {
if name == "templatefile" {
if name == "templatefile" || name == "core::templatefile" {
// We stub this one out to prevent recursive calls.
funcs[name] = function.New(&function.Spec{
Params: params,

@ -151,6 +151,12 @@ func TestTemplateFile(t *testing.T) {
cty.NilVal,
`testdata/recursive.tmpl:1,3-16: Error in function call; Call to function "templatefile" failed: cannot recursively call templatefile from inside templatefile call.`,
},
{
cty.StringVal("testdata/recursive_namespaced.tmpl"),
cty.MapValEmpty(cty.String),
cty.NilVal,
`testdata/recursive_namespaced.tmpl:1,3-22: Error in function call; Call to function "core::templatefile" failed: cannot recursively call templatefile from inside templatefile call.`,
},
{
cty.StringVal("testdata/list.tmpl"),
cty.ObjectVal(map[string]cty.Value{
@ -183,8 +189,10 @@ func TestTemplateFile(t *testing.T) {
templateFileFn := MakeTemplateFileFunc(".", func() map[string]function.Function {
return map[string]function.Function{
"join": stdlib.JoinFunc,
"templatefile": MakeFileFunc(".", false), // just a placeholder, since templatefile itself overrides this
"join": stdlib.JoinFunc,
"core::join": stdlib.JoinFunc,
"templatefile": MakeFileFunc(".", false), // just a placeholder, since templatefile itself overrides this
"core::templatefile": MakeFileFunc(".", false), // just a placeholder, since templatefile itself overrides this
}
})

@ -0,0 +1 @@
${core::templatefile("recursive_namespaced.tmpl", {})}

@ -29,34 +29,157 @@ func (s *Scope) Functions() map[string]function.Function {
if s.funcs == nil {
s.funcs = baseFunctions(s.BaseDir)
// Then we add some functions that are only relevant when being accessed
// from inside a specific scope.
coreFuncs := map[string]function.Function{
"abs": stdlib.AbsoluteFunc,
"abspath": funcs.AbsPathFunc,
"alltrue": funcs.AllTrueFunc,
"anytrue": funcs.AnyTrueFunc,
"basename": funcs.BasenameFunc,
"base64decode": funcs.Base64DecodeFunc,
"base64encode": funcs.Base64EncodeFunc,
"base64gzip": funcs.Base64GzipFunc,
"base64sha256": funcs.Base64Sha256Func,
"base64sha512": funcs.Base64Sha512Func,
"bcrypt": funcs.BcryptFunc,
"can": tryfunc.CanFunc,
"ceil": stdlib.CeilFunc,
"chomp": stdlib.ChompFunc,
"cidrhost": funcs.CidrHostFunc,
"cidrnetmask": funcs.CidrNetmaskFunc,
"cidrsubnet": funcs.CidrSubnetFunc,
"cidrsubnets": funcs.CidrSubnetsFunc,
"coalesce": funcs.CoalesceFunc,
"coalescelist": stdlib.CoalesceListFunc,
"compact": stdlib.CompactFunc,
"concat": stdlib.ConcatFunc,
"contains": stdlib.ContainsFunc,
"csvdecode": stdlib.CSVDecodeFunc,
"dirname": funcs.DirnameFunc,
"distinct": stdlib.DistinctFunc,
"element": stdlib.ElementFunc,
"endswith": funcs.EndsWithFunc,
"chunklist": stdlib.ChunklistFunc,
"file": funcs.MakeFileFunc(s.BaseDir, false),
"fileexists": funcs.MakeFileExistsFunc(s.BaseDir),
"fileset": funcs.MakeFileSetFunc(s.BaseDir),
"filebase64": funcs.MakeFileFunc(s.BaseDir, true),
"filebase64sha256": funcs.MakeFileBase64Sha256Func(s.BaseDir),
"filebase64sha512": funcs.MakeFileBase64Sha512Func(s.BaseDir),
"filemd5": funcs.MakeFileMd5Func(s.BaseDir),
"filesha1": funcs.MakeFileSha1Func(s.BaseDir),
"filesha256": funcs.MakeFileSha256Func(s.BaseDir),
"filesha512": funcs.MakeFileSha512Func(s.BaseDir),
"flatten": stdlib.FlattenFunc,
"floor": stdlib.FloorFunc,
"format": stdlib.FormatFunc,
"formatdate": stdlib.FormatDateFunc,
"formatlist": stdlib.FormatListFunc,
"indent": stdlib.IndentFunc,
"index": funcs.IndexFunc, // stdlib.IndexFunc is not compatible
"join": stdlib.JoinFunc,
"jsondecode": stdlib.JSONDecodeFunc,
"jsonencode": stdlib.JSONEncodeFunc,
"keys": stdlib.KeysFunc,
"length": funcs.LengthFunc,
"list": funcs.ListFunc,
"log": stdlib.LogFunc,
"lookup": funcs.LookupFunc,
"lower": stdlib.LowerFunc,
"map": funcs.MapFunc,
"matchkeys": funcs.MatchkeysFunc,
"max": stdlib.MaxFunc,
"md5": funcs.Md5Func,
"merge": stdlib.MergeFunc,
"min": stdlib.MinFunc,
"one": funcs.OneFunc,
"parseint": stdlib.ParseIntFunc,
"pathexpand": funcs.PathExpandFunc,
"pow": stdlib.PowFunc,
"range": stdlib.RangeFunc,
"regex": stdlib.RegexFunc,
"regexall": stdlib.RegexAllFunc,
"replace": funcs.ReplaceFunc,
"reverse": stdlib.ReverseListFunc,
"rsadecrypt": funcs.RsaDecryptFunc,
"sensitive": funcs.SensitiveFunc,
"nonsensitive": funcs.NonsensitiveFunc,
"setintersection": stdlib.SetIntersectionFunc,
"setproduct": stdlib.SetProductFunc,
"setsubtract": stdlib.SetSubtractFunc,
"setunion": stdlib.SetUnionFunc,
"sha1": funcs.Sha1Func,
"sha256": funcs.Sha256Func,
"sha512": funcs.Sha512Func,
"signum": stdlib.SignumFunc,
"slice": stdlib.SliceFunc,
"sort": stdlib.SortFunc,
"split": stdlib.SplitFunc,
"startswith": funcs.StartsWithFunc,
"strcontains": funcs.StrContainsFunc,
"strrev": stdlib.ReverseFunc,
"substr": stdlib.SubstrFunc,
"sum": funcs.SumFunc,
"textdecodebase64": funcs.TextDecodeBase64Func,
"textencodebase64": funcs.TextEncodeBase64Func,
"timestamp": funcs.TimestampFunc,
"timeadd": stdlib.TimeAddFunc,
"timecmp": funcs.TimeCmpFunc,
"title": stdlib.TitleFunc,
"tostring": funcs.MakeToFunc(cty.String),
"tonumber": funcs.MakeToFunc(cty.Number),
"tobool": funcs.MakeToFunc(cty.Bool),
"toset": funcs.MakeToFunc(cty.Set(cty.DynamicPseudoType)),
"tolist": funcs.MakeToFunc(cty.List(cty.DynamicPseudoType)),
"tomap": funcs.MakeToFunc(cty.Map(cty.DynamicPseudoType)),
"transpose": funcs.TransposeFunc,
"trim": stdlib.TrimFunc,
"trimprefix": stdlib.TrimPrefixFunc,
"trimspace": stdlib.TrimSpaceFunc,
"trimsuffix": stdlib.TrimSuffixFunc,
"try": tryfunc.TryFunc,
"upper": stdlib.UpperFunc,
"urlencode": funcs.URLEncodeFunc,
"uuid": funcs.UUIDFunc,
"uuidv5": funcs.UUIDV5Func,
"values": stdlib.ValuesFunc,
"yamldecode": ctyyaml.YAMLDecodeFunc,
"yamlencode": ctyyaml.YAMLEncodeFunc,
"zipmap": stdlib.ZipmapFunc,
}
coreFuncs["templatefile"] = funcs.MakeTemplateFileFunc(s.BaseDir, func() map[string]function.Function {
// The templatefile function prevents recursive calls to itself
// by copying this map and overwriting the "templatefile" and
// "core:templatefile" entries.
return s.funcs
})
if s.ConsoleMode {
// The type function is only available in terraform console.
s.funcs["type"] = funcs.TypeFunc
coreFuncs["type"] = funcs.TypeFunc
}
if !s.ConsoleMode {
// The plantimestamp function doesn't make sense in the terraform
// console.
s.funcs["plantimestamp"] = funcs.MakeStaticTimestampFunc(s.PlanTimestamp)
coreFuncs["plantimestamp"] = funcs.MakeStaticTimestampFunc(s.PlanTimestamp)
}
if s.PureOnly {
// Force our few impure functions to return unknown so that we
// can defer evaluating them until a later pass.
for _, name := range impureFunctions {
s.funcs[name] = function.Unpredictable(s.funcs[name])
coreFuncs[name] = function.Unpredictable(coreFuncs[name])
}
}
// Add a description to each function and parameter based on the
// contents of descriptionList.
// One must create a matching description entry whenever a new
// function is introduced.
for name, f := range s.funcs {
s.funcs[name] = funcs.WithDescription(name, f)
// All of the built-in functions are also available under the "core::"
// namespace, to distinguish from the "provider::" and "module::"
// namespaces that can serve as external extension points.
s.funcs = make(map[string]function.Function, len(coreFuncs)*2)
for name, fn := range coreFuncs {
s.funcs[name] = funcs.WithDescription(name, fn)
s.funcs["core::"+name] = fn
}
}
s.funcsLock.Unlock()

@ -7,6 +7,7 @@ import (
"fmt"
"os"
"path/filepath"
"strings"
"testing"
"time"
@ -960,6 +961,10 @@ func TestFunctions(t *testing.T) {
`templatefile("hello.tmpl", {name = "Jodie"})`,
cty.StringVal("Hello, Jodie!"),
},
{
`core::templatefile("hello.tmpl", {name = "Namespaced Jodie"})`,
cty.StringVal("Hello, Namespaced Jodie!"),
},
},
"timeadd": {
@ -1100,6 +1105,10 @@ func TestFunctions(t *testing.T) {
`upper("hello")`,
cty.StringVal("HELLO"),
},
{
`core::upper("hello")`,
cty.StringVal("HELLO"),
},
},
"urlencode": {
@ -1207,6 +1216,11 @@ func TestFunctions(t *testing.T) {
delete(allFunctions, impureFunc)
}
for f := range scope.Functions() {
if strings.Contains(f, "::") {
// Only non-namespaced functions are absolutely required to
// have at least one test. (Others _may_ have tests.)
continue
}
if _, ok := tests[f]; !ok {
t.Errorf("Missing test for function %s\n", f)
}

Loading…
Cancel
Save