funcs

package
v1.0.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Apr 27, 2026 License: Apache-2.0 Imports: 28 Imported by: 0

Documentation

Index

Constants

This section is empty.

Variables

View Source
var CoalesceFunc = function.New(&function.Spec{
	Params: []function.Parameter{},
	VarParam: &function.Parameter{
		Name:             "vals",
		Type:             cty.DynamicPseudoType,
		AllowUnknown:     true,
		AllowDynamicType: true,
		AllowNull:        true,
	},
	Type: func(args []cty.Value) (ret cty.Type, err error) {
		argTypes := make([]cty.Type, len(args))
		for i, val := range args {
			argTypes[i] = val.Type()
		}
		retType, _ := convert.UnifyUnsafe(argTypes)
		if retType == cty.NilType {
			return cty.NilType, errors.New("all arguments must have the same type")
		}
		return retType, nil
	},
	Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) {
		for _, argVal := range args {

			argVal, _ = convert.Convert(argVal, retType)
			if !argVal.IsKnown() {
				return cty.UnknownVal(retType), nil
			}
			if argVal.IsNull() {
				continue
			}
			if retType == cty.String && argVal.RawEquals(cty.StringVal("")) {
				continue
			}

			return argVal, nil
		}
		return cty.NilVal, errors.New("no non-null, non-empty-string arguments")
	},
})

CoalesceFunc constructs a function that takes any number of arguments and returns the first one that isn't empty. This function was copied from go-cty stdlib and modified so that it returns the first *non-empty* non-null element from a sequence, instead of merely the first non-null.

View Source
var FormatDateFunc = function.New(&function.Spec{
	Description:  stdlib.FormatDateFunc.Description(),
	Params:       stdlib.FormatDateFunc.Params(),
	Type:         function.StaticReturnType(cty.String),
	RefineResult: refineNonNull,
	Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) {
		val, err := stdlib.FormatDateFunc.Call(args)
		var argsError function.ArgError
		if errors.As(err, &argsError) {

			if argsError.Index == 1 {
				args[1] = cty.StringVal(time.Now().UTC().Format(time.RFC3339))
				val, err = stdlib.FormatDateFunc.Call(args)
				if err == nil {
					return val, nil
				}
			}
		}

		if err != nil {
			return cty.StringVal(time.Now().UTC().Format(time.RFC3339)), nil
		}

		return val, nil
	},
})

FormatDateFunc is a wrapper around the stdlib.FormatDateFunc function that returns the current date if the input date is invalid. This is useful in cases where the date is invalid because of mocking/incomplete data because C3X cannot infer the values from the IaC.

View Source
var JSONDecodeFunc = function.New(&function.Spec{
	Description: `Parses the given string as JSON and returns a value corresponding to what the JSON document describes.`,
	Params: []function.Parameter{
		{
			Name: "str",
			Type: cty.String,
		},
	},
	Type: func(args []cty.Value) (cty.Type, error) {
		str := args[0]
		if !str.IsKnown() {
			return cty.DynamicPseudoType, nil
		}

		val := str.AsString()
		if strings.HasSuffix(val, "-mock") || strings.Contains(val, mock.Identifier) {
			return cty.Object(map[string]cty.Type{
				"foo": cty.String,
			}), nil
		}

		return json.ImpliedType([]byte(val))
	},
	Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) {
		val := args[0].AsString()
		if strings.HasSuffix(val, "-mock") || strings.Contains(val, mock.Identifier) {
			return cty.ObjectVal(map[string]cty.Value{
				"foo": cty.StringVal("bar"),
			}), nil
		}

		return json.Unmarshal([]byte(val), retType)
	},
})

JSONDecodeFunc is a C3X specific version of the json.JSONDecodeFunc which handles C3X mocked return values. If the argument passed to JSONDecodeFunc is a C3X mock (e.g. a string with value mock-value) then we return a mocked object that can be used in the HCL evaluation loop. This means we get less unwanted nil values when evaluating HCL files. This is especially important when evaluating Terragrunt HCL files as unexpected nils cause program termination.

