lang/funcs: Add "alltrue" function (#25656)
This commit adds an `alltrue` function to Terraform configuration. A reason we might want this function is because it will enable more powerful custom variable validations. For example: ```hcl variable "amis" { type = list(object({ id = string })) validation { condition = (alltrue([ for a in var.amis : length(a.id) > 4 && substr(a.id, 0, 4) == "ami-" ])) error_message = "The ID of at least one AMI was invalid." } } ```
This commit is contained in:
parent
7222bad59c
commit
6ed47c7241
|
@ -56,6 +56,44 @@ var LengthFunc = function.New(&function.Spec{
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// AllTrueFunc constructs a function that returns true if all elements of the
|
||||||
|
// collection are true or "true". If the collection is empty, return true.
|
||||||
|
var AllTrueFunc = function.New(&function.Spec{
|
||||||
|
Params: []function.Parameter{
|
||||||
|
{
|
||||||
|
Name: "collection",
|
||||||
|
Type: cty.DynamicPseudoType,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Type: function.StaticReturnType(cty.Bool),
|
||||||
|
Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) {
|
||||||
|
ty := args[0].Type()
|
||||||
|
if !ty.IsListType() && !ty.IsTupleType() && !ty.IsSetType() {
|
||||||
|
return cty.NilVal, errors.New("argument must be list, tuple, or set")
|
||||||
|
}
|
||||||
|
|
||||||
|
tobool := MakeToFunc(cty.Bool)
|
||||||
|
for it := args[0].ElementIterator(); it.Next(); {
|
||||||
|
_, v := it.Element()
|
||||||
|
if !v.IsKnown() {
|
||||||
|
return cty.UnknownVal(cty.Bool), nil
|
||||||
|
}
|
||||||
|
got, err := tobool.Call([]cty.Value{v})
|
||||||
|
if err != nil {
|
||||||
|
return cty.False, nil
|
||||||
|
}
|
||||||
|
eq, err := stdlib.Equal(got, cty.True)
|
||||||
|
if err != nil {
|
||||||
|
return cty.NilVal, err
|
||||||
|
}
|
||||||
|
if eq.False() {
|
||||||
|
return cty.False, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return cty.True, nil
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
// CoalesceFunc constructs a function that takes any number of arguments and
|
// 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
|
// 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
|
// stdlib and modified so that it returns the first *non-empty* non-null element
|
||||||
|
@ -582,6 +620,12 @@ func Length(collection cty.Value) (cty.Value, error) {
|
||||||
return LengthFunc.Call([]cty.Value{collection})
|
return LengthFunc.Call([]cty.Value{collection})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// AllTrue returns true if all elements of the collection are true or "true".
|
||||||
|
// If the collection is empty, return true.
|
||||||
|
func AllTrue(collection cty.Value) (cty.Value, error) {
|
||||||
|
return AllTrueFunc.Call([]cty.Value{collection})
|
||||||
|
}
|
||||||
|
|
||||||
// Coalesce takes any number of arguments and returns the first one that isn't empty.
|
// Coalesce takes any number of arguments and returns the first one that isn't empty.
|
||||||
func Coalesce(args ...cty.Value) (cty.Value, error) {
|
func Coalesce(args ...cty.Value) (cty.Value, error) {
|
||||||
return CoalesceFunc.Call(args)
|
return CoalesceFunc.Call(args)
|
||||||
|
|
|
@ -139,6 +139,99 @@ func TestLength(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestAllTrue(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
Collection cty.Value
|
||||||
|
Want cty.Value
|
||||||
|
Err bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
cty.ListValEmpty(cty.String),
|
||||||
|
cty.True,
|
||||||
|
false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
cty.TupleVal([]cty.Value{}),
|
||||||
|
cty.True,
|
||||||
|
false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
cty.SetValEmpty(cty.Bool),
|
||||||
|
cty.True,
|
||||||
|
false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
cty.ListVal([]cty.Value{cty.True}),
|
||||||
|
cty.True,
|
||||||
|
false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
cty.ListVal([]cty.Value{cty.StringVal("true")}),
|
||||||
|
cty.True,
|
||||||
|
false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
cty.TupleVal([]cty.Value{cty.True, cty.StringVal("true")}),
|
||||||
|
cty.True,
|
||||||
|
false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
cty.ListVal([]cty.Value{cty.False}),
|
||||||
|
cty.False,
|
||||||
|
false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
cty.ListVal([]cty.Value{cty.True, cty.False}),
|
||||||
|
cty.False,
|
||||||
|
false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
cty.ListVal([]cty.Value{cty.False, cty.True}),
|
||||||
|
cty.False,
|
||||||
|
false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
cty.ListVal([]cty.Value{cty.NumberIntVal(1)}),
|
||||||
|
cty.False,
|
||||||
|
false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
cty.StringVal("true"),
|
||||||
|
cty.False,
|
||||||
|
true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
cty.ListVal([]cty.Value{cty.ListValEmpty(cty.String)}),
|
||||||
|
cty.False,
|
||||||
|
false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
cty.ListVal([]cty.Value{cty.UnknownVal(cty.String)}),
|
||||||
|
cty.UnknownVal(cty.Bool),
|
||||||
|
false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range tests {
|
||||||
|
t.Run(fmt.Sprintf("alltrue(%#v)", test.Collection), func(t *testing.T) {
|
||||||
|
got, err := AllTrue(test.Collection)
|
||||||
|
|
||||||
|
if test.Err {
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal("succeeded; want error")
|
||||||
|
}
|
||||||
|
return
|
||||||
|
} else if err != nil {
|
||||||
|
t.Fatalf("unexpected error: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !got.RawEquals(test.Want) {
|
||||||
|
t.Errorf("wrong result\ngot: %#v\nwant: %#v", got, test.Want)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestCoalesce(t *testing.T) {
|
func TestCoalesce(t *testing.T) {
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
Values []cty.Value
|
Values []cty.Value
|
||||||
|
|
|
@ -33,6 +33,7 @@ func (s *Scope) Functions() map[string]function.Function {
|
||||||
s.funcs = map[string]function.Function{
|
s.funcs = map[string]function.Function{
|
||||||
"abs": stdlib.AbsoluteFunc,
|
"abs": stdlib.AbsoluteFunc,
|
||||||
"abspath": funcs.AbsPathFunc,
|
"abspath": funcs.AbsPathFunc,
|
||||||
|
"alltrue": funcs.AllTrueFunc,
|
||||||
"basename": funcs.BasenameFunc,
|
"basename": funcs.BasenameFunc,
|
||||||
"base64decode": funcs.Base64DecodeFunc,
|
"base64decode": funcs.Base64DecodeFunc,
|
||||||
"base64encode": funcs.Base64EncodeFunc,
|
"base64encode": funcs.Base64EncodeFunc,
|
||||||
|
|
|
@ -67,6 +67,13 @@ func TestFunctions(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
"alltrue": {
|
||||||
|
{
|
||||||
|
`alltrue([true])`,
|
||||||
|
cty.True,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
"base64decode": {
|
"base64decode": {
|
||||||
{
|
{
|
||||||
`base64decode("YWJjMTIzIT8kKiYoKSctPUB+")`,
|
`base64decode("YWJjMTIzIT8kKiYoKSctPUB+")`,
|
||||||
|
|
|
@ -0,0 +1,30 @@
|
||||||
|
---
|
||||||
|
layout: functions
|
||||||
|
page_title: alltrue - Functions - Configuration Language
|
||||||
|
sidebar_current: docs-funcs-collection-alltrue
|
||||||
|
description: |-
|
||||||
|
The alltrue function determines whether all elements of a collection
|
||||||
|
are true or "true". If the collection is empty, it returns true.
|
||||||
|
---
|
||||||
|
|
||||||
|
# `alltrue` Function
|
||||||
|
|
||||||
|
-> **Note:** This page is about Terraform 0.12 and later. For Terraform 0.11 and
|
||||||
|
earlier, see
|
||||||
|
[0.11 Configuration Language: Interpolation Syntax](../../configuration-0-11/interpolation.html).
|
||||||
|
|
||||||
|
`alltrue` returns `true` if all elements in a given collection are `true`
|
||||||
|
or `"true"`. It also returns `true` if the collection is empty.
|
||||||
|
|
||||||
|
```hcl
|
||||||
|
alltrue(list)
|
||||||
|
```
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
```command
|
||||||
|
> alltrue(["true", true])
|
||||||
|
true
|
||||||
|
> alltrue([true, false])
|
||||||
|
false
|
||||||
|
```
|
|
@ -138,6 +138,10 @@
|
||||||
<a href="#docs-funcs-collection">Collection Functions</a>
|
<a href="#docs-funcs-collection">Collection Functions</a>
|
||||||
<ul class="nav">
|
<ul class="nav">
|
||||||
|
|
||||||
|
<li>
|
||||||
|
<a href="/docs/configuration/functions/alltrue.html">alltrue</a>
|
||||||
|
</li>
|
||||||
|
|
||||||
<li>
|
<li>
|
||||||
<a href="/docs/configuration/functions/chunklist.html">chunklist</a>
|
<a href="/docs/configuration/functions/chunklist.html">chunklist</a>
|
||||||
</li>
|
</li>
|
||||||
|
|
Loading…
Reference in New Issue