terraform: implement destroy planning basics from state
This commit is contained in:
parent
db807f4b0f
commit
0ed896a313
|
@ -516,8 +516,19 @@ func (c *Context) Plan() (*Plan, error) {
|
||||||
c.diff.init()
|
c.diff.init()
|
||||||
c.diffLock.Unlock()
|
c.diffLock.Unlock()
|
||||||
|
|
||||||
// Build the graph
|
// Build the graph. We have a branch here since for the pure-destroy
|
||||||
graph, err := c.Graph(&ContextGraphOpts{Validate: true})
|
// plan (c.destroy) we use a much simpler graph builder that simply
|
||||||
|
// walks the state and reverses edges.
|
||||||
|
var graph *Graph
|
||||||
|
var err error
|
||||||
|
if c.destroy && X_newDestroy {
|
||||||
|
graph, err = (&DestroyPlanGraphBuilder{
|
||||||
|
Module: c.module,
|
||||||
|
State: c.state,
|
||||||
|
}).Build(RootModulePath)
|
||||||
|
} else {
|
||||||
|
graph, err = c.Graph(&ContextGraphOpts{Validate: true})
|
||||||
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -540,11 +551,16 @@ func (c *Context) Plan() (*Plan, error) {
|
||||||
p.Diff.DeepCopy()
|
p.Diff.DeepCopy()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// We don't do the reverification during the new destroy plan because
|
||||||
|
// it will use a different apply process.
|
||||||
|
if !(c.destroy && X_newDestroy) {
|
||||||
// Now that we have a diff, we can build the exact graph that Apply will use
|
// Now that we have a diff, we can build the exact graph that Apply will use
|
||||||
// and catch any possible cycles during the Plan phase.
|
// and catch any possible cycles during the Plan phase.
|
||||||
if _, err := c.Graph(&ContextGraphOpts{Validate: true}); err != nil {
|
if _, err := c.Graph(&ContextGraphOpts{Validate: true}); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var errs error
|
var errs error
|
||||||
if len(walker.ValidationErrors) > 0 {
|
if len(walker.ValidationErrors) > 0 {
|
||||||
errs = multierror.Append(errs, walker.ValidationErrors...)
|
errs = multierror.Append(errs, walker.ValidationErrors...)
|
||||||
|
|
|
@ -0,0 +1,49 @@
|
||||||
|
package terraform
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/hashicorp/terraform/config/module"
|
||||||
|
"github.com/hashicorp/terraform/dag"
|
||||||
|
)
|
||||||
|
|
||||||
|
// DestroyPlanGraphBuilder implements GraphBuilder and is responsible for
|
||||||
|
// planning a pure-destroy.
|
||||||
|
//
|
||||||
|
// Planning a pure destroy operation is simple because we can ignore most
|
||||||
|
// ordering configuration and simply reverse the state.
|
||||||
|
type DestroyPlanGraphBuilder struct {
|
||||||
|
// Module is the root module for the graph to build.
|
||||||
|
Module *module.Tree
|
||||||
|
|
||||||
|
// State is the current state
|
||||||
|
State *State
|
||||||
|
}
|
||||||
|
|
||||||
|
// See GraphBuilder
|
||||||
|
func (b *DestroyPlanGraphBuilder) Build(path []string) (*Graph, error) {
|
||||||
|
return (&BasicGraphBuilder{
|
||||||
|
Steps: b.Steps(),
|
||||||
|
Validate: true,
|
||||||
|
}).Build(path)
|
||||||
|
}
|
||||||
|
|
||||||
|
// See GraphBuilder
|
||||||
|
func (b *DestroyPlanGraphBuilder) Steps() []GraphTransformer {
|
||||||
|
concreteResource := func(a *NodeAbstractResource) dag.Vertex {
|
||||||
|
return &NodePlanDestroyableResource{
|
||||||
|
NodeAbstractResource: a,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
steps := []GraphTransformer{
|
||||||
|
// Creates all the nodes represented in the state.
|
||||||
|
&StateTransformer{
|
||||||
|
Concrete: concreteResource,
|
||||||
|
State: b.State,
|
||||||
|
},
|
||||||
|
|
||||||
|
// Single root
|
||||||
|
&RootTransformer{},
|
||||||
|
}
|
||||||
|
|
||||||
|
return steps
|
||||||
|
}
|
|
@ -0,0 +1,55 @@
|
||||||
|
package terraform
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
// NodePlanDestroyableResource represents a resource that is "applyable":
|
||||||
|
// it is ready to be applied and is represented by a diff.
|
||||||
|
type NodePlanDestroyableResource struct {
|
||||||
|
*NodeAbstractResource
|
||||||
|
}
|
||||||
|
|
||||||
|
// GraphNodeEvalable
|
||||||
|
func (n *NodePlanDestroyableResource) EvalTree() EvalNode {
|
||||||
|
addr := n.NodeAbstractResource.Addr
|
||||||
|
|
||||||
|
// stateId is the ID to put into the state
|
||||||
|
stateId := addr.stateId()
|
||||||
|
if addr.Index > -1 {
|
||||||
|
stateId = fmt.Sprintf("%s.%d", stateId, addr.Index)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build the instance info. More of this will be populated during eval
|
||||||
|
info := &InstanceInfo{
|
||||||
|
Id: stateId,
|
||||||
|
Type: addr.Type,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Declare a bunch of variables that are used for state during
|
||||||
|
// evaluation. Most of this are written to by-address below.
|
||||||
|
var diff *InstanceDiff
|
||||||
|
var state *InstanceState
|
||||||
|
|
||||||
|
return &EvalSequence{
|
||||||
|
Nodes: []EvalNode{
|
||||||
|
&EvalReadState{
|
||||||
|
Name: stateId,
|
||||||
|
Output: &state,
|
||||||
|
},
|
||||||
|
&EvalDiffDestroy{
|
||||||
|
Info: info,
|
||||||
|
State: &state,
|
||||||
|
Output: &diff,
|
||||||
|
},
|
||||||
|
&EvalCheckPreventDestroy{
|
||||||
|
Resource: n.Config,
|
||||||
|
Diff: &diff,
|
||||||
|
},
|
||||||
|
&EvalWriteDiff{
|
||||||
|
Name: stateId,
|
||||||
|
Diff: &diff,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,65 @@
|
||||||
|
package terraform
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
|
||||||
|
"github.com/hashicorp/terraform/dag"
|
||||||
|
)
|
||||||
|
|
||||||
|
// StateTransformer is a GraphTransformer that adds the elements of
|
||||||
|
// the state to the graph.
|
||||||
|
//
|
||||||
|
// This transform is used for example by the DestroyPlanGraphBuilder to ensure
|
||||||
|
// that only resources that are in the state are represented in the graph.
|
||||||
|
type StateTransformer struct {
|
||||||
|
Concrete ConcreteResourceNodeFunc
|
||||||
|
|
||||||
|
State *State
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *StateTransformer) Transform(g *Graph) error {
|
||||||
|
// If the state is nil or empty (nil is empty) then do nothing
|
||||||
|
if t.State.Empty() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Go through all the modules in the diff.
|
||||||
|
log.Printf("[TRACE] StateTransformer: starting")
|
||||||
|
var nodes []dag.Vertex
|
||||||
|
for _, ms := range t.State.Modules {
|
||||||
|
log.Printf("[TRACE] StateTransformer: Module: %v", ms.Path)
|
||||||
|
|
||||||
|
// Go through all the resources in this module.
|
||||||
|
for name, rs := range ms.Resources {
|
||||||
|
log.Printf("[TRACE] StateTransformer: Resource %q: %#v", name, rs)
|
||||||
|
|
||||||
|
// Add the resource to the graph
|
||||||
|
addr, err := parseResourceAddressInternal(name)
|
||||||
|
if err != nil {
|
||||||
|
panic(fmt.Sprintf(
|
||||||
|
"Error parsing internal name, this is a bug: %q", name))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Very important: add the module path for this resource to
|
||||||
|
// the address. Remove "root" from it.
|
||||||
|
addr.Path = ms.Path[1:]
|
||||||
|
|
||||||
|
// Add the resource to the graph
|
||||||
|
abstract := &NodeAbstractResource{Addr: addr}
|
||||||
|
var node dag.Vertex = abstract
|
||||||
|
if f := t.Concrete; f != nil {
|
||||||
|
node = f(abstract)
|
||||||
|
}
|
||||||
|
|
||||||
|
nodes = append(nodes, node)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add all the nodes to the graph
|
||||||
|
for _, n := range nodes {
|
||||||
|
g.Add(n)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
Loading…
Reference in New Issue