Skip to content

Commit

Permalink
Adding AtLeastSumOf, AtMostSumOf, EqualToSumOf Validators (#29)
Browse files Browse the repository at this point in the history
* Adding atLeastSumOf, atMostSumOf and equalToSumOf int64 validators (#20)

* Switching to using path expressions

* Do not validate if any attributes are unknown (#20)

* Updating dependencies, including terraform-plugin-framework@0.10.0

* PR review: making use of the new `path.Expression` `.MergeExpressions` method

* Preparing CHANGELOG entry

* Rely on 'tfsdk.ValueAs' to do type validation

Co-authored-by: Ivan De Marino <ivan.demarino@hashicorp.com>
Co-authored-by: Brian Flad <bflad417@gmail.com>
  • Loading branch information
3 people authored Jul 20, 2022
1 parent 81783a7 commit 3ef7858
Show file tree
Hide file tree
Showing 11 changed files with 953 additions and 36 deletions.
3 changes: 3 additions & 0 deletions .changelog/29.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
```release-note:enhancement
int64validator: Added `AtLeastSumOf()`, `AtMostSumOf()` and `EqualToSumOf()` validation functions
```
14 changes: 7 additions & 7 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -4,22 +4,22 @@ go 1.17

require (
github.com/google/go-cmp v0.5.8
github.com/hashicorp/terraform-plugin-framework v0.9.1-0.20220711170800-89baaa204707
github.com/hashicorp/terraform-plugin-go v0.11.0
github.com/hashicorp/terraform-plugin-framework v0.10.0
github.com/hashicorp/terraform-plugin-go v0.12.0
)

require (
github.com/fatih/color v1.13.0 // indirect
github.com/golang/protobuf v1.5.2 // indirect
github.com/hashicorp/go-hclog v1.2.1 // indirect
github.com/hashicorp/terraform-plugin-log v0.4.1 // indirect
github.com/hashicorp/terraform-plugin-log v0.6.0 // indirect
github.com/mattn/go-colorable v0.1.12 // indirect
github.com/mattn/go-isatty v0.0.14 // indirect
github.com/mitchellh/go-testing-interface v1.14.1 // indirect
github.com/vmihailenco/msgpack/v4 v4.3.12 // indirect
github.com/vmihailenco/tagparser v0.1.1 // indirect
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2 // indirect
golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6 // indirect
google.golang.org/appengine v1.6.5 // indirect
github.com/vmihailenco/tagparser v0.1.2 // indirect
golang.org/x/net v0.0.0-20220708220712-1185a9018129 // indirect
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8 // indirect
google.golang.org/appengine v1.6.7 // indirect
google.golang.org/protobuf v1.28.0 // indirect
)
22 changes: 15 additions & 7 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -62,12 +62,12 @@ github.com/hashicorp/go-hclog v1.2.1/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVH
github.com/hashicorp/go-plugin v1.4.4/go.mod h1:viDMjcLJuDui6pXb8U4HVfb8AamCWhHGUjr2IrTF67s=
github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
github.com/hashicorp/terraform-plugin-framework v0.9.1-0.20220711170800-89baaa204707 h1:4wAdubsgZ/eAbCjW2At8w6UDeVCel76jWDHsWvbc8Pk=
github.com/hashicorp/terraform-plugin-framework v0.9.1-0.20220711170800-89baaa204707/go.mod h1:+H4ieVu7X4bfYlLB/zytek48e4CjcG+gjKdVOjVY1PU=
github.com/hashicorp/terraform-plugin-go v0.11.0 h1:YXsvSCx7GbQO5jIUQd77FesqmIBxgSvYAtAX1NqErTk=
github.com/hashicorp/terraform-plugin-go v0.11.0/go.mod h1:aphXBG8qtQH0yF1waMRlaw/3G+ZFlR/6Artnvt1QEDE=
github.com/hashicorp/terraform-plugin-log v0.4.1 h1:xpbmVhvuU3mgHzLetOmx9pkOL2rmgpu302XxddON6eo=
github.com/hashicorp/terraform-plugin-log v0.4.1/go.mod h1:p4R1jWBXRTvL4odmEkFfDdhUjHf9zcs/BCoNHAc7IK4=
github.com/hashicorp/terraform-plugin-framework v0.10.0 h1:LGYcnvNdVaZA1ZHe53BHLVjaaGs7HTiq6+9Js29stL4=
github.com/hashicorp/terraform-plugin-framework v0.10.0/go.mod h1:CK7Opzukfu/2CPJs+HzUdfHrFlp+ZIQeSxjF0x8k464=
github.com/hashicorp/terraform-plugin-go v0.12.0 h1:6wW9mT1dSs0Xq4LR6HXj1heQ5ovr5GxXNJwkErZzpJw=
github.com/hashicorp/terraform-plugin-go v0.12.0/go.mod h1:kwhmaWHNDvT1B3QiSJdAtrB/D4RaKSY/v3r2BuoWK4M=
github.com/hashicorp/terraform-plugin-log v0.6.0 h1:/Vq78uSIdUSZ3iqDc9PESKtwt8YqNKN6u+khD+lLjuw=
github.com/hashicorp/terraform-plugin-log v0.6.0/go.mod h1:p4R1jWBXRTvL4odmEkFfDdhUjHf9zcs/BCoNHAc7IK4=
github.com/hashicorp/terraform-registry-address v0.0.0-20220623143253-7d51757b572c/go.mod h1:Wn3Na71knbXc1G8Lh+yu/dQWWJeFQEpDeJMtWMtlmNI=
github.com/hashicorp/terraform-svchost v0.0.0-20200729002733-f050f53b9734/go.mod h1:kNDNcF7sN4DocDLBkQYz73HGKwN1ANB1blq4lIYLYvg=
github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM=
Expand Down Expand Up @@ -110,6 +110,8 @@ github.com/vmihailenco/msgpack/v4 v4.3.12 h1:07s4sz9IReOgdikxLTKNbBdqDMLsjPKXwvC
github.com/vmihailenco/msgpack/v4 v4.3.12/go.mod h1:gborTTJjAo/GWTqqRjrLCn9pgNN+NXzzngzBKDPIqw4=
github.com/vmihailenco/tagparser v0.1.1 h1:quXMXlA39OCbd2wAdTsGDlK9RkOk6Wuw+x37wVyIuWY=
github.com/vmihailenco/tagparser v0.1.1/go.mod h1:OeAg3pn3UbLjkWt+rN9oFYB6u/cQgqMEUPoW2WPyhdI=
github.com/vmihailenco/tagparser v0.1.2 h1:gnjoVuB/kljJ5wICEEOpx98oXMWPLj22G67Vbd1qPqc=
github.com/vmihailenco/tagparser v0.1.2/go.mod h1:OeAg3pn3UbLjkWt+rN9oFYB6u/cQgqMEUPoW2WPyhdI=
github.com/zclconf/go-cty v1.1.0/go.mod h1:xnAOWiHeOqg2nWS62VtQ7pbOu17FtxJNW8RLEih+O3s=
go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
Expand All @@ -134,6 +136,8 @@ golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwY
golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2 h1:CIJ76btIcR3eFI5EgSo6k1qKw9KJexJuRLI9G7Hp5wE=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220708220712-1185a9018129 h1:vucSRfWwTsoXro7P+3Cjlr6flUMtzCwzlvkxEQtHHB0=
golang.org/x/net v0.0.0-20220708220712-1185a9018129/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
Expand All @@ -157,6 +161,8 @@ golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6 h1:nonptSpoQ4vQjyraW20DXPAglgQfVnM9ZC6MmNLMR60=
golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8 h1:0A+M6Uqn+Eje4kHMK80dtF3JCXC4ykBgQG4Fe06QRhQ=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
Expand All @@ -174,6 +180,8 @@ google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9Ywl
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.6.5 h1:tycE03LOZYQNhDpS27tcQdAzLCVMaj7QT2SXxebnpCM=
google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c=
google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/genproto v0.0.0-20170818010345-ee236bd376b0/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
Expand All @@ -187,7 +195,7 @@ google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8
google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0=
google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
google.golang.org/grpc v1.47.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk=
google.golang.org/grpc v1.48.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk=
google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.2.0/go.mod h1:DNq5QpG7LJqD2AamLZ7zvKE0DEpVl2BSEVjFycAAjRY=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
Expand Down
115 changes: 115 additions & 0 deletions int64validator/at_least_sum_of.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
package int64validator

import (
"context"
"fmt"
"strings"

"github.com/hashicorp/terraform-plugin-framework/attr"
"github.com/hashicorp/terraform-plugin-framework/path"
"github.com/hashicorp/terraform-plugin-framework/tfsdk"
"github.com/hashicorp/terraform-plugin-framework/types"

"github.com/hashicorp/terraform-plugin-framework-validators/helpers/validatordiag"
)

var _ tfsdk.AttributeValidator = atLeastSumOfValidator{}

// atLeastSumOfValidator validates that an integer Attribute's value is at least the sum of one
// or more integer Attributes retrieved via the given path expressions.
type atLeastSumOfValidator struct {
attributesToSumPathExpressions path.Expressions
}

// Description describes the validation in plain text formatting.
func (av atLeastSumOfValidator) Description(_ context.Context) string {
var attributePaths []string
for _, p := range av.attributesToSumPathExpressions {
attributePaths = append(attributePaths, p.String())
}

return fmt.Sprintf("value must be at least sum of %s", strings.Join(attributePaths, " + "))
}

// MarkdownDescription describes the validation in Markdown formatting.
func (av atLeastSumOfValidator) MarkdownDescription(ctx context.Context) string {
return av.Description(ctx)
}

// Validate performs the validation.
func (av atLeastSumOfValidator) Validate(ctx context.Context, request tfsdk.ValidateAttributeRequest, response *tfsdk.ValidateAttributeResponse) {
i, ok := validateInt(ctx, request, response)
if !ok {
return
}

// Ensure input path expressions resolution against the current attribute
expressions := request.AttributePathExpression.MergeExpressions(av.attributesToSumPathExpressions...)

// Sum the value of all the attributes involved, but only if they are all known.
var sumOfAttribs int64
for _, expression := range expressions {
matchedPaths, diags := request.Config.PathMatches(ctx, expression)
response.Diagnostics.Append(diags...)

// Collect all errors
if diags.HasError() {
continue
}

for _, mp := range matchedPaths {
// If the user specifies the same attribute this validator is applied to,
// also as part of the input, skip it
if mp.Equal(request.AttributePath) {
continue
}

// Get the value
var matchedValue attr.Value
diags := request.Config.GetAttribute(ctx, mp, &matchedValue)
response.Diagnostics.Append(diags...)
if diags.HasError() {
continue
}

if matchedValue.IsUnknown() {
return
}

if matchedValue.IsNull() {
continue
}

// We know there is a value, convert it to the expected type
var attribToSum types.Int64
diags = tfsdk.ValueAs(ctx, matchedValue, &attribToSum)
response.Diagnostics.Append(diags...)
if diags.HasError() {
continue
}

sumOfAttribs += attribToSum.Value
}
}

if i < sumOfAttribs {
response.Diagnostics.Append(validatordiag.InvalidAttributeValueDiagnostic(
request.AttributePath,
av.Description(ctx),
fmt.Sprintf("%d", i),
))

return
}
}

// AtLeastSumOf returns an AttributeValidator which ensures that any configured
// attribute value:
//
// - Is a number, which can be represented by a 64-bit integer.
// - Is at least the sum of the attributes retrieved via the given path expression(s).
//
// Null (unconfigured) and unknown (known after apply) values are skipped.
func AtLeastSumOf(attributesToSumPathExpressions ...path.Expression) tfsdk.AttributeValidator {
return atLeastSumOfValidator{attributesToSumPathExpressions}
}
181 changes: 181 additions & 0 deletions int64validator/at_least_sum_of_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,181 @@
package int64validator

import (
"context"
"testing"

"github.com/hashicorp/terraform-plugin-framework/attr"
"github.com/hashicorp/terraform-plugin-framework/path"
"github.com/hashicorp/terraform-plugin-framework/tfsdk"
"github.com/hashicorp/terraform-plugin-framework/types"
"github.com/hashicorp/terraform-plugin-go/tftypes"
)

func TestAtLeastSumOfValidator(t *testing.T) {
t.Parallel()

type testCase struct {
val attr.Value
attributesToSumExpressions path.Expressions
requestConfigRaw map[string]tftypes.Value
expectError bool
}
tests := map[string]testCase{
"not an Int64": {
val: types.Bool{Value: true},
expectError: true,
},
"unknown Int64": {
val: types.Int64{Unknown: true},
},
"null Int64": {
val: types.Int64{Null: true},
},
"valid integer as Int64 less than sum of attributes": {
val: types.Int64{Value: 10},
attributesToSumExpressions: path.Expressions{
path.MatchRoot("one"),
path.MatchRoot("two"),
},
requestConfigRaw: map[string]tftypes.Value{
"one": tftypes.NewValue(tftypes.Number, 15),
"two": tftypes.NewValue(tftypes.Number, 15),
},
expectError: true,
},
"valid integer as Int64 equal to sum of attributes": {
val: types.Int64{Value: 10},
attributesToSumExpressions: path.Expressions{
path.MatchRoot("one"),
path.MatchRoot("two"),
},
requestConfigRaw: map[string]tftypes.Value{
"one": tftypes.NewValue(tftypes.Number, 5),
"two": tftypes.NewValue(tftypes.Number, 5),
},
},
"valid integer as Int64 greater than sum of attributes": {
val: types.Int64{Value: 10},
attributesToSumExpressions: path.Expressions{
path.MatchRoot("one"),
path.MatchRoot("two"),
},
requestConfigRaw: map[string]tftypes.Value{
"one": tftypes.NewValue(tftypes.Number, 4),
"two": tftypes.NewValue(tftypes.Number, 4),
},
},
"valid integer as Int64 greater than sum of attributes, when one summed attribute is null": {
val: types.Int64{Value: 10},
attributesToSumExpressions: path.Expressions{
path.MatchRoot("one"),
path.MatchRoot("two"),
},
requestConfigRaw: map[string]tftypes.Value{
"one": tftypes.NewValue(tftypes.Number, nil),
"two": tftypes.NewValue(tftypes.Number, 9),
},
},
"valid integer as Int64 does not return error when all attributes are null": {
val: types.Int64{Null: true},
attributesToSumExpressions: path.Expressions{
path.MatchRoot("one"),
path.MatchRoot("two"),
},
requestConfigRaw: map[string]tftypes.Value{
"one": tftypes.NewValue(tftypes.Number, nil),
"two": tftypes.NewValue(tftypes.Number, nil),
},
},
"valid integer as Int64 returns error when all attributes to sum are null": {
val: types.Int64{Value: -1},
attributesToSumExpressions: path.Expressions{
path.MatchRoot("one"),
path.MatchRoot("two"),
},
requestConfigRaw: map[string]tftypes.Value{
"one": tftypes.NewValue(tftypes.Number, nil),
"two": tftypes.NewValue(tftypes.Number, nil),
},
expectError: true,
},
"valid integer as Int64 greater than sum of attributes, when one summed attribute is unknown": {
val: types.Int64{Value: 10},
attributesToSumExpressions: path.Expressions{
path.MatchRoot("one"),
path.MatchRoot("two"),
},
requestConfigRaw: map[string]tftypes.Value{
"one": tftypes.NewValue(tftypes.Number, tftypes.UnknownValue),
"two": tftypes.NewValue(tftypes.Number, 9),
},
},
"valid integer as Int64 does not return error when all attributes are unknown": {
val: types.Int64{Unknown: true},
attributesToSumExpressions: path.Expressions{
path.MatchRoot("one"),
path.MatchRoot("two"),
},
requestConfigRaw: map[string]tftypes.Value{
"one": tftypes.NewValue(tftypes.Number, tftypes.UnknownValue),
"two": tftypes.NewValue(tftypes.Number, tftypes.UnknownValue),
},
},
"valid integer as Int64 does not return error when all attributes to sum are unknown": {
val: types.Int64{Value: -1},
attributesToSumExpressions: path.Expressions{
path.MatchRoot("one"),
path.MatchRoot("two"),
},
requestConfigRaw: map[string]tftypes.Value{
"one": tftypes.NewValue(tftypes.Number, tftypes.UnknownValue),
"two": tftypes.NewValue(tftypes.Number, tftypes.UnknownValue),
},
},
"error when attribute to sum is not Number": {
val: types.Int64{Value: 9},
attributesToSumExpressions: path.Expressions{
path.MatchRoot("one"),
path.MatchRoot("two"),
},
requestConfigRaw: map[string]tftypes.Value{
"one": tftypes.NewValue(tftypes.Bool, true),
"two": tftypes.NewValue(tftypes.Number, 9),
},
expectError: true,
},
}

for name, test := range tests {
name, test := name, test
t.Run(name, func(t *testing.T) {
request := tfsdk.ValidateAttributeRequest{
AttributePath: path.Root("test"),
AttributePathExpression: path.MatchRoot("test"),
AttributeConfig: test.val,
Config: tfsdk.Config{
Raw: tftypes.NewValue(tftypes.Object{}, test.requestConfigRaw),
Schema: tfsdk.Schema{
Attributes: map[string]tfsdk.Attribute{
"test": {Type: types.Int64Type},
"one": {Type: types.Int64Type},
"two": {Type: types.Int64Type},
},
},
},
}

response := tfsdk.ValidateAttributeResponse{}

AtLeastSumOf(test.attributesToSumExpressions...).Validate(context.Background(), request, &response)

if !response.Diagnostics.HasError() && test.expectError {
t.Fatal("expected error, got no error")
}

if response.Diagnostics.HasError() && !test.expectError {
t.Fatalf("got unexpected error: %s", response.Diagnostics)
}
})
}
}
Loading

0 comments on commit 3ef7858

Please sign in to comment.