View Source
var MergeFunc = function.New(&function.Spec{
	Description: `Merges all of the elements from the given maps into a single map, or the attributes from given objects into a single object.`,
	Params:      []function.Parameter{},
	VarParam: &function.Parameter{
		Name:             "maps",
		Type:             cty.DynamicPseudoType,
		AllowUnknown:     true,
		AllowDynamicType: true,
		AllowNull:        true,
		AllowMarked:      true,
	},
	Type: func(args []cty.Value) (cty.Type, error) {

		if len(args) == 0 {
			return cty.EmptyObject, nil
		}

		attrs := map[string]cty.Type{}

		first := cty.NilType
		matching := true
		attrsKnown := true
		for i, arg := range args {
			ty := arg.Type()

			arg, _ = arg.Unmark()

			switch {
			case ty.IsObjectType() && !arg.IsNull():
				for attr, aty := range ty.AttributeTypes() {
					attrs[attr] = aty
				}
			case ty.IsMapType():
				switch {
				case arg.IsNull():

				case arg.IsKnown():
					ety := arg.Type().ElementType()
					for it := arg.ElementIterator(); it.Next(); {
						attr, _ := it.Element()
						attrs[attr.AsString()] = ety
					}
				default:

					attrsKnown = false
				}
			}

			if i == 0 {
				first = arg.Type()
				continue
			}

			if !ty.Equals(first) && matching {
				matching = false
			}
		}

		if matching {
			return first, nil
		}

		if !attrsKnown {
			return cty.DynamicPseudoType, nil
		}

		return cty.Object(attrs), nil
	},
	RefineResult: refineNonNull,
	Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) {
		outputMap := make(map[string]cty.Value)
		var markses []cty.ValueMarks // remember any marked maps/objects we find

		for _, arg := range args {

			if arg.IsNull() || !arg.CanIterateElements() {
				continue
			}
			arg, argMarks := arg.Unmark()
			if len(argMarks) > 0 {
				markses = append(markses, argMarks)
			}
			for it := arg.ElementIterator(); it.Next(); {
				k, v := it.Element()
				outputMap[k.AsString()] = v
			}
		}

		switch {
		case retType.IsMapType():
			if len(outputMap) == 0 {
				return cty.MapValEmpty(retType.ElementType()).WithMarks(markses...), nil
			}
			return cty.MapVal(outputMap).WithMarks(markses...), nil
		case retType.IsObjectType(), retType.Equals(cty.DynamicPseudoType):
			return cty.ObjectVal(outputMap).WithMarks(markses...), nil
		default:
			panic(fmt.Sprintf("unexpected return type: %#v", retType))
		}
	},
})

MergeFunc is a C3X specific version of collection.MergeFunc which handles C3X mocked return values. If the argument contains a C3X mock string then we ignore it in the merge.

View Source
var MockTimestampFunc = function.New(&function.Spec{
	Params: []function.Parameter{},
	Type:   function.StaticReturnType(cty.String),
	Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) {
		return cty.StringVal(time.Date(2023, 3, 14, 15, 9, 29, 0, time.UTC).UTC().Format(time.RFC3339)), nil
	},
})

MockTimestampFunc constructs a function that returns a string representation of a static timestamp. We keep this as a static value so that it is deterministic when generating cost estimates.

View Source
var PrintArgs = function.New(&function.Spec{
	Params: []function.Parameter{
		{
			Name: "name",
			Type: cty.String,
		},
		{
			Name:             "v",
			Type:             cty.DynamicPseudoType,
			AllowNull:        true,
			AllowUnknown:     true,
			AllowMarked:      true,
			AllowDynamicType: true,
		},
	},
	Type: func(args []cty.Value) (cty.Type, error) {
		return args[1].Type(), nil
	},
	Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) {
		fmt.Printf("terraform print %q:%s\n", args[0].AsString(), string(valueToBytes(args[1])))

		return args[1], nil
	},
})

PrintArgs prints any number of args to the std out using fmt. This can be used for debugging variables/inputs at points in the Terraform evaluation. Example usage:

c3xprint("test", 50)

will print:

"terraform print "test":cty.IntVal(50)

PrintArgs will return any args passed unaltered so that the args are still safe to use in the evaluation context. e.g:

locals {
	test = c3xprint("a")
}

will still have `local.test` == "a" if used by other Terraform attributes/blocks. This allows debugging to unalter the Terraform evaluation and not cause unwanted consequences.

