diff --git a/terraform/context.go b/terraform/context.go index 5b7777743..176c1e182 100644 --- a/terraform/context.go +++ b/terraform/context.go @@ -392,23 +392,14 @@ func (c *Context) Apply() (*State, error) { } // Build the new graph. We do this no matter what so we can shadow it. - var newGraph *Graph - if c.destroy { - newGraph, err = (&DestroyApplyGraphBuilder{ - Module: c.module, - Diff: c.diff, - State: c.state, - Providers: c.components.ResourceProviders(), - }).Build(RootModulePath) - } else { - newGraph, err = (&ApplyGraphBuilder{ - Module: c.module, - Diff: c.diff, - State: c.state, - Providers: c.components.ResourceProviders(), - Provisioners: c.components.ResourceProvisioners(), - }).Build(RootModulePath) - } + newGraph, err := (&ApplyGraphBuilder{ + Module: c.module, + Diff: c.diff, + State: c.state, + Providers: c.components.ResourceProviders(), + Provisioners: c.components.ResourceProvisioners(), + Destroy: c.destroy, + }).Build(RootModulePath) if err != nil && !newGraphEnabled { // If we had an error graphing but we're not using this graph, just // set it to nil and record it as a shadow error. diff --git a/terraform/graph_builder.go b/terraform/graph_builder.go index 1c0a2d595..ff3145df0 100644 --- a/terraform/graph_builder.go +++ b/terraform/graph_builder.go @@ -26,6 +26,10 @@ type BasicGraphBuilder struct { func (b *BasicGraphBuilder) Build(path []string) (*Graph, error) { g := &Graph{Path: path} for _, step := range b.Steps { + if step == nil { + continue + } + if err := step.Transform(g); err != nil { return g, err } diff --git a/terraform/graph_builder_apply.go b/terraform/graph_builder_apply.go index 83cc89374..1d10be39a 100644 --- a/terraform/graph_builder_apply.go +++ b/terraform/graph_builder_apply.go @@ -30,6 +30,9 @@ type ApplyGraphBuilder struct { // DisableReduce, if true, will not reduce the graph. Great for testing. DisableReduce bool + + // Destroy, if true, represents a pure destroy operation + Destroy bool } // See GraphBuilder @@ -77,7 +80,10 @@ func (b *ApplyGraphBuilder) Steps() []GraphTransformer { // Destruction ordering &DestroyEdgeTransformer{Module: b.Module, State: b.State}, - &CBDEdgeTransformer{Module: b.Module, State: b.State}, + GraphTransformIf( + func() bool { return !b.Destroy }, + &CBDEdgeTransformer{Module: b.Module, State: b.State}, + ), // Create all the providers &MissingProviderTransformer{Providers: b.Providers, Factory: providerFactory}, @@ -87,8 +93,13 @@ func (b *ApplyGraphBuilder) Steps() []GraphTransformer { &AttachProviderConfigTransformer{Module: b.Module}, // Provisioner-related transformations - &MissingProvisionerTransformer{Provisioners: b.Provisioners}, - &ProvisionerTransformer{}, + GraphTransformIf( + func() bool { return !b.Destroy }, + GraphTransformMulti( + &MissingProvisionerTransformer{Provisioners: b.Provisioners}, + &ProvisionerTransformer{}, + ), + ), // Add root variables &RootVariableTransformer{Module: b.Module}, diff --git a/terraform/graph_builder_destroy_apply.go b/terraform/graph_builder_destroy_apply.go deleted file mode 100644 index 2246e70b9..000000000 --- a/terraform/graph_builder_destroy_apply.go +++ /dev/null @@ -1,110 +0,0 @@ -package terraform - -import ( - "github.com/hashicorp/terraform/config/module" - "github.com/hashicorp/terraform/dag" -) - -// DestroyApplyGraphBuilder implements GraphBuilder and is responsible for -// applying a pure-destroy plan. -// -// This graph builder is very similar to the ApplyGraphBuilder but -// is slightly simpler. -type DestroyApplyGraphBuilder struct { - // Module is the root module for the graph to build. - Module *module.Tree - - // Diff is the diff to apply. - Diff *Diff - - // State is the current state - State *State - - // Providers is the list of providers supported. - Providers []string - - // DisableReduce, if true, will not reduce the graph. Great for testing. - DisableReduce bool -} - -// See GraphBuilder -func (b *DestroyApplyGraphBuilder) Build(path []string) (*Graph, error) { - return (&BasicGraphBuilder{ - Steps: b.Steps(), - Validate: true, - }).Build(path) -} - -// See GraphBuilder -func (b *DestroyApplyGraphBuilder) Steps() []GraphTransformer { - // Custom factory for creating providers. - providerFactory := func(name string, path []string) GraphNodeProvider { - return &NodeApplyableProvider{ - NameValue: name, - PathValue: path, - } - } - - concreteResource := func(a *NodeAbstractResource) dag.Vertex { - return &NodeApplyableResource{ - NodeAbstractResource: a, - } - } - - steps := []GraphTransformer{ - // Creates all the nodes represented in the diff. - &DiffTransformer{ - Concrete: concreteResource, - - Diff: b.Diff, - Module: b.Module, - State: b.State, - }, - - // Create orphan output nodes - &OrphanOutputTransformer{Module: b.Module, State: b.State}, - - // Attach the configuration to any resources - &AttachResourceConfigTransformer{Module: b.Module}, - - // Attach the state - &AttachStateTransformer{State: b.State}, - - // Destruction ordering. NOTE: For destroys, we don't need to - // do any CBD stuff, so that is explicitly not here. - &DestroyEdgeTransformer{Module: b.Module, State: b.State}, - - // Create all the providers - &MissingProviderTransformer{Providers: b.Providers, Factory: providerFactory}, - &ProviderTransformer{}, - &DisableProviderTransformer{}, - &ParentProviderTransformer{}, - &AttachProviderConfigTransformer{Module: b.Module}, - - // Add root variables - &RootVariableTransformer{Module: b.Module}, - - // Add module variables - &ModuleVariableTransformer{Module: b.Module}, - - // Add the outputs - &OutputTransformer{Module: b.Module}, - - // Connect references so ordering is correct - &ReferenceTransformer{}, - - // 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 -} diff --git a/terraform/transform.go b/terraform/transform.go index ca5736940..f4a431a67 100644 --- a/terraform/transform.go +++ b/terraform/transform.go @@ -19,3 +19,34 @@ type GraphTransformer interface { type GraphVertexTransformer interface { Transform(dag.Vertex) (dag.Vertex, error) } + +// GraphTransformIf is a helper function that conditionally returns a +// GraphTransformer given. This is useful for calling inline a sequence +// of transforms without having to split it up into multiple append() calls. +func GraphTransformIf(f func() bool, then GraphTransformer) GraphTransformer { + if f() { + return then + } + + return nil +} + +type graphTransformerMulti struct { + Transforms []GraphTransformer +} + +func (t *graphTransformerMulti) Transform(g *Graph) error { + for _, t := range t.Transforms { + if err := t.Transform(g); err != nil { + return err + } + } + + return nil +} + +// GraphTransformMulti combines multiple graph transformers into a single +// GraphTransformer that runs all the individual graph transformers. +func GraphTransformMulti(ts ...GraphTransformer) GraphTransformer { + return &graphTransformerMulti{Transforms: ts} +}