terraform/terraform/graph_builder_plan.go

182 lines
4.5 KiB
Go
Raw Normal View History

2016-11-06 00:26:12 +01:00
package terraform
import (
"sync"
2016-11-06 00:26:12 +01:00
"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
2016-11-06 00:26:12 +01:00
// DisableReduce, if true, will not reduce the graph. Great for testing.
DisableReduce bool
// Validate will do structural validation of the graph.
Validate bool
2017-01-24 06:48:22 +01:00
// 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
2016-11-06 00:26:12 +01:00
}
// See GraphBuilder
func (b *PlanGraphBuilder) Build(path []string) (*Graph, error) {
return (&BasicGraphBuilder{
Steps: b.Steps(),
Validate: b.Validate,
Name: "PlanGraphBuilder",
2016-11-06 00:26:12 +01:00
}).Build(path)
}
// See GraphBuilder
func (b *PlanGraphBuilder) Steps() []GraphTransformer {
b.once.Do(b.init)
2016-11-07 17:57:27 +01:00
2016-11-06 00:26:12 +01:00
steps := []GraphTransformer{
// Creates all the resources represented in the config
&ConfigTransformer{
Concrete: b.ConcreteResource,
2016-11-06 00:26:12 +01:00
Module: b.Module,
},
// Add the local values
&LocalTransformer{Module: b.Module},
2016-11-06 00:26:12 +01:00
// Add the outputs
&OutputTransformer{Module: b.Module},
2016-11-07 17:57:27 +01:00
// Add orphan resources
&OrphanResourceTransformer{
Concrete: b.ConcreteResourceOrphan,
2016-11-07 17:57:27 +01:00
State: b.State,
Module: b.Module,
},
// Create orphan output nodes
&OrphanOutputTransformer{
Module: b.Module,
State: b.State,
},
2016-11-06 00:26:12 +01:00
// Attach the configuration to any resources
&AttachResourceConfigTransformer{Module: b.Module},
// Attach the state
&AttachStateTransformer{State: b.State},
// Add root variables
&RootVariableTransformer{Module: b.Module},
TransformProviders(b.Providers, b.ConcreteProvider, 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,
},
// Remove modules no longer present in the config
&RemovedModuleTransformer{Module: b.Module, State: b.State},
// 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{},
// Target
core: -target option to also select resources in descendant modules Previously the behavior for -target when given a module address was to target only resources directly within that module, ignoring any resources defined in child modules. This behavior turned out to be counter-intuitive, since users expected the -target address to be interpreted hierarchically. We'll now use the new "Contains" function for addresses, which provides a hierarchical "containment" concept that is more consistent with user expectations. In particular, it allows module.foo to match module.foo.module.bar.aws_instance.baz, where before that would not have been true. Since Contains isn't commutative (unlike Equals) this requires some special handling for targeting specific indices. When given an argument like -target=aws_instance.foo[0], the initial graph construction (for both plan and refresh) is for the resource nodes from configuration, which have not yet been expanded to separate indexed instances. Thus we need to do the first pass of TargetsTransformer in mode where indices are ignored, with the work then completed by the DynamicExpand method which re-applies the TargetsTransformer in index-sensitive mode. This is a breaking change for anyone depending on the previous behavior of -target, since it will now select more resources than before. There is no way provided to obtain the previous behavior. Eventually we may support negative targeting, which could then combine with positive targets to regain the previous behavior as an explicit choice.
2017-06-16 03:15:41 +02:00
&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,
},
// Close opened plugin connections
&CloseProviderTransformer{},
&CloseProvisionerTransformer{},
2016-11-06 00:26:12 +01:00
// 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,
}
}
}