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
|
||||
// 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
|
||||
|
@ -582,6 +620,12 @@ func Length(collection cty.Value) (cty.Value, error) {
|
|||
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.
|
||||
func Coalesce(args ...cty.Value) (cty.Value, error) {
|
||||
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) {
|
||||
tests := []struct {
|
||||
Values []cty.Value
|
||||
|
|
|
@ -33,6 +33,7 @@ func (s *Scope) Functions() map[string]function.Function {
|
|||
s.funcs = map[string]function.Function{
|
||||
"abs": stdlib.AbsoluteFunc,
|
||||
"abspath": funcs.AbsPathFunc,
|
||||
"alltrue": funcs.AllTrueFunc,
|
||||
"basename": funcs.BasenameFunc,
|
||||
"base64decode": funcs.Base64DecodeFunc,
|
||||
"base64encode": funcs.Base64EncodeFunc,
|
||||
|
|
|
@ -67,6 +67,13 @@ func TestFunctions(t *testing.T) {
|
|||
},
|
||||
},
|
||||
|
||||
"alltrue": {
|
||||
{
|
||||
`alltrue([true])`,
|
||||
cty.True,
|
||||
},
|
||||
},
|
||||
|
||||
"base64decode": {
|
||||
{
|
||||
`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>
|
||||
<ul class="nav">
|
||||
|
||||
<li>
|
||||
<a href="/docs/configuration/functions/alltrue.html">alltrue</a>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<a href="/docs/configuration/functions/chunklist.html">chunklist</a>
|
||||
</li>
|
||||
|
|
Loading…
Reference in New Issue