1package terraform
2
3import (
4	"sync"
5
6	"github.com/hashicorp/terraform/internal/addrs"
7	"github.com/hashicorp/terraform/internal/configs"
8	"github.com/hashicorp/terraform/internal/dag"
9	"github.com/hashicorp/terraform/internal/states"
10	"github.com/hashicorp/terraform/internal/tfdiags"
11)
12
13// PlanGraphBuilder implements GraphBuilder and is responsible for building
14// a graph for planning (creating a Terraform Diff).
15//
16// The primary difference between this graph and others:
17//
18//   * Based on the config since it represents the target state
19//
20//   * Ignores lifecycle options since no lifecycle events occur here. This
21//     simplifies the graph significantly since complex transforms such as
22//     create-before-destroy can be completely ignored.
23//
24type PlanGraphBuilder struct {
25	// Config is the configuration tree to build a plan from.
26	Config *configs.Config
27
28	// State is the current state
29	State *states.State
30
31	// Components is a factory for the plug-in components (providers and
32	// provisioners) available for use.
33	Components contextComponentFactory
34
35	// Schemas is the repository of schemas we will draw from to analyse
36	// the configuration.
37	Schemas *Schemas
38
39	// Targets are resources to target
40	Targets []addrs.Targetable
41
42	// ForceReplace are resource instances where if we would normally have
43	// generated a NoOp or Update action then we'll force generating a replace
44	// action instead. Create and Delete actions are not affected.
45	ForceReplace []addrs.AbsResourceInstance
46
47	// Validate will do structural validation of the graph.
48	Validate bool
49
50	// skipRefresh indicates that we should skip refreshing managed resources
51	skipRefresh bool
52
53	// skipPlanChanges indicates that we should skip the step of comparing
54	// prior state with configuration and generating planned changes to
55	// resource instances. (This is for the "refresh only" planning mode,
56	// where we _only_ do the refresh step.)
57	skipPlanChanges bool
58
59	// CustomConcrete can be set to customize the node types created
60	// for various parts of the plan. This is useful in order to customize
61	// the plan behavior.
62	CustomConcrete         bool
63	ConcreteProvider       ConcreteProviderNodeFunc
64	ConcreteResource       ConcreteResourceNodeFunc
65	ConcreteResourceOrphan ConcreteResourceInstanceNodeFunc
66	ConcreteModule         ConcreteModuleNodeFunc
67
68	once sync.Once
69}
70
71// See GraphBuilder
72func (b *PlanGraphBuilder) Build(path addrs.ModuleInstance) (*Graph, tfdiags.Diagnostics) {
73	return (&BasicGraphBuilder{
74		Steps:    b.Steps(),
75		Validate: b.Validate,
76		Name:     "PlanGraphBuilder",
77	}).Build(path)
78}
79
80// See GraphBuilder
81func (b *PlanGraphBuilder) Steps() []GraphTransformer {
82	b.once.Do(b.init)
83
84	concreteResourceInstanceDeposed := func(a *NodeAbstractResourceInstance, key states.DeposedKey) dag.Vertex {
85		return &NodePlanDeposedResourceInstanceObject{
86			NodeAbstractResourceInstance: a,
87			DeposedKey:                   key,
88
89			skipRefresh:     b.skipRefresh,
90			skipPlanChanges: b.skipPlanChanges,
91		}
92	}
93
94	steps := []GraphTransformer{
95		// Creates all the resources represented in the config
96		&ConfigTransformer{
97			Concrete: b.ConcreteResource,
98			Config:   b.Config,
99		},
100
101		// Add dynamic values
102		&RootVariableTransformer{Config: b.Config},
103		&ModuleVariableTransformer{Config: b.Config},
104		&LocalTransformer{Config: b.Config},
105		&OutputTransformer{Config: b.Config},
106
107		// Add orphan resources
108		&OrphanResourceInstanceTransformer{
109			Concrete: b.ConcreteResourceOrphan,
110			State:    b.State,
111			Config:   b.Config,
112		},
113
114		// We also need nodes for any deposed instance objects present in the
115		// state, so we can plan to destroy them. (This intentionally
116		// skips creating nodes for _current_ objects, since ConfigTransformer
117		// created nodes that will do that during DynamicExpand.)
118		&StateTransformer{
119			ConcreteDeposed: concreteResourceInstanceDeposed,
120			State:           b.State,
121		},
122
123		// Attach the state
124		&AttachStateTransformer{State: b.State},
125
126		// Create orphan output nodes
127		&OrphanOutputTransformer{Config: b.Config, State: b.State},
128
129		// Attach the configuration to any resources
130		&AttachResourceConfigTransformer{Config: b.Config},
131
132		// add providers
133		TransformProviders(b.Components.ResourceProviders(), b.ConcreteProvider, b.Config),
134
135		// Remove modules no longer present in the config
136		&RemovedModuleTransformer{Config: b.Config, State: b.State},
137
138		// Must attach schemas before ReferenceTransformer so that we can
139		// analyze the configuration to find references.
140		&AttachSchemaTransformer{Schemas: b.Schemas, Config: b.Config},
141
142		// Create expansion nodes for all of the module calls. This must
143		// come after all other transformers that create nodes representing
144		// objects that can belong to modules.
145		&ModuleExpansionTransformer{Concrete: b.ConcreteModule, Config: b.Config},
146
147		// Connect so that the references are ready for targeting. We'll
148		// have to connect again later for providers and so on.
149		&ReferenceTransformer{},
150		&AttachDependenciesTransformer{},
151
152		// Make sure data sources are aware of any depends_on from the
153		// configuration
154		&attachDataResourceDependsOnTransformer{},
155
156		// Target
157		&TargetsTransformer{Targets: b.Targets},
158
159		// Detect when create_before_destroy must be forced on for a particular
160		// node due to dependency edges, to avoid graph cycles during apply.
161		&ForcedCBDTransformer{},
162
163		// Add the node to fix the state count boundaries
164		&CountBoundaryTransformer{
165			Config: b.Config,
166		},
167
168		// Close opened plugin connections
169		&CloseProviderTransformer{},
170
171		// Close the root module
172		&CloseRootModuleTransformer{},
173
174		// Perform the transitive reduction to make our graph a bit
175		// more understandable if possible (it usually is possible).
176		&TransitiveReductionTransformer{},
177	}
178
179	return steps
180}
181
182func (b *PlanGraphBuilder) init() {
183	// Do nothing if the user requests customizing the fields
184	if b.CustomConcrete {
185		return
186	}
187
188	b.ConcreteProvider = func(a *NodeAbstractProvider) dag.Vertex {
189		return &NodeApplyableProvider{
190			NodeAbstractProvider: a,
191		}
192	}
193
194	b.ConcreteResource = func(a *NodeAbstractResource) dag.Vertex {
195		return &nodeExpandPlannableResource{
196			NodeAbstractResource: a,
197			skipRefresh:          b.skipRefresh,
198			skipPlanChanges:      b.skipPlanChanges,
199			forceReplace:         b.ForceReplace,
200		}
201	}
202
203	b.ConcreteResourceOrphan = func(a *NodeAbstractResourceInstance) dag.Vertex {
204		return &NodePlannableResourceInstanceOrphan{
205			NodeAbstractResourceInstance: a,
206			skipRefresh:                  b.skipRefresh,
207			skipPlanChanges:              b.skipPlanChanges,
208		}
209	}
210}
211