github.com/hugorut/terraform@v1.1.3/src/lang/functions.go (about) 1 package lang 2 3 import ( 4 "fmt" 5 6 "github.com/hashicorp/hcl/v2/ext/tryfunc" 7 ctyyaml "github.com/zclconf/go-cty-yaml" 8 "github.com/zclconf/go-cty/cty" 9 "github.com/zclconf/go-cty/cty/function" 10 "github.com/zclconf/go-cty/cty/function/stdlib" 11 12 "github.com/hugorut/terraform/src/experiments" 13 "github.com/hugorut/terraform/src/lang/funcs" 14 ) 15 16 var impureFunctions = []string{ 17 "bcrypt", 18 "timestamp", 19 "uuid", 20 } 21 22 // Functions returns the set of functions that should be used to when evaluating 23 // expressions in the receiving scope. 24 func (s *Scope) Functions() map[string]function.Function { 25 s.funcsLock.Lock() 26 if s.funcs == nil { 27 // Some of our functions are just directly the cty stdlib functions. 28 // Others are implemented in the subdirectory "funcs" here in this 29 // repository. New functions should generally start out their lives 30 // in the "funcs" directory and potentially graduate to cty stdlib 31 // later if the functionality seems to be something domain-agnostic 32 // that would be useful to all applications using cty functions. 33 34 s.funcs = map[string]function.Function{ 35 "abs": stdlib.AbsoluteFunc, 36 "abspath": funcs.AbsPathFunc, 37 "alltrue": funcs.AllTrueFunc, 38 "anytrue": funcs.AnyTrueFunc, 39 "basename": funcs.BasenameFunc, 40 "base64decode": funcs.Base64DecodeFunc, 41 "base64encode": funcs.Base64EncodeFunc, 42 "base64gzip": funcs.Base64GzipFunc, 43 "base64sha256": funcs.Base64Sha256Func, 44 "base64sha512": funcs.Base64Sha512Func, 45 "bcrypt": funcs.BcryptFunc, 46 "can": tryfunc.CanFunc, 47 "ceil": stdlib.CeilFunc, 48 "chomp": stdlib.ChompFunc, 49 "cidrhost": funcs.CidrHostFunc, 50 "cidrnetmask": funcs.CidrNetmaskFunc, 51 "cidrsubnet": funcs.CidrSubnetFunc, 52 "cidrsubnets": funcs.CidrSubnetsFunc, 53 "coalesce": funcs.CoalesceFunc, 54 "coalescelist": stdlib.CoalesceListFunc, 55 "compact": stdlib.CompactFunc, 56 "concat": stdlib.ConcatFunc, 57 "contains": stdlib.ContainsFunc, 58 "csvdecode": stdlib.CSVDecodeFunc, 59 "defaults": s.experimentalFunction(experiments.ModuleVariableOptionalAttrs, funcs.DefaultsFunc), 60 "dirname": funcs.DirnameFunc, 61 "distinct": stdlib.DistinctFunc, 62 "element": stdlib.ElementFunc, 63 "chunklist": stdlib.ChunklistFunc, 64 "file": funcs.MakeFileFunc(s.BaseDir, false), 65 "fileexists": funcs.MakeFileExistsFunc(s.BaseDir), 66 "fileset": funcs.MakeFileSetFunc(s.BaseDir), 67 "filebase64": funcs.MakeFileFunc(s.BaseDir, true), 68 "filebase64sha256": funcs.MakeFileBase64Sha256Func(s.BaseDir), 69 "filebase64sha512": funcs.MakeFileBase64Sha512Func(s.BaseDir), 70 "filemd5": funcs.MakeFileMd5Func(s.BaseDir), 71 "filesha1": funcs.MakeFileSha1Func(s.BaseDir), 72 "filesha256": funcs.MakeFileSha256Func(s.BaseDir), 73 "filesha512": funcs.MakeFileSha512Func(s.BaseDir), 74 "flatten": stdlib.FlattenFunc, 75 "floor": stdlib.FloorFunc, 76 "format": stdlib.FormatFunc, 77 "formatdate": stdlib.FormatDateFunc, 78 "formatlist": stdlib.FormatListFunc, 79 "indent": stdlib.IndentFunc, 80 "index": funcs.IndexFunc, // stdlib.IndexFunc is not compatible 81 "join": stdlib.JoinFunc, 82 "jsondecode": stdlib.JSONDecodeFunc, 83 "jsonencode": stdlib.JSONEncodeFunc, 84 "keys": stdlib.KeysFunc, 85 "length": funcs.LengthFunc, 86 "list": funcs.ListFunc, 87 "log": stdlib.LogFunc, 88 "lookup": funcs.LookupFunc, 89 "lower": stdlib.LowerFunc, 90 "map": funcs.MapFunc, 91 "matchkeys": funcs.MatchkeysFunc, 92 "max": stdlib.MaxFunc, 93 "md5": funcs.Md5Func, 94 "merge": stdlib.MergeFunc, 95 "min": stdlib.MinFunc, 96 "one": funcs.OneFunc, 97 "parseint": stdlib.ParseIntFunc, 98 "pathexpand": funcs.PathExpandFunc, 99 "pow": stdlib.PowFunc, 100 "range": stdlib.RangeFunc, 101 "regex": stdlib.RegexFunc, 102 "regexall": stdlib.RegexAllFunc, 103 "replace": funcs.ReplaceFunc, 104 "reverse": stdlib.ReverseListFunc, 105 "rsadecrypt": funcs.RsaDecryptFunc, 106 "sensitive": funcs.SensitiveFunc, 107 "nonsensitive": funcs.NonsensitiveFunc, 108 "setintersection": stdlib.SetIntersectionFunc, 109 "setproduct": stdlib.SetProductFunc, 110 "setsubtract": stdlib.SetSubtractFunc, 111 "setunion": stdlib.SetUnionFunc, 112 "sha1": funcs.Sha1Func, 113 "sha256": funcs.Sha256Func, 114 "sha512": funcs.Sha512Func, 115 "signum": stdlib.SignumFunc, 116 "slice": stdlib.SliceFunc, 117 "sort": stdlib.SortFunc, 118 "split": stdlib.SplitFunc, 119 "strrev": stdlib.ReverseFunc, 120 "substr": stdlib.SubstrFunc, 121 "sum": funcs.SumFunc, 122 "textdecodebase64": funcs.TextDecodeBase64Func, 123 "textencodebase64": funcs.TextEncodeBase64Func, 124 "timestamp": funcs.TimestampFunc, 125 "timeadd": stdlib.TimeAddFunc, 126 "title": stdlib.TitleFunc, 127 "tostring": funcs.MakeToFunc(cty.String), 128 "tonumber": funcs.MakeToFunc(cty.Number), 129 "tobool": funcs.MakeToFunc(cty.Bool), 130 "toset": funcs.MakeToFunc(cty.Set(cty.DynamicPseudoType)), 131 "tolist": funcs.MakeToFunc(cty.List(cty.DynamicPseudoType)), 132 "tomap": funcs.MakeToFunc(cty.Map(cty.DynamicPseudoType)), 133 "transpose": funcs.TransposeFunc, 134 "trim": stdlib.TrimFunc, 135 "trimprefix": stdlib.TrimPrefixFunc, 136 "trimspace": stdlib.TrimSpaceFunc, 137 "trimsuffix": stdlib.TrimSuffixFunc, 138 "try": tryfunc.TryFunc, 139 "upper": stdlib.UpperFunc, 140 "urlencode": funcs.URLEncodeFunc, 141 "uuid": funcs.UUIDFunc, 142 "uuidv5": funcs.UUIDV5Func, 143 "values": stdlib.ValuesFunc, 144 "yamldecode": ctyyaml.YAMLDecodeFunc, 145 "yamlencode": ctyyaml.YAMLEncodeFunc, 146 "zipmap": stdlib.ZipmapFunc, 147 } 148 149 s.funcs["templatefile"] = funcs.MakeTemplateFileFunc(s.BaseDir, func() map[string]function.Function { 150 // The templatefile function prevents recursive calls to itself 151 // by copying this map and overwriting the "templatefile" entry. 152 return s.funcs 153 }) 154 155 if s.ConsoleMode { 156 // The type function is only available in terraform console. 157 s.funcs["type"] = funcs.TypeFunc 158 } 159 160 if s.PureOnly { 161 // Force our few impure functions to return unknown so that we 162 // can defer evaluating them until a later pass. 163 for _, name := range impureFunctions { 164 s.funcs[name] = function.Unpredictable(s.funcs[name]) 165 } 166 } 167 } 168 s.funcsLock.Unlock() 169 170 return s.funcs 171 } 172 173 // experimentalFunction checks whether the given experiment is enabled for 174 // the recieving scope. If so, it will return the given function verbatim. 175 // If not, it will return a placeholder function that just returns an 176 // error explaining that the function requires the experiment to be enabled. 177 func (s *Scope) experimentalFunction(experiment experiments.Experiment, fn function.Function) function.Function { 178 if s.activeExperiments.Has(experiment) { 179 return fn 180 } 181 182 err := fmt.Errorf( 183 "this function is experimental and available only when the experiment keyword %s is enabled for the current module", 184 experiment.Keyword(), 185 ) 186 187 return function.New(&function.Spec{ 188 Params: fn.Params(), 189 VarParam: fn.VarParam(), 190 Type: func(args []cty.Value) (cty.Type, error) { 191 return cty.DynamicPseudoType, err 192 }, 193 Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) { 194 // It would be weird to get here because the Type function always 195 // fails, but we'll return an error here too anyway just to be 196 // robust. 197 return cty.DynamicVal, err 198 }, 199 }) 200 }