mirror of
https://github.com/opentofu/opentofu.git
synced 2025-01-05 21:53:04 -06:00
68b900928d
This is a minimal integration of instances.Expander used just for resource count and for_each, for now just forcing modules to always be singletons because the rest of Terraform Core isn't ready to deal with expanding module calls yet. This doesn't integrate super cleanly yet because we still have some cleanup work to do in the design of the plan walk, to make it explicit that the nodes in the plan graph represent static configuration objects rather than expanded instances, including for modules. To make this work in the meantime, there is some shimming between addrs.Module and addrs.ModuleInstance to correct for the discontinuities that result from the fact that Terraform currently assumes that modules are always singletons.
171 lines
5.1 KiB
Go
171 lines
5.1 KiB
Go
package terraform
|
|
|
|
import (
|
|
"log"
|
|
|
|
"github.com/hashicorp/terraform/dag"
|
|
"github.com/hashicorp/terraform/tfdiags"
|
|
)
|
|
|
|
// NodePlannableResource represents a resource that is "plannable":
|
|
// it is ready to be planned in order to create a diff.
|
|
type NodePlannableResource struct {
|
|
*NodeAbstractResource
|
|
|
|
// ForceCreateBeforeDestroy might be set via our GraphNodeDestroyerCBD
|
|
// during graph construction, if dependencies require us to force this
|
|
// on regardless of what the configuration says.
|
|
ForceCreateBeforeDestroy *bool
|
|
}
|
|
|
|
var (
|
|
_ GraphNodeSubPath = (*NodePlannableResource)(nil)
|
|
_ GraphNodeDestroyerCBD = (*NodePlannableResource)(nil)
|
|
_ GraphNodeDynamicExpandable = (*NodePlannableResource)(nil)
|
|
_ GraphNodeReferenceable = (*NodePlannableResource)(nil)
|
|
_ GraphNodeReferencer = (*NodePlannableResource)(nil)
|
|
_ GraphNodeResource = (*NodePlannableResource)(nil)
|
|
_ GraphNodeAttachResourceConfig = (*NodePlannableResource)(nil)
|
|
)
|
|
|
|
// GraphNodeEvalable
|
|
func (n *NodePlannableResource) EvalTree() EvalNode {
|
|
addr := n.ResourceAddr()
|
|
config := n.Config
|
|
|
|
if config == nil {
|
|
// Nothing to do, then.
|
|
log.Printf("[TRACE] NodeApplyableResource: no configuration present for %s", addr)
|
|
return &EvalNoop{}
|
|
}
|
|
|
|
// this ensures we can reference the resource even if the count is 0
|
|
return &EvalWriteResourceState{
|
|
Addr: addr.Resource,
|
|
Config: config,
|
|
ProviderAddr: n.ResolvedProvider,
|
|
}
|
|
}
|
|
|
|
// GraphNodeDestroyerCBD
|
|
func (n *NodePlannableResource) CreateBeforeDestroy() bool {
|
|
if n.ForceCreateBeforeDestroy != nil {
|
|
return *n.ForceCreateBeforeDestroy
|
|
}
|
|
|
|
// If we have no config, we just assume no
|
|
if n.Config == nil || n.Config.Managed == nil {
|
|
return false
|
|
}
|
|
|
|
return n.Config.Managed.CreateBeforeDestroy
|
|
}
|
|
|
|
// GraphNodeDestroyerCBD
|
|
func (n *NodePlannableResource) ModifyCreateBeforeDestroy(v bool) error {
|
|
n.ForceCreateBeforeDestroy = &v
|
|
return nil
|
|
}
|
|
|
|
// GraphNodeDynamicExpandable
|
|
func (n *NodePlannableResource) DynamicExpand(ctx EvalContext) (*Graph, error) {
|
|
var diags tfdiags.Diagnostics
|
|
|
|
// Our instance expander should already have been informed about the
|
|
// expansion of this resource and of all of its containing modules, so
|
|
// it can tell us which instance addresses we need to process.
|
|
module := ctx.Path().Module()
|
|
expander := ctx.InstanceExpander()
|
|
instanceAddrs := expander.ExpandResource(module, n.ResourceAddr().Resource)
|
|
|
|
// We need to potentially rename an instance address in the state
|
|
// if we're transitioning whether "count" is set at all.
|
|
//
|
|
// FIXME: We're re-evaluating count here, even though the InstanceExpander
|
|
// has already dealt with our expansion above, because we need it to
|
|
// call fixResourceCountSetTransition; the expander API and that function
|
|
// are not compatible yet.
|
|
count, countDiags := evaluateResourceCountExpression(n.Config.Count, ctx)
|
|
diags = diags.Append(countDiags)
|
|
if countDiags.HasErrors() {
|
|
return nil, diags.Err()
|
|
}
|
|
fixResourceCountSetTransition(ctx, n.ResourceAddr(), count != -1)
|
|
|
|
// Our graph transformers require access to the full state, so we'll
|
|
// temporarily lock it while we work on this.
|
|
state := ctx.State().Lock()
|
|
defer ctx.State().Unlock()
|
|
|
|
// The concrete resource factory we'll use
|
|
concreteResource := func(a *NodeAbstractResourceInstance) dag.Vertex {
|
|
// Add the config and state since we don't do that via transforms
|
|
a.Config = n.Config
|
|
a.ResolvedProvider = n.ResolvedProvider
|
|
a.Schema = n.Schema
|
|
a.ProvisionerSchemas = n.ProvisionerSchemas
|
|
|
|
return &NodePlannableResourceInstance{
|
|
NodeAbstractResourceInstance: a,
|
|
|
|
// By the time we're walking, we've figured out whether we need
|
|
// to force on CreateBeforeDestroy due to dependencies on other
|
|
// nodes that have it.
|
|
ForceCreateBeforeDestroy: n.CreateBeforeDestroy(),
|
|
}
|
|
}
|
|
|
|
// The concrete resource factory we'll use for orphans
|
|
concreteResourceOrphan := func(a *NodeAbstractResourceInstance) dag.Vertex {
|
|
// Add the config and state since we don't do that via transforms
|
|
a.Config = n.Config
|
|
a.ResolvedProvider = n.ResolvedProvider
|
|
a.Schema = n.Schema
|
|
a.ProvisionerSchemas = n.ProvisionerSchemas
|
|
|
|
return &NodePlannableResourceInstanceOrphan{
|
|
NodeAbstractResourceInstance: a,
|
|
}
|
|
}
|
|
|
|
// Start creating the steps
|
|
steps := []GraphTransformer{
|
|
// Expand the count or for_each (if present)
|
|
&ResourceCountTransformer{
|
|
Concrete: concreteResource,
|
|
Schema: n.Schema,
|
|
Addr: n.ResourceAddr(),
|
|
InstanceAddrs: instanceAddrs,
|
|
},
|
|
|
|
// Add the count/for_each orphans
|
|
&OrphanResourceCountTransformer{
|
|
Concrete: concreteResourceOrphan,
|
|
Addr: n.ResourceAddr(),
|
|
InstanceAddrs: instanceAddrs,
|
|
State: state,
|
|
},
|
|
|
|
// Attach the state
|
|
&AttachStateTransformer{State: state},
|
|
|
|
// Targeting
|
|
&TargetsTransformer{Targets: n.Targets},
|
|
|
|
// Connect references so ordering is correct
|
|
&ReferenceTransformer{},
|
|
|
|
// Make sure there is a single root
|
|
&RootTransformer{},
|
|
}
|
|
|
|
// Build the graph
|
|
b := &BasicGraphBuilder{
|
|
Steps: steps,
|
|
Validate: true,
|
|
Name: "NodePlannableResource",
|
|
}
|
|
graph, diags := b.Build(ctx.Path())
|
|
return graph, diags.ErrWithWarnings()
|
|
}
|