mirror of
https://github.com/opentofu/opentofu.git
synced 2024-12-28 01:41:48 -06:00
2802d319d2
This fixes interpolation issues on grandchild data sources that have multiple instances (ie: counts). For example, baz depends on bar, which depends on foo. In this instance, after an initial TF run is done and state is saved, the next refresh/plan is not properly transformed, and instead of the graph/state coming through as data.x.bar.0, it comes through as data.x.bar. This breaks interpolations that rely on splat operators - ie: data.x.bar.*.out.
165 lines
4.2 KiB
Go
165 lines
4.2 KiB
Go
package terraform
|
|
|
|
import (
|
|
"sync"
|
|
|
|
"github.com/hashicorp/terraform/config/module"
|
|
"github.com/hashicorp/terraform/dag"
|
|
)
|
|
|
|
// 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 {
|
|
// Module is the root module for the graph to build.
|
|
Module *module.Tree
|
|
|
|
// State is the current state
|
|
State *State
|
|
|
|
// Providers is the list of providers supported.
|
|
Providers []string
|
|
|
|
// Provisioners is the list of provisioners supported.
|
|
Provisioners []string
|
|
|
|
// Targets are resources to target
|
|
Targets []string
|
|
|
|
// 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 ConcreteResourceNodeFunc
|
|
|
|
once sync.Once
|
|
}
|
|
|
|
// See GraphBuilder
|
|
func (b *PlanGraphBuilder) Build(path []string) (*Graph, error) {
|
|
return (&BasicGraphBuilder{
|
|
Steps: b.Steps(),
|
|
Validate: b.Validate,
|
|
Name: "PlanGraphBuilder",
|
|
}).Build(path)
|
|
}
|
|
|
|
// See GraphBuilder
|
|
func (b *PlanGraphBuilder) Steps() []GraphTransformer {
|
|
b.once.Do(b.init)
|
|
|
|
steps := []GraphTransformer{
|
|
// Creates all the resources represented in the config
|
|
&ConfigTransformer{
|
|
Concrete: b.ConcreteResource,
|
|
Module: b.Module,
|
|
},
|
|
|
|
// Add the outputs
|
|
&OutputTransformer{Module: b.Module},
|
|
|
|
// Add orphan resources
|
|
&OrphanResourceTransformer{
|
|
Concrete: b.ConcreteResourceOrphan,
|
|
State: b.State,
|
|
Module: b.Module,
|
|
},
|
|
|
|
// Attach the configuration to any resources
|
|
&AttachResourceConfigTransformer{Module: b.Module},
|
|
|
|
// Attach the state
|
|
&AttachStateTransformer{State: b.State},
|
|
|
|
// Add root variables
|
|
&RootVariableTransformer{Module: b.Module},
|
|
|
|
// Create all the providers
|
|
&MissingProviderTransformer{Providers: b.Providers, Concrete: b.ConcreteProvider},
|
|
&ProviderTransformer{},
|
|
&DisableProviderTransformer{},
|
|
&ParentProviderTransformer{},
|
|
&AttachProviderConfigTransformer{Module: b.Module},
|
|
|
|
// Provisioner-related transformations. Only add these if requested.
|
|
GraphTransformIf(
|
|
func() bool { return b.Provisioners != nil },
|
|
GraphTransformMulti(
|
|
&MissingProvisionerTransformer{Provisioners: b.Provisioners},
|
|
&ProvisionerTransformer{},
|
|
),
|
|
),
|
|
|
|
// Add module variables
|
|
&ModuleVariableTransformer{Module: b.Module},
|
|
|
|
// Connect so that the references are ready for targeting. We'll
|
|
// have to connect again later for providers and so on.
|
|
&ReferenceTransformer{},
|
|
|
|
// Target
|
|
&TargetsTransformer{Targets: b.Targets},
|
|
|
|
// Close opened plugin connections
|
|
&CloseProviderTransformer{},
|
|
&CloseProvisionerTransformer{},
|
|
|
|
// Add the node to fix the state count boundaries
|
|
&CountBoundaryTransformer{},
|
|
|
|
// 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{
|
|
NodeAbstractCountResource: &NodeAbstractCountResource{
|
|
NodeAbstractResource: a,
|
|
},
|
|
}
|
|
}
|
|
|
|
b.ConcreteResourceOrphan = func(a *NodeAbstractResource) dag.Vertex {
|
|
return &NodePlannableResourceOrphan{
|
|
NodeAbstractResource: a,
|
|
}
|
|
}
|
|
}
|