mirror of https://github.com/hashicorp/terraform
Merge pull request #9527 from hashicorp/f-destroy-builder2
terraform: destroy graph builder based on statepull/9633/head
commit
aed23a0a31
@ -1 +1 @@
|
||||
go test ./terraform | grep -E '(FAIL|panic)' | tee /dev/tty | wc -l
|
||||
go test ./terraform -Xnew-apply -Xnew-destroy | grep -E '(FAIL|panic)' | tee /dev/tty | wc -l
|
||||
|
||||
@ -0,0 +1,58 @@
|
||||
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
|
||||
|
||||
// Targets are resources to target
|
||||
Targets []string
|
||||
}
|
||||
|
||||
// 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,
|
||||
},
|
||||
|
||||
// Target
|
||||
&TargetsTransformer{Targets: b.Targets},
|
||||
|
||||
// Attach the configuration to any resources
|
||||
&AttachResourceConfigTransformer{Module: b.Module},
|
||||
|
||||
// Single root
|
||||
&RootTransformer{},
|
||||
}
|
||||
|
||||
return steps
|
||||
}
|
||||
@ -0,0 +1,29 @@
|
||||
package terraform
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// NodeDestroyableModule represents a module destruction.
|
||||
type NodeDestroyableModuleVariable struct {
|
||||
PathValue []string
|
||||
}
|
||||
|
||||
func (n *NodeDestroyableModuleVariable) Name() string {
|
||||
result := "plan-destroy"
|
||||
if len(n.PathValue) > 1 {
|
||||
result = fmt.Sprintf("%s.%s", modulePrefixStr(n.PathValue), result)
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// GraphNodeSubPath
|
||||
func (n *NodeDestroyableModuleVariable) Path() []string {
|
||||
return n.PathValue
|
||||
}
|
||||
|
||||
// GraphNodeEvalable
|
||||
func (n *NodeDestroyableModuleVariable) EvalTree() EvalNode {
|
||||
return &EvalDiffDestroyModule{Path: n.PathValue}
|
||||
}
|
||||
@ -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