diff --git a/.golangci.yml b/.golangci.yml index 78574a003e..49258d633e 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -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 diff --git a/internal/tofu/context_validate_test.go b/internal/tofu/context_validate_test.go index 0bdaf02e32..d882988d07 100644 --- a/internal/tofu/context_validate_test.go +++ b/internal/tofu/context_validate_test.go @@ -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") + } +} diff --git a/internal/tofu/node_module_variable.go b/internal/tofu/node_module_variable.go index 77022faa2b..4ce4a5fedf 100644 --- a/internal/tofu/node_module_variable.go +++ b/internal/tofu/node_module_variable.go @@ -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)