Files
opentf/internal/engine/planning/plan_data.go
Martin Atkins b6cdfd1afa lang/eval: Bind PlanningOracle to PlanGlue
Previously the PlanGlue methods all took PlanningOracle pointers as one
of their arguments, which is annoying since all of them should end up with
pointers to the same object and it makes it hard for the PlanGlue
implementation to do any work outside of and between the PlanGlue method
calls.

Instead then we'll have DrivePlanning take a function for building a
PlanGlue implementation given a PlanningOracle pointer, and then the
planning engine returns an implementation that binds a planContext to a
PlanningOracle it can then use to do all of its work.

Signed-off-by: Martin Atkins <mart@degeneration.co.uk>
2025-10-27 10:15:41 -07:00

96 lines
3.8 KiB
Go

// Copyright (c) The OpenTofu Authors
// SPDX-License-Identifier: MPL-2.0
// Copyright (c) 2023 HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0
package planning
import (
"context"
"fmt"
"github.com/zclconf/go-cty/cty"
"github.com/opentofu/opentofu/internal/addrs"
"github.com/opentofu/opentofu/internal/lang/eval"
"github.com/opentofu/opentofu/internal/providers"
"github.com/opentofu/opentofu/internal/states"
"github.com/opentofu/opentofu/internal/tfdiags"
)
func (p *planGlue) planDesiredDataResourceInstance(ctx context.Context, inst *eval.DesiredResourceInstance) (cty.Value, tfdiags.Diagnostics) {
// Regardless of outcome we'll always report that we completed planning.
defer p.planCtx.reportResourceInstancePlanCompletion(inst.Addr)
var diags tfdiags.Diagnostics
if inst.ProviderInstance == nil {
// TODO: Record that this was deferred because we don't yet know which
// provider instance it belongs to.
return deferredVal(cty.DynamicVal), diags
}
// TODO: There are various other reasons why we might need to defer planning
// this until a future plan/apply round.
// The equivalent of "refreshing" a data resource is just to discard it
// completely, because we only retain the previous result in state snapshots
// to support unusual situations like "tofu console"; it's not expected that
// data resource instances persist between rounds and they cannot because
// the protocol doesn't include any way to "upgrade" them if the provider
// schema has changed since previous round.
// FIXME: State is still using the weird old representation of provider
// instance addresses, so we can't actually populate the provider instance
// arguments properly here.
p.planCtx.refreshedState.SetResourceInstanceCurrent(inst.Addr, nil, addrs.AbsProviderConfig{}, inst.ProviderInstance.Key)
// TODO: If the config value is not wholly known, or if any resource
// instance in inst.RequiredResourceInstances already has a planned change,
// then plan to read this during the apply phase and return a "proposed new
// value" for use during the planning phase.
providerClient, moreDiags := p.providerClient(ctx, *inst.ProviderInstance)
if providerClient == nil {
moreDiags = moreDiags.Append(tfdiags.AttributeValue(
tfdiags.Error,
"Provider instance not available",
fmt.Sprintf("Cannot plan %s because its associated provider instance %s cannot initialize.", inst.Addr, *inst.ProviderInstance),
nil,
))
}
diags = diags.Append(moreDiags)
if moreDiags.HasErrors() {
return cty.DynamicVal, diags
}
resp := providerClient.ReadDataSource(ctx, providers.ReadDataSourceRequest{
TypeName: inst.Addr.Resource.Resource.Type,
Config: inst.ConfigVal,
// TODO: Add ProviderMeta information to eval.DesiredResourceInstance
// and then pass it on to the provider here.
})
diags = diags.Append(resp.Diagnostics)
if resp.Diagnostics.HasErrors() {
return cty.DynamicVal, diags
}
// TODO: Implement
panic("unimplemented")
}
func (p *planGlue) planOrphanDataResourceInstance(_ context.Context, addr addrs.AbsResourceInstance, state *states.ResourceInstance) tfdiags.Diagnostics {
// Regardless of outcome we'll always report that we completed planning.
defer p.planCtx.reportResourceInstancePlanCompletion(addr)
var diags tfdiags.Diagnostics
// An orphan data resource is always just discarded completely, because
// OpenTofu retains them only for esoteric uses like the "tofu console"
// command: they are not actually expected to persist between rounds.
//
// FIXME: We can't actually populate the provider instance address here
// because in our current model it's split awkwardly across *states.Resource
// and *states.ResourceInstance, and we only have the latter here.
p.planCtx.refreshedState.SetResourceInstanceCurrent(addr, nil, addrs.AbsProviderConfig{}, state.ProviderKey)
return diags
}