mirror of
https://github.com/opentffoundation/opentf.git
synced 2025-12-25 01:00:16 -05:00
lang/eval: Shuffle resource instance result handling a little more
The separation of concerns here still doesn't feel _quite_ right since a resource instance node needs to know what provider it belongs to even though that package doesn't otherwise interact with providers directly at all, but this at least clarifies exactly one location that's responsible for getting the config value for the result value callback to use. Signed-off-by: Martin Atkins <mart@degeneration.co.uk>
This commit is contained in:
@@ -60,7 +60,7 @@ type validationGlue struct {
|
||||
}
|
||||
|
||||
// ResourceInstanceValue implements evaluationGlue.
|
||||
func (v *validationGlue) ResourceInstanceValue(ctx context.Context, ri *configgraph.ResourceInstance) (cty.Value, tfdiags.Diagnostics) {
|
||||
func (v *validationGlue) ResourceInstanceValue(ctx context.Context, ri *configgraph.ResourceInstance, configVal cty.Value) (cty.Value, tfdiags.Diagnostics) {
|
||||
schema, diags := v.providers.ResourceTypeSchema(ctx,
|
||||
ri.Provider,
|
||||
ri.Addr.Resource.Resource.Mode,
|
||||
@@ -72,18 +72,6 @@ func (v *validationGlue) ResourceInstanceValue(ctx context.Context, ri *configgr
|
||||
return cty.DynamicVal, diags
|
||||
}
|
||||
|
||||
// During the validation phase we always just use a placeholder value
|
||||
// based on the config value, inserting unknown values in all of the
|
||||
// locations where a provider could potentially choose a value during
|
||||
// the plan or apply phases.
|
||||
configVal, moreDiags := ri.ConfigValue(ctx)
|
||||
diags = diags.Append(moreDiags)
|
||||
if moreDiags.HasErrors() {
|
||||
// In practice we shouldn't even get called when the config value
|
||||
// isn't valid, so the following is just for robustness.
|
||||
return cty.UnknownVal(schema.Block.ImpliedType()), diags
|
||||
}
|
||||
|
||||
// We now have enough information to produce a placeholder "planned new
|
||||
// state" by placing unknown values in any location that the provider
|
||||
// would be allowed to choose a value.
|
||||
|
||||
@@ -29,5 +29,10 @@ type evaluationGlue interface {
|
||||
//
|
||||
// What "result value" means depends on the phase. For example, during
|
||||
// the planning phase it's the "planned new state".
|
||||
ResourceInstanceValue(ctx context.Context, ri *configgraph.ResourceInstance) (cty.Value, tfdiags.Diagnostics)
|
||||
//
|
||||
// The given configVal is the result of calling ConfigValue on the given
|
||||
// resource instance object, but guaranteed to have already been validated.
|
||||
// The implementation of this method should not call ConfigValue again
|
||||
// and should instead just trust the value given as an argument.
|
||||
ResourceInstanceValue(ctx context.Context, ri *configgraph.ResourceInstance, configVal cty.Value) (cty.Value, tfdiags.Diagnostics)
|
||||
}
|
||||
|
||||
@@ -45,7 +45,7 @@ type Resource struct {
|
||||
|
||||
// GetInstanceResultValue is the callback that child instances of this
|
||||
// resource should use to obtain their result values.
|
||||
GetInstanceResultValue func(ctx context.Context, inst *ResourceInstance) func(ctx context.Context) (cty.Value, tfdiags.Diagnostics)
|
||||
GetInstanceResultValue func(ctx context.Context, inst *ResourceInstance) func(ctx context.Context, configVal cty.Value) (cty.Value, tfdiags.Diagnostics)
|
||||
|
||||
// InstanceSelector represents a rule for deciding which instances of
|
||||
// this resource have been declared.
|
||||
|
||||
@@ -59,7 +59,7 @@ type ResourceInstance struct {
|
||||
// MUST use the mechanisms from package grapheval in order to cooperate
|
||||
// with the self-dependency detection used by this package to prevent
|
||||
// deadlocks.
|
||||
GetResultValue func(ctx context.Context) (cty.Value, tfdiags.Diagnostics)
|
||||
GetResultValue func(ctx context.Context, configVal cty.Value) (cty.Value, tfdiags.Diagnostics)
|
||||
}
|
||||
|
||||
var _ exprs.Valuer = (*ResourceInstance)(nil)
|
||||
@@ -86,6 +86,8 @@ func (ri *ResourceInstance) ConfigValue(ctx context.Context) (cty.Value, tfdiags
|
||||
|
||||
// Value implements exprs.Valuer.
|
||||
func (ri *ResourceInstance) Value(ctx context.Context) (cty.Value, tfdiags.Diagnostics) {
|
||||
// We use the configuration value here only for its marks, since that
|
||||
// allows us to propagate any
|
||||
configVal, diags := ri.ConfigValue(ctx)
|
||||
if diags.HasErrors() {
|
||||
// If we don't have a valid config value then we'll stop early
|
||||
@@ -101,9 +103,10 @@ func (ri *ResourceInstance) Value(ctx context.Context) (cty.Value, tfdiags.Diagn
|
||||
// this evaluation in support of. Refer to the documentation of
|
||||
// the GetResultValue field for details on what we're expecting this
|
||||
// function to do.
|
||||
resultVal, diags := ri.GetResultValue(ctx)
|
||||
// The result must always be marked with ResourceInstanceMark{ri} so that
|
||||
// we can detect when another value elsewhere is derived from this one.
|
||||
resultVal, diags := ri.GetResultValue(ctx, configVal)
|
||||
|
||||
// The result needs some additional preparation to make sure it's
|
||||
// marked correctly for ongoing use in other expressions.
|
||||
return prepareResourceInstanceResult(resultVal, ri, configVal), diags
|
||||
}
|
||||
|
||||
|
||||
@@ -236,7 +236,7 @@ func compileModuleInstanceResources(
|
||||
declScope exprs.Scope,
|
||||
moduleInstanceAddr addrs.ModuleInstance,
|
||||
providers Providers,
|
||||
getResultValue func(context.Context, *configgraph.ResourceInstance) (cty.Value, tfdiags.Diagnostics),
|
||||
getResultValue func(context.Context, *configgraph.ResourceInstance, cty.Value) (cty.Value, tfdiags.Diagnostics),
|
||||
) map[addrs.Resource]*configgraph.Resource {
|
||||
ret := make(map[addrs.Resource]*configgraph.Resource, len(managedConfigs)+len(dataConfigs)+len(ephemeralConfigs))
|
||||
for _, rc := range managedConfigs {
|
||||
@@ -260,7 +260,7 @@ func compileModuleInstanceResource(
|
||||
declScope exprs.Scope,
|
||||
moduleInstanceAddr addrs.ModuleInstance,
|
||||
providers Providers,
|
||||
getResultValue func(context.Context, *configgraph.ResourceInstance) (cty.Value, tfdiags.Diagnostics),
|
||||
getResultValue func(context.Context, *configgraph.ResourceInstance, cty.Value) (cty.Value, tfdiags.Diagnostics),
|
||||
) (addrs.Resource, *configgraph.Resource) {
|
||||
resourceAddr := config.Addr()
|
||||
absAddr := moduleInstanceAddr.Resource(resourceAddr.Mode, resourceAddr.Type, resourceAddr.Name)
|
||||
@@ -294,9 +294,23 @@ func compileModuleInstanceResource(
|
||||
// Resource implementation calls this during resource instance
|
||||
// compilation to get a resource-instance-specific value fetcher
|
||||
// for each of its instances.
|
||||
GetInstanceResultValue: func(ctx context.Context, inst *configgraph.ResourceInstance) func(ctx context.Context) (cty.Value, tfdiags.Diagnostics) {
|
||||
return func(ctx context.Context) (cty.Value, tfdiags.Diagnostics) {
|
||||
return getResultValue(ctx, inst)
|
||||
GetInstanceResultValue: func(ctx context.Context, inst *configgraph.ResourceInstance) func(context.Context, cty.Value) (cty.Value, tfdiags.Diagnostics) {
|
||||
return func(ctx context.Context, configVal cty.Value) (cty.Value, tfdiags.Diagnostics) {
|
||||
schema, moreDiags := providers.ResourceTypeSchema(ctx, config.Provider, resourceAddr.Mode, resourceAddr.Type)
|
||||
diags = diags.Append(moreDiags)
|
||||
if moreDiags.HasErrors() {
|
||||
return cty.DynamicVal, diags
|
||||
}
|
||||
|
||||
moreDiags = providers.ValidateResourceConfig(ctx, config.Provider, resourceAddr.Mode, resourceAddr.Type, configVal)
|
||||
diags = diags.Append(moreDiags)
|
||||
if diags.HasErrors() {
|
||||
return cty.UnknownVal(schema.Block.ImpliedType()), diags
|
||||
}
|
||||
|
||||
// The real getResultValue function can now assume that
|
||||
// the given configuration is valid.
|
||||
return getResultValue(ctx, inst, configVal)
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@ import (
|
||||
"github.com/opentofu/opentofu/internal/addrs"
|
||||
"github.com/opentofu/opentofu/internal/configs"
|
||||
"github.com/opentofu/opentofu/internal/lang/grapheval"
|
||||
"github.com/opentofu/opentofu/internal/providers"
|
||||
)
|
||||
|
||||
func TestCompileModuleInstance_valuesOnly(t *testing.T) {
|
||||
@@ -43,6 +44,9 @@ func TestCompileModuleInstance_valuesOnly(t *testing.T) {
|
||||
"a": cty.True,
|
||||
}),
|
||||
evalContext: evalCtx,
|
||||
evaluationGlue: &validationGlue{
|
||||
providers: ProvidersForTesting(map[addrs.Provider]*providers.GetProviderSchemaResponse{}),
|
||||
},
|
||||
}
|
||||
inst := compileModuleInstance(ctx, module, addrs.ModuleSourceLocal("."), call)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user