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.
210 lines
5.8 KiB
Go
210 lines
5.8 KiB
Go
package terraform
|
|
|
|
import (
|
|
"sync"
|
|
|
|
"github.com/hashicorp/terraform/addrs"
|
|
"github.com/hashicorp/terraform/configs"
|
|
"github.com/hashicorp/terraform/dag"
|
|
"github.com/hashicorp/terraform/states"
|
|
"github.com/hashicorp/terraform/tfdiags"
|
|
)
|
|
|
|
// PlanGraphBuilder implements GraphBuilder and is responsible for building
|
|
// a graph for planning (creating a Terraform Diff).
|
|
//
|
|
// The primary difference between this graph and others:
|
|
//
|
|
// * Based on the config since it represents the target state
|
|
//
|
|
// * Ignores lifecycle options since no lifecycle events occur here. This
|
|
// simplifies the graph significantly since complex transforms such as
|
|
// create-before-destroy can be completely ignored.
|
|
//
|
|
type PlanGraphBuilder struct {
|
|
// Config is the configuration tree to build a plan from.
|
|
Config *configs.Config
|
|
|
|
// State is the current state
|
|
State *states.State
|
|
|
|
// Components is a factory for the plug-in components (providers and
|
|
// provisioners) available for use.
|
|
Components contextComponentFactory
|
|
|
|
// Schemas is the repository of schemas we will draw from to analyse
|
|
// the configuration.
|
|
Schemas *Schemas
|
|
|
|
// Targets are resources to target
|
|
Targets []addrs.Targetable
|
|
|
|
// DisableReduce, if true, will not reduce the graph. Great for testing.
|
|
DisableReduce bool
|
|
|
|
// Validate will do structural validation of the graph.
|
|
Validate bool
|
|
|
|
// CustomConcrete can be set to customize the node types created
|
|
// for various parts of the plan. This is useful in order to customize
|
|
// the plan behavior.
|
|
CustomConcrete bool
|
|
ConcreteProvider ConcreteProviderNodeFunc
|
|
ConcreteResource ConcreteResourceNodeFunc
|
|
ConcreteResourceOrphan ConcreteResourceInstanceNodeFunc
|
|
|
|
once sync.Once
|
|
}
|
|
|
|
// See GraphBuilder
|
|
func (b *PlanGraphBuilder) Build(path addrs.ModuleInstance) (*Graph, tfdiags.Diagnostics) {
|
|
return (&BasicGraphBuilder{
|
|
Steps: b.Steps(),
|
|
Validate: b.Validate,
|
|
Name: "PlanGraphBuilder",
|
|
}).Build(path)
|
|
}
|
|
|
|
// See GraphBuilder
|
|
func (b *PlanGraphBuilder) Steps() []GraphTransformer {
|
|
b.once.Do(b.init)
|
|
|
|
concreteResourceInstanceDeposed := func(a *NodeAbstractResourceInstance, key states.DeposedKey) dag.Vertex {
|
|
return &NodePlanDeposedResourceInstanceObject{
|
|
NodeAbstractResourceInstance: a,
|
|
DeposedKey: key,
|
|
}
|
|
}
|
|
|
|
steps := []GraphTransformer{
|
|
// Creates all the resources represented in the config
|
|
&ConfigTransformer{
|
|
Concrete: b.ConcreteResource,
|
|
Config: b.Config,
|
|
},
|
|
|
|
// Add the local values
|
|
&LocalTransformer{Config: b.Config},
|
|
|
|
// Add the outputs
|
|
&OutputTransformer{Config: b.Config},
|
|
|
|
// Add orphan resources
|
|
&OrphanResourceInstanceTransformer{
|
|
Concrete: b.ConcreteResourceOrphan,
|
|
State: b.State,
|
|
Config: b.Config,
|
|
},
|
|
|
|
// We also need nodes for any deposed instance objects present in the
|
|
// state, so we can plan to destroy them. (This intentionally
|
|
// skips creating nodes for _current_ objects, since ConfigTransformer
|
|
// created nodes that will do that during DynamicExpand.)
|
|
&StateTransformer{
|
|
ConcreteDeposed: concreteResourceInstanceDeposed,
|
|
State: b.State,
|
|
},
|
|
|
|
// Create orphan output nodes
|
|
&OrphanOutputTransformer{
|
|
Config: b.Config,
|
|
State: b.State,
|
|
},
|
|
|
|
// Attach the configuration to any resources
|
|
&AttachResourceConfigTransformer{Config: b.Config},
|
|
|
|
// Attach the state
|
|
&AttachStateTransformer{State: b.State},
|
|
|
|
// Add root variables
|
|
&RootVariableTransformer{Config: b.Config},
|
|
|
|
&MissingProvisionerTransformer{Provisioners: b.Components.ResourceProvisioners()},
|
|
&ProvisionerTransformer{},
|
|
|
|
// Add module variables
|
|
&ModuleVariableTransformer{
|
|
Config: b.Config,
|
|
},
|
|
|
|
TransformProviders(b.Components.ResourceProviders(), b.ConcreteProvider, b.Config),
|
|
|
|
// Remove modules no longer present in the config
|
|
&RemovedModuleTransformer{Config: b.Config, State: b.State},
|
|
|
|
// Must attach schemas before ReferenceTransformer so that we can
|
|
// analyze the configuration to find references.
|
|
&AttachSchemaTransformer{Schemas: b.Schemas},
|
|
|
|
// Create expansion nodes for all of the module calls. This must
|
|
// come after all other transformers that create nodes representing
|
|
// objects that can belong to modules.
|
|
&ModuleExpansionTransformer{Config: b.Config},
|
|
|
|
// Connect so that the references are ready for targeting. We'll
|
|
// have to connect again later for providers and so on.
|
|
&ReferenceTransformer{},
|
|
|
|
// Add the node to fix the state count boundaries
|
|
&CountBoundaryTransformer{
|
|
Config: b.Config,
|
|
},
|
|
|
|
// Target
|
|
&TargetsTransformer{
|
|
Targets: b.Targets,
|
|
|
|
// Resource nodes from config have not yet been expanded for
|
|
// "count", so we must apply targeting without indices. Exact
|
|
// targeting will be dealt with later when these resources
|
|
// DynamicExpand.
|
|
IgnoreIndices: true,
|
|
},
|
|
|
|
// Detect when create_before_destroy must be forced on for a particular
|
|
// node due to dependency edges, to avoid graph cycles during apply.
|
|
&ForcedCBDTransformer{},
|
|
|
|
// Close opened plugin connections
|
|
&CloseProviderTransformer{},
|
|
&CloseProvisionerTransformer{},
|
|
|
|
// Single root
|
|
&RootTransformer{},
|
|
}
|
|
|
|
if !b.DisableReduce {
|
|
// Perform the transitive reduction to make our graph a bit
|
|
// more sane if possible (it usually is possible).
|
|
steps = append(steps, &TransitiveReductionTransformer{})
|
|
}
|
|
|
|
return steps
|
|
}
|
|
|
|
func (b *PlanGraphBuilder) init() {
|
|
// Do nothing if the user requests customizing the fields
|
|
if b.CustomConcrete {
|
|
return
|
|
}
|
|
|
|
b.ConcreteProvider = func(a *NodeAbstractProvider) dag.Vertex {
|
|
return &NodeApplyableProvider{
|
|
NodeAbstractProvider: a,
|
|
}
|
|
}
|
|
|
|
b.ConcreteResource = func(a *NodeAbstractResource) dag.Vertex {
|
|
return &NodePlannableResource{
|
|
NodeAbstractResource: a,
|
|
}
|
|
}
|
|
|
|
b.ConcreteResourceOrphan = func(a *NodeAbstractResourceInstance) dag.Vertex {
|
|
return &NodePlannableResourceInstanceOrphan{
|
|
NodeAbstractResourceInstance: a,
|
|
}
|
|
}
|
|
}
|