diff --git a/hcl2template/function/datetime.go b/hcl2template/function/datetime.go index 066db18db39..a2b0cc9d17c 100644 --- a/hcl2template/function/datetime.go +++ b/hcl2template/function/datetime.go @@ -12,19 +12,20 @@ import ( "github.com/zclconf/go-cty/cty/function" ) -// InitTime is the UTC time when this package was initialized. It is +// initTime is the UTC time when this package was initialized. It is // used as the timestamp for all configuration templates so that they // match for a single build. -var InitTime time.Time +var initTime time.Time func init() { - InitTime = time.Now().UTC() + initTime = time.Now().UTC() } // TimestampFunc constructs a function that returns a string representation of the current date and time. var TimestampFunc = function.New(&function.Spec{ - Params: []function.Parameter{}, - Type: function.StaticReturnType(cty.String), + Params: []function.Parameter{}, + Type: function.StaticReturnType(cty.String), + RefineResult: refineNotNull, Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) { return cty.StringVal(time.Now().UTC().Format(time.RFC3339)), nil }, @@ -40,42 +41,46 @@ func Timestamp() (cty.Value, error) { } // LegacyIsotimeFunc constructs a function that returns a string representation -// of the current date and time using golang's datetime formatting. +// of the current date and time using the Go language datetime formatting syntax. var LegacyIsotimeFunc = function.New(&function.Spec{ Params: []function.Parameter{}, VarParam: &function.Parameter{ Name: "format", Type: cty.String, }, - Type: function.StaticReturnType(cty.String), + Type: function.StaticReturnType(cty.String), + RefineResult: refineNotNull, Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) { if len(args) > 1 { return cty.StringVal(""), fmt.Errorf("too many values, 1 needed: %v", args) - } else if len(args) == 0 { - return cty.StringVal(InitTime.Format(time.RFC3339)), nil + } + if len(args) == 0 { + return cty.StringVal(initTime.Format(time.RFC3339)), nil } format := args[0].AsString() - return cty.StringVal(InitTime.Format(format)), nil + return cty.StringVal(initTime.Format(format)), nil }, }) // LegacyStrftimeFunc constructs a function that returns a string representation -// of the current date and time using golang's strftime datetime formatting. +// of the current date and time using the Go language strftime datetime formatting syntax. var LegacyStrftimeFunc = function.New(&function.Spec{ Params: []function.Parameter{}, VarParam: &function.Parameter{ Name: "format", Type: cty.String, }, - Type: function.StaticReturnType(cty.String), + Type: function.StaticReturnType(cty.String), + RefineResult: refineNotNull, Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) { if len(args) > 1 { return cty.StringVal(""), fmt.Errorf("too many values, 1 needed: %v", args) - } else if len(args) == 0 { - return cty.StringVal(InitTime.Format(time.RFC3339)), nil + } + if len(args) == 0 { + return cty.StringVal(initTime.Format(time.RFC3339)), nil } format := args[0].AsString() - return cty.StringVal(strftime.Format(format, InitTime)), nil + return cty.StringVal(strftime.Format(format, initTime)), nil }, }) diff --git a/hcl2template/function/env.go b/hcl2template/function/env.go index 34c09a8b921..b6c36e68494 100644 --- a/hcl2template/function/env.go +++ b/hcl2template/function/env.go @@ -21,7 +21,8 @@ var EnvFunc = function.New(&function.Spec{ AllowUnknown: false, }, }, - Type: function.StaticReturnType(cty.String), + Type: function.StaticReturnType(cty.String), + RefineResult: refineNotNull, Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) { key := args[0].AsString() value := os.Getenv(key) diff --git a/hcl2template/function/index.go b/hcl2template/function/index.go index cb4f0c51921..8aade576ded 100644 --- a/hcl2template/function/index.go +++ b/hcl2template/function/index.go @@ -23,7 +23,8 @@ var IndexFunc = function.New(&function.Spec{ Type: cty.DynamicPseudoType, }, }, - Type: function.StaticReturnType(cty.Number), + Type: function.StaticReturnType(cty.Number), + RefineResult: refineNotNull, Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) { if !(args[0].Type().IsListType() || args[0].Type().IsTupleType()) { return cty.NilVal, errors.New("argument must be a list or tuple") diff --git a/hcl2template/function/length.go b/hcl2template/function/length.go index 23b5969af10..f2b83dad925 100644 --- a/hcl2template/function/length.go +++ b/hcl2template/function/length.go @@ -29,6 +29,7 @@ var LengthFunc = function.New(&function.Spec{ return cty.Number, errors.New("argument must be a string, a collection type, or a structural type") } }, + RefineResult: refineNotNull, Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) { coll := args[0] collTy := args[0].Type() diff --git a/hcl2template/function/length_test.go b/hcl2template/function/length_test.go index 0a68c126c51..9bae3886f74 100644 --- a/hcl2template/function/length_test.go +++ b/hcl2template/function/length_test.go @@ -5,6 +5,7 @@ package function import ( "fmt" + "math" "testing" "github.com/zclconf/go-cty/cty" @@ -69,11 +70,15 @@ func TestLength(t *testing.T) { }, { cty.UnknownVal(cty.List(cty.Bool)), - cty.UnknownVal(cty.Number), + cty.UnknownVal(cty.Number).Refine(). + NotNull(). + NumberRangeLowerBound(cty.Zero, true). + NumberRangeUpperBound(cty.NumberIntVal(math.MaxInt), true). + NewValue(), }, { cty.DynamicVal, - cty.UnknownVal(cty.Number), + cty.UnknownVal(cty.Number).RefineNotNull(), }, { cty.StringVal("hello"), @@ -118,11 +123,10 @@ func TestLength(t *testing.T) { }, { cty.UnknownVal(cty.String), - cty.UnknownVal(cty.Number), - }, - { - cty.DynamicVal, - cty.UnknownVal(cty.Number), + cty.UnknownVal(cty.Number).Refine(). + NotNull(). + NumberRangeLowerBound(cty.Zero, true). + NewValue(), }, } diff --git a/hcl2template/function/refinements.go b/hcl2template/function/refinements.go new file mode 100644 index 00000000000..99314d7ee48 --- /dev/null +++ b/hcl2template/function/refinements.go @@ -0,0 +1,9 @@ +package function + +import ( + "github.com/zclconf/go-cty/cty" +) + +func refineNotNull(b *cty.RefinementBuilder) *cty.RefinementBuilder { + return b.NotNull() +}