terraform: destroy graph builder, -Xnew-destroy flag

This commit is contained in:
Mitchell Hashimoto 2016-10-16 18:28:02 -07:00
parent bff4e31070
commit db807f4b0f
No known key found for this signature in database
GPG Key ID: 744E147AA52F5B0A
3 changed files with 140 additions and 18 deletions

View File

@ -21,6 +21,10 @@ var (
// X_newApply will enable the new apply graph. This will be removed // X_newApply will enable the new apply graph. This will be removed
// and be on by default in 0.8.0. // and be on by default in 0.8.0.
X_newApply = false X_newApply = false
// X_newDestroy will enable the new destroy graph. This will be removed
// and be on by default in 0.8.0.
X_newDestroy = false
) )
// InputMode defines what sort of input will be asked for when Input // InputMode defines what sort of input will be asked for when Input
@ -371,6 +375,8 @@ func (c *Context) Apply() (*State, error) {
// Copy our own state // Copy our own state
c.state = c.state.DeepCopy() c.state = c.state.DeepCopy()
newGraphEnabled := (c.destroy && X_newDestroy) || (!c.destroy && X_newApply)
// Build the original graph. This is before the new graph builders // Build the original graph. This is before the new graph builders
// coming in 0.8. We do this for shadow graphing. // coming in 0.8. We do this for shadow graphing.
oldGraph, err := c.Graph(&ContextGraphOpts{Validate: true}) oldGraph, err := c.Graph(&ContextGraphOpts{Validate: true})
@ -386,18 +392,28 @@ func (c *Context) Apply() (*State, error) {
} }
// Build the new graph. We do this no matter what so we can shadow it. // Build the new graph. We do this no matter what so we can shadow it.
newGraph, err := (&ApplyGraphBuilder{ var newGraph *Graph
Module: c.module, if c.destroy {
Diff: c.diff, newGraph, err = (&DestroyApplyGraphBuilder{
State: c.state, Module: c.module,
Providers: c.components.ResourceProviders(), Diff: c.diff,
Provisioners: c.components.ResourceProvisioners(), State: c.state,
}).Build(RootModulePath) Providers: c.providersList(),
if err != nil && !X_newApply { }).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)
}
if err != nil && !newGraphEnabled {
// If we had an error graphing but we're not using this graph, just // 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. // set it to nil and record it as a shadow error.
c.shadowErr = multierror.Append(c.shadowErr, fmt.Errorf( c.shadowErr = multierror.Append(c.shadowErr, fmt.Errorf(
"Error building new apply graph: %s", err)) "Error building new graph: %s", err))
newGraph = nil newGraph = nil
err = nil err = nil
@ -418,16 +434,11 @@ func (c *Context) Apply() (*State, error) {
// //
real := oldGraph real := oldGraph
shadow := newGraph shadow := newGraph
if c.destroy { if newGraphEnabled {
log.Printf("[WARN] terraform: real graph is original, shadow is nil") log.Printf("[WARN] terraform: real graph is experiment, shadow is experiment")
shadow = nil real = shadow
} else { } else {
if X_newApply { log.Printf("[WARN] terraform: real graph is original, shadow is experiment")
log.Printf("[WARN] terraform: real graph is Xnew-apply, shadow is Xnew-apply")
real = shadow
} else {
log.Printf("[WARN] terraform: real graph is original, shadow is Xnew-apply")
}
} }
// For now, always shadow with the real graph for verification. We don't // For now, always shadow with the real graph for verification. We don't

View File

@ -0,0 +1,109 @@
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{},
&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
}

View File

@ -24,6 +24,7 @@ const fixtureDir = "./test-fixtures"
func TestMain(m *testing.M) { func TestMain(m *testing.M) {
// Experimental features // Experimental features
xNewApply := flag.Bool("Xnew-apply", false, "Experiment: new apply graph") xNewApply := flag.Bool("Xnew-apply", false, "Experiment: new apply graph")
xNewDestroy := flag.Bool("Xnew-destroy", false, "Experiment: new destroy graph")
// Normal features // Normal features
shadow := flag.Bool("shadow", true, "Enable shadow graph") shadow := flag.Bool("shadow", true, "Enable shadow graph")
@ -32,6 +33,7 @@ func TestMain(m *testing.M) {
// Setup experimental features // Setup experimental features
X_newApply = *xNewApply X_newApply = *xNewApply
X_newDestroy = *xNewDestroy
if testing.Verbose() { if testing.Verbose() {
// if we're verbose, use the logging requested by TF_LOG // if we're verbose, use the logging requested by TF_LOG