mirror of
https://github.com/opentofu/opentofu.git
synced 2024-12-23 07:33:32 -06:00
Fix function refs in variable validation (#2052)
Signed-off-by: Christian Mesh <christianmesh1@gmail.com>
This commit is contained in:
parent
d36220e44c
commit
7cacb9f066
@ -41,6 +41,11 @@ issues:
|
||||
linters:
|
||||
- funlen
|
||||
- dupl
|
||||
- revive
|
||||
- path: (.+)_test.go
|
||||
text: "ST1003"
|
||||
- path: (.+)_test.go
|
||||
text: "var-naming: don't use underscores in Go names"
|
||||
|
||||
linters:
|
||||
disable-all: true
|
||||
|
@ -2487,3 +2487,84 @@ locals {
|
||||
t.Fatalf("expected deprecated warning, got: %q\n", warn)
|
||||
}
|
||||
}
|
||||
|
||||
func TextContext2Validate_providerFunctions(t *testing.T) {
|
||||
p := testProvider("aws")
|
||||
p.GetProviderSchemaResponse = &providers.GetProviderSchemaResponse{
|
||||
Functions: map[string]providers.FunctionSpec{
|
||||
"arn_parse": providers.FunctionSpec{
|
||||
Parameters: []providers.FunctionParameterSpec{{
|
||||
Name: "arn",
|
||||
Type: cty.String,
|
||||
}},
|
||||
Return: cty.Bool,
|
||||
},
|
||||
},
|
||||
}
|
||||
p.CallFunctionResponse = &providers.CallFunctionResponse{
|
||||
Result: cty.True,
|
||||
}
|
||||
m := testModuleInline(t, map[string]string{
|
||||
"main.tf": `
|
||||
terraform {
|
||||
required_providers {
|
||||
aws = ">=5.70.0"
|
||||
}
|
||||
}
|
||||
|
||||
provider "aws" {
|
||||
region="us-east-1"
|
||||
}
|
||||
|
||||
module "mod" {
|
||||
source = "./mod"
|
||||
providers = {
|
||||
aws = aws
|
||||
}
|
||||
}
|
||||
`,
|
||||
"mod/mod.tf": `
|
||||
terraform {
|
||||
required_providers {
|
||||
aws = ">=5.70.0"
|
||||
}
|
||||
}
|
||||
|
||||
variable "obfmod" {
|
||||
type = object({
|
||||
arns = optional(list(string))
|
||||
})
|
||||
description = "Configuration for xxx."
|
||||
|
||||
validation {
|
||||
condition = alltrue([
|
||||
for arn in var.obfmod.arns: can(provider::aws::arn_parse(arn))
|
||||
])
|
||||
error_message = "All arns MUST BE a valid AWS ARN format."
|
||||
}
|
||||
|
||||
default = {
|
||||
arns = [
|
||||
"arn:partition:service:region:account-id:resource-id",
|
||||
]
|
||||
}
|
||||
}
|
||||
`,
|
||||
})
|
||||
|
||||
ctx := testContext2(t, &ContextOpts{
|
||||
Providers: map[addrs.Provider]providers.Factory{
|
||||
addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
|
||||
},
|
||||
})
|
||||
|
||||
diags := ctx.Validate(m)
|
||||
warn := diags.ErrWithWarnings().Error()
|
||||
if !strings.Contains(warn, `The attribute "foo" is deprecated`) {
|
||||
t.Fatalf("expected deprecated warning, got: %q\n", warn)
|
||||
}
|
||||
|
||||
if !p.CallFunctionCalled {
|
||||
t.Fatalf("Expected function call")
|
||||
}
|
||||
}
|
||||
|
@ -91,16 +91,27 @@ func (n *nodeExpandModuleVariable) ModulePath() addrs.Module {
|
||||
|
||||
// GraphNodeReferencer
|
||||
func (n *nodeExpandModuleVariable) References() []*addrs.Reference {
|
||||
var refs []*addrs.Reference
|
||||
if n.Config != nil {
|
||||
// These references will ignore GraphNodeReferenceOutside and are used by the ProviderFunctionTransformer and lang.Scope.evalContext
|
||||
// It's an odd pattern, but it works
|
||||
for _, validation := range n.Config.Validations {
|
||||
condFuncs, _ := lang.ProviderFunctionsInExpr(addrs.ParseRef, validation.Condition)
|
||||
refs = append(refs, condFuncs...)
|
||||
errFuncs, _ := lang.ProviderFunctionsInExpr(addrs.ParseRef, validation.ErrorMessage)
|
||||
refs = append(refs, errFuncs...)
|
||||
}
|
||||
}
|
||||
|
||||
// If we have no value expression, we cannot depend on anything.
|
||||
if n.Expr == nil {
|
||||
return nil
|
||||
return refs
|
||||
}
|
||||
|
||||
// Variables in the root don't depend on anything, because their values
|
||||
// are gathered prior to the graph walk and recorded in the context.
|
||||
if len(n.Module) == 0 {
|
||||
return nil
|
||||
return refs
|
||||
}
|
||||
|
||||
// Otherwise, we depend on anything referenced by our value expression.
|
||||
@ -113,7 +124,9 @@ func (n *nodeExpandModuleVariable) References() []*addrs.Reference {
|
||||
// where our associated variable was declared, which is correct because
|
||||
// our value expression is assigned within a "module" block in the parent
|
||||
// module.
|
||||
refs, _ := lang.ReferencesInExpr(addrs.ParseRef, n.Expr)
|
||||
outerRefs, _ := lang.ReferencesInExpr(addrs.ParseRef, n.Expr)
|
||||
refs = append(refs, outerRefs...)
|
||||
|
||||
return refs
|
||||
}
|
||||
|
||||
@ -167,23 +180,6 @@ func (n *nodeModuleVariable) ModulePath() addrs.Module {
|
||||
return n.Addr.Module.Module()
|
||||
}
|
||||
|
||||
// GraphNodeReferencer
|
||||
func (n *nodeModuleVariable) References() []*addrs.Reference {
|
||||
// This is identical to NodeRootVariable.References
|
||||
var refs []*addrs.Reference
|
||||
|
||||
if n.Config != nil {
|
||||
for _, validation := range n.Config.Validations {
|
||||
condFuncs, _ := lang.ProviderFunctionsInExpr(addrs.ParseRef, validation.Condition)
|
||||
refs = append(refs, condFuncs...)
|
||||
errFuncs, _ := lang.ProviderFunctionsInExpr(addrs.ParseRef, validation.ErrorMessage)
|
||||
refs = append(refs, errFuncs...)
|
||||
}
|
||||
}
|
||||
|
||||
return refs
|
||||
}
|
||||
|
||||
// GraphNodeExecutable
|
||||
func (n *nodeModuleVariable) Execute(ctx EvalContext, op walkOperation) (diags tfdiags.Diagnostics) {
|
||||
log.Printf("[TRACE] nodeModuleVariable: evaluating %s", n.Addr)
|
||||
|
Loading…
Reference in New Issue
Block a user