mirror of
https://github.com/opentffoundation/opentf.git
synced 2026-02-22 23:00:53 -05:00
This is a _very_ early, minimal plumbing of the execution graph builder into the experimental planning engine. The goal here is mainly just to prove the idea that the planning engine can build execution graphs using the execgraph.Builder API we currently have. This implementation is not complete yet, and also we are expecting to rework the structure of the planning engine later on anyway so this initial work focuses on just plumbing it in to what we had as straightforwardly as possible. This is enough to get a serialized form of _an_ execution graph included in the generated plan, though since we don't have the apply engine implemented we don't actually use it for anything yet. In subsequent commits we'll continue building out the graph-building logic and then arrange for the apply phase to unmarshal the saved execution graph and attempt to execute it, so we can hopefully see a minimal version of all of this working end-to-end for the first time. But for now, this was mainly just a proof-of-concept of building an execution graph and capturing it into its temporary home in the plans.Plan model. Signed-off-by: Martin Atkins <mart@degeneration.co.uk>
99 lines
4.0 KiB
Go
99 lines
4.0 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/engine/internal/execgraph"
|
|
"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, egb *execgraph.Builder) (cty.Value, execgraph.ResourceInstanceResultRef, tfdiags.Diagnostics) {
|
|
// Regardless of outcome we'll always report that we completed planning.
|
|
defer p.planCtx.reportResourceInstancePlanCompletion(inst.Addr)
|
|
var diags tfdiags.Diagnostics
|
|
|
|
validateDiags := p.planCtx.providers.ValidateResourceConfig(ctx, inst.Provider, inst.Addr.Resource.Resource.Mode, inst.Addr.Resource.Resource.Type, inst.ConfigVal)
|
|
diags = diags.Append(validateDiags)
|
|
if diags.HasErrors() {
|
|
return cty.DynamicVal, nil, diags
|
|
}
|
|
|
|
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), nil, 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, nil, 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, nil, diags
|
|
}
|
|
|
|
// TODO: Implement
|
|
panic("unimplemented")
|
|
}
|
|
|
|
func (p *planGlue) planOrphanDataResourceInstance(_ context.Context, addr addrs.AbsResourceInstance, state *states.ResourceInstanceObjectFullSrc, egb *execgraph.Builder) 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 object 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.
|
|
p.planCtx.refreshedState.SetResourceInstanceObjectFull(addr, states.NotDeposed, nil)
|
|
|
|
return diags
|
|
}
|