View Source
var TimeAddFunc = function.New(&function.Spec{
	Params: []function.Parameter{
		{
			Name: "timestamp",
			Type: cty.String,
		},
		{
			Name: "duration",
			Type: cty.String,
		},
	},
	Type: function.StaticReturnType(cty.String),
	Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) {
		ts, err := time.Parse(time.RFC3339, args[0].AsString())
		if err != nil {
			return cty.UnknownVal(cty.String), err
		}
		duration, err := time.ParseDuration(args[1].AsString())
		if err != nil {
			return cty.UnknownVal(cty.String), err
		}

		return cty.StringVal(ts.Add(duration).Format(time.RFC3339)), nil
	},
})

TimeAddFunc constructs a function that adds a duration to a timestamp, returning a new timestamp.

View Source
var YAMLDecodeFunc = function.New(&function.Spec{
	Params: []function.Parameter{
		{
			Name: "src",
			Type: cty.String,
		},
	},
	Type: func(args []cty.Value) (cty.Type, error) {
		if !args[0].IsKnown() {
			return cty.DynamicPseudoType, nil
		}
		if args[0].IsNull() {
			return cty.NilType, function.NewArgErrorf(0, "YAML source code cannot be null")
		}
		val := args[0].AsString()
		if strings.HasSuffix(val, "-mock") || strings.Contains(val, mock.Identifier) {
			return cty.Object(map[string]cty.Type{
				"foo": cty.String,
			}), nil
		}

		return yaml.Standard.ImpliedType([]byte(val))
	},
	Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) {
		val := args[0].AsString()
		if strings.HasSuffix(val, "-mock") || strings.Contains(val, mock.Identifier) {
			return cty.ObjectVal(map[string]cty.Value{
				"foo": cty.StringVal("bar"),
			}), nil
		}

		return yaml.Standard.Unmarshal([]byte(val), retType)
	},
})

YAMLDecodeFunc is a C3X specific version of the yaml.YAMLDecodeFunc which handles C3X mocked return values. If the argument passed to YAMLDecodeFunc is a C3X mock (e.g. a string with value mock-value) then we return a mocked object that can be used in the HCL evaluation loop. This means we get less unwanted nil values when evaluating HCL files. This is especially important when evaluating Terragrunt HCL files as unexpected nils cause program termination.

Functions

func Coalesce

func Coalesce(args ...cty.Value) (cty.Value, error)

Coalesce takes any number of arguments and returns the first one that isn't empty.

func File

func File(baseDir string, path cty.Value) (cty.Value, error)

File reads the contents of the file at the given path.

The file must contain valid UTF-8 bytes, or this function will return an error.

The underlying function implementation works relative to a particular base directory, so this wrapper takes a base directory string and uses it to construct the underlying function before calling it.

func FileBase64

func FileBase64(baseDir string, path cty.Value) (cty.Value, error)

FileBase64 reads the contents of the file at the given path.

The bytes from the file are encoded as base64 before returning.

The underlying function implementation works relative to a particular base directory, so this wrapper takes a base directory string and uses it to construct the underlying function before calling it.

func FileExists

func FileExists(baseDir string, path cty.Value) (cty.Value, error)

FileExists determines whether a file exists at the given path.

The underlying function implementation works relative to a particular base directory, so this wrapper takes a base directory string and uses it to construct the underlying function before calling it.

func FileSet

func FileSet(baseDir string, path, pattern cty.Value) (cty.Value, error)

FileSet enumerates a set of files given a glob pattern

The underlying function implementation works relative to a particular base directory, so this wrapper takes a base directory string and uses it to construct the underlying function before calling it.

func LogArgs

func LogArgs(logger zerolog.Logger) function.Function

LogArgs is identical to PrintArgs but writes the arguments to the C3X log. This is useful to understand arguments as they change in the module evaluation. As the arguments will be printed next to log entries that correspond to the program runtime. e.g:

root_block_device {
	volume_size = c3xlog("test", "foo")
}

will log:

time="2022-12-06T10:27:40Z" level=debug enable_cloud_org=false ... attribute_name=volume_size provider=terraform_dir block_name=root_block_device. sync_usage=false msg="fetching attribute value"
time="2022-12-06T10:27:40Z" level=debug ... msg="terraform print "test":cty.StringVal(\"foo\")"

