github.com/terramate-io/tf@v0.0.0-20230830114523-fce866b4dfcd/lang/functions.go (about)

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