mirror of
https://github.com/opentffoundation/opentf.git
synced 2025-12-19 17:59:05 -05:00
Extract the provider functions references from the dynamic blocks (#3429)
Signed-off-by: Andrei Ciobanu <andrei.ciobanu@opentofu.org>
This commit is contained in:
@@ -38,6 +38,9 @@ func (s *Scope) ExpandBlock(ctx context.Context, body hcl.Body, schema *configsc
|
||||
spec := schema.DecoderSpec()
|
||||
|
||||
traversals := dynblock.ExpandVariablesHCLDec(body, spec)
|
||||
// using ExpandFunctionsHCLDec to extract strictly the functions that are referenced inside the `dynamic`
|
||||
// block, since that is what is needed to be injected into the expansion evalCtx for the expansion to work
|
||||
traversals = append(traversals, filterProviderFunctions(dynblock.ExpandFunctionsHCLDec(body, spec))...)
|
||||
refs, diags := References(s.ParseRef, traversals)
|
||||
|
||||
hclCtx, ctxDiags := s.EvalContext(ctx, refs)
|
||||
|
||||
@@ -10,7 +10,9 @@ import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"slices"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"testing"
|
||||
@@ -6569,3 +6571,110 @@ func TestContext2Apply_enabledForModule(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestContext2Apply_callingProviderFunctionFromDynamicBlock checks that a
|
||||
// provider function can be used by referencing it in a dynamic block inside
|
||||
// a resource.
|
||||
func TestContext2Apply_callingProviderFunctionFromDynamicBlock(t *testing.T) {
|
||||
m := testModuleInline(t, map[string]string{
|
||||
"main.tf": `
|
||||
terraform {
|
||||
required_providers {
|
||||
test = {
|
||||
source = "example.com/foo/test"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
locals {
|
||||
urls = ["foo:80", "bar:81"]
|
||||
}
|
||||
resource "test_resource" "res" {
|
||||
dynamic "allow" {
|
||||
iterator = item
|
||||
for_each = {
|
||||
for z in local.urls :
|
||||
z => provider::test::extract_port(z) if contains([80, 81], provider::test::extract_port(z))
|
||||
}
|
||||
content {
|
||||
port = item.value
|
||||
}
|
||||
}
|
||||
}
|
||||
`,
|
||||
})
|
||||
|
||||
p := MockProvider{}
|
||||
p.GetProviderSchemaResponse = &providers.GetProviderSchemaResponse{
|
||||
ResourceTypes: map[string]providers.Schema{
|
||||
"test_resource": {
|
||||
Block: &configschema.Block{
|
||||
BlockTypes: map[string]*configschema.NestedBlock{
|
||||
"allow": {
|
||||
Nesting: configschema.NestingList,
|
||||
Block: configschema.Block{
|
||||
Attributes: map[string]*configschema.Attribute{
|
||||
"port": {
|
||||
Type: cty.Number,
|
||||
Optional: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Functions: map[string]providers.FunctionSpec{
|
||||
"extract_port": {
|
||||
Parameters: []providers.FunctionParameterSpec{
|
||||
{
|
||||
Name: "in",
|
||||
Type: cty.String,
|
||||
AllowNullValue: false,
|
||||
AllowUnknownValues: false,
|
||||
},
|
||||
},
|
||||
Return: cty.Number,
|
||||
},
|
||||
},
|
||||
}
|
||||
p.CallFunctionFn = func(request providers.CallFunctionRequest) providers.CallFunctionResponse {
|
||||
// Since there is only a single function defined, we don't want to make this implementation more complex than
|
||||
// needed, so we have only the implementation for that.
|
||||
v := request.Arguments[0].AsString()
|
||||
idx := strings.LastIndex(v, ":")
|
||||
if idx >= 0 {
|
||||
v = v[idx+1:]
|
||||
}
|
||||
port, err := strconv.ParseFloat(v, 64)
|
||||
if err != nil {
|
||||
return providers.CallFunctionResponse{
|
||||
Error: err,
|
||||
}
|
||||
}
|
||||
return providers.CallFunctionResponse{
|
||||
Result: cty.NumberVal(big.NewFloat(port)),
|
||||
}
|
||||
}
|
||||
ctx := testContext2(t, &ContextOpts{
|
||||
Providers: map[addrs.Provider]providers.Factory{
|
||||
addrs.MustParseProviderSourceString("example.com/foo/test"): testProviderFuncFixed(&p),
|
||||
},
|
||||
})
|
||||
|
||||
assertState := func(t *testing.T, s *states.State) {
|
||||
res := s.Resource(mustAbsResourceAddr("test_resource.res"))
|
||||
diff := cmp.Diff(`{"allow":[{"port":81},{"port":80}]}`, string(res.Instances[addrs.NoKey].Current.AttrsJSON))
|
||||
if diff != "" {
|
||||
t.Fatalf("wrong expected resource change found (-wanted, +got):\n%s", diff)
|
||||
}
|
||||
}
|
||||
plan, diags := ctx.Plan(context.Background(), m, states.NewState(), SimplePlanOpts(plans.NormalMode, nil))
|
||||
assertNoErrors(t, diags)
|
||||
assertState(t, plan.PlannedState)
|
||||
|
||||
state, diags := ctx.Apply(context.Background(), plan, m, nil)
|
||||
assertNoErrors(t, diags)
|
||||
assertState(t, state)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user