func MakeFileBase64Sha256Func

func MakeFileBase64Sha256Func(baseDir string) function.Function

MakeFileBase64Sha256Func constructs a function that is like Base64Sha256Func but reads the contents of a file rather than hashing a given literal string.

func MakeFileBase64Sha512Func

func MakeFileBase64Sha512Func(baseDir string) function.Function

MakeFileBase64Sha512Func constructs a function that is like Base64Sha512Func but reads the contents of a file rather than hashing a given literal string.

func MakeFileExistsFunc

func MakeFileExistsFunc(baseDir string) function.Function

MakeFileExistsFunc constructs a function that takes a path and determines whether a file exists at that path

func MakeFileFunc

func MakeFileFunc(baseDir string, encBase64 bool) function.Function

func MakeFileMd5Func

func MakeFileMd5Func(baseDir string) function.Function

MakeFileMd5Func constructs a function that is like Md5Func but reads the contents of a file rather than hashing a given literal string.

func MakeFileSetFunc

func MakeFileSetFunc(baseDir string) function.Function

MakeFileSetFunc constructs a function that takes a glob pattern and enumerates a file set from that pattern

func MakeFileSha1Func

func MakeFileSha1Func(baseDir string) function.Function

MakeFileSha1Func constructs a function that is like Sha1Func but reads the contents of a file rather than hashing a given literal string.

func MakeFileSha256Func

func MakeFileSha256Func(baseDir string) function.Function

MakeFileSha256Func constructs a function that is like Sha256Func but reads the contents of a file rather than hashing a given literal string.

func MakeFileSha512Func

func MakeFileSha512Func(baseDir string) function.Function

MakeFileSha512Func constructs a function that is like Sha512Func but reads the contents of a file rather than hashing a given literal string.

func MakeTemplateFileFunc

func MakeTemplateFileFunc(baseDir string, funcsCb func() map[string]function.Function) function.Function

MakeTemplateFileFunc constructs a function that takes a file path and an arbitrary object of named values and attempts to render the referenced file as a template using HCL template syntax.

The template itself may recursively call other functions so a callback must be provided to get access to those functions. The template cannot, however, access any variables defined in the scope: it is restricted only to those variables provided in the second function argument, to ensure that all dependencies on other graph nodes can be seen before executing this function.

As a special exception, a referenced template file may not recursively call the templatefile function, since that would risk the same file being included into itself indefinitely.

func MakeToFunc

func MakeToFunc(wantTy cty.Type) function.Function

Adapted from https://github.com/zclconf/go-cty/blob/ea922e7a95ba2be57897697117f318670e066d22/cty/function/stdlib/conversion.go to handle C3X mock values.

MakeToFunc constructs a "to..." function, like "tostring", which converts its argument to a specific type or type kind.

The given type wantTy can be any type constraint that cty's "convert" package would accept. In particular, this means that you can pass cty.List(cty.DynamicPseudoType) to mean "list of any single type", which will then cause cty to attempt to unify all of the element types when given a tuple.

func TimeAdd

func TimeAdd(timestamp cty.Value, duration cty.Value) (cty.Value, error)

TimeAdd adds a duration to a timestamp, returning a new timestamp.

In the Terraform language, timestamps are conventionally represented as strings using RFC 3339 "Date and Time format" syntax. Timeadd requires the timestamp argument to be a string conforming to this syntax.

`duration` is a string representation of a time difference, consisting of sequences of number and unit pairs, like `"1.5h"` or `1h30m`. The accepted units are `ns`, `us` (or `µs`), `"ms"`, `"s"`, `"m"`, and `"h"`. The first number may be negative to indicate a negative duration, like `"-2h5m"`.

The result is a string, also in RFC 3339 format, representing the result of adding the given direction to the given timestamp.

func Timestamp

func Timestamp() (cty.Value, error)

Timestamp returns a string representation of a static timestamp.

In the Terraform language, timestamps are conventionally represented as strings using RFC 3339 "Date and Time format" syntax, and so timestamp returns a string in this format.

Types

This section is empty.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL