terraform: transform to attach resource configs
This commit is contained in:
parent
ceb5c53d56
commit
bd5d97f9f5
|
@ -2789,7 +2789,7 @@ func TestContext2Apply_Provisioner_ConnInfo(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestContext2Apply_destroy(t *testing.T) {
|
||||
func TestContext2Apply_destroyX(t *testing.T) {
|
||||
m := testModule(t, "apply-destroy")
|
||||
h := new(HookRecordApplyOrder)
|
||||
p := testProvider("aws")
|
||||
|
@ -2849,6 +2849,65 @@ func TestContext2Apply_destroy(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestContext2Apply_destroyOrder(t *testing.T) {
|
||||
m := testModule(t, "apply-destroy")
|
||||
h := new(HookRecordApplyOrder)
|
||||
p := testProvider("aws")
|
||||
p.ApplyFn = testApplyFn
|
||||
p.DiffFn = testDiffFn
|
||||
ctx := testContext2(t, &ContextOpts{
|
||||
Module: m,
|
||||
Hooks: []Hook{h},
|
||||
Providers: map[string]ResourceProviderFactory{
|
||||
"aws": testProviderFuncFixed(p),
|
||||
},
|
||||
})
|
||||
|
||||
// First plan and apply a create operation
|
||||
if _, err := ctx.Plan(); err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
|
||||
state, err := ctx.Apply()
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
|
||||
// Next, plan and apply config-less to force a destroy with "apply"
|
||||
h.Active = true
|
||||
ctx = testContext2(t, &ContextOpts{
|
||||
State: state,
|
||||
Module: module.NewEmptyTree(),
|
||||
Hooks: []Hook{h},
|
||||
Providers: map[string]ResourceProviderFactory{
|
||||
"aws": testProviderFuncFixed(p),
|
||||
},
|
||||
})
|
||||
|
||||
if _, err := ctx.Plan(); err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
|
||||
state, err = ctx.Apply()
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
|
||||
// Test that things were destroyed
|
||||
actual := strings.TrimSpace(state.String())
|
||||
expected := strings.TrimSpace(testTerraformApplyDestroyStr)
|
||||
if actual != expected {
|
||||
t.Fatalf("bad: \n%s", actual)
|
||||
}
|
||||
|
||||
// Test that things were destroyed _in the right order_
|
||||
expected2 := []string{"aws_instance.bar", "aws_instance.foo"}
|
||||
actual2 := h.IDs
|
||||
if !reflect.DeepEqual(actual2, expected2) {
|
||||
t.Fatalf("expected: %#v\n\ngot:%#v", expected2, actual2)
|
||||
}
|
||||
}
|
||||
|
||||
// https://github.com/hashicorp/terraform/issues/2767
|
||||
func TestContext2Apply_destroyModulePrefix(t *testing.T) {
|
||||
m := testModule(t, "apply-destroy-module-resource-prefix")
|
||||
|
|
|
@ -54,6 +54,9 @@ func (b *ApplyGraphBuilder) Steps() []GraphTransformer {
|
|||
State: b.State,
|
||||
},
|
||||
|
||||
// Attach the configuration to any resources
|
||||
&AttachResourceConfigTransformer{Module: b.Module},
|
||||
|
||||
// Create orphan output nodes
|
||||
&OrphanOutputTransformer{Module: b.Module, State: b.State},
|
||||
|
||||
|
|
|
@ -91,6 +91,11 @@ func (n *NodeApplyableResource) ResourceAddr() *ResourceAddress {
|
|||
return n.Addr
|
||||
}
|
||||
|
||||
// GraphNodeAttachResource
|
||||
func (n *NodeApplyableResource) AttachResourceConfig(c *config.Resource) {
|
||||
n.Config = c
|
||||
}
|
||||
|
||||
// GraphNodeAttachResourceState
|
||||
func (n *NodeApplyableResource) AttachResourceState(s *ResourceState) {
|
||||
n.ResourceState = s
|
||||
|
|
|
@ -0,0 +1,74 @@
|
|||
package terraform
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
|
||||
"github.com/hashicorp/terraform/config"
|
||||
"github.com/hashicorp/terraform/config/module"
|
||||
)
|
||||
|
||||
// GraphNodeAttachResourceConfig is an interface that must be implemented by nodes
|
||||
// that want resource configurations attached.
|
||||
type GraphNodeAttachResourceConfig interface {
|
||||
// ResourceAddr is the address to the resource
|
||||
ResourceAddr() *ResourceAddress
|
||||
|
||||
// Sets the configuration
|
||||
AttachResourceConfig(*config.Resource)
|
||||
}
|
||||
|
||||
// AttachResourceConfigTransformer goes through the graph and attaches
|
||||
// resource configuration structures to nodes that implement the interfaces
|
||||
// above.
|
||||
//
|
||||
// The attached configuration structures are directly from the configuration.
|
||||
// If they're going to be modified, a copy should be made.
|
||||
type AttachResourceConfigTransformer struct {
|
||||
Module *module.Tree // Module is the root module for the config
|
||||
}
|
||||
|
||||
func (t *AttachResourceConfigTransformer) Transform(g *Graph) error {
|
||||
// Go through and find GraphNodeAttachResource
|
||||
for _, v := range g.Vertices() {
|
||||
// Only care about GraphNodeAttachResource implementations
|
||||
arn, ok := v.(GraphNodeAttachResourceConfig)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
// Determine what we're looking for
|
||||
addr := arn.ResourceAddr()
|
||||
log.Printf("[TRACE] Attach resource request: %s", addr)
|
||||
|
||||
// Get the configuration.
|
||||
path := normalizeModulePath(addr.Path)
|
||||
path = path[1:]
|
||||
tree := t.Module.Child(path)
|
||||
if tree == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
// Go through the resource configs to find the matching config
|
||||
for _, r := range tree.Config().Resources {
|
||||
// Get a resource address so we can compare
|
||||
a, err := parseResourceAddressConfig(r)
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf(
|
||||
"Error parsing config address, this is a bug: %#v", r))
|
||||
}
|
||||
a.Path = addr.Path
|
||||
|
||||
// If this is not the same resource, then continue
|
||||
if !a.Equals(addr) {
|
||||
continue
|
||||
}
|
||||
|
||||
log.Printf("[TRACE] Attaching resource config: %#v", r)
|
||||
arn.AttachResourceConfig(r)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
|
@ -0,0 +1,64 @@
|
|||
package terraform
|
||||
|
||||
// GraphNodeDestroyer must be implemented by nodes that destroy resources.
|
||||
type GraphNodeDestroyer interface {
|
||||
// ResourceAddr is the address of the resource that is being
|
||||
// destroyed by this node. If this returns nil, then this node
|
||||
// is not destroying anything.
|
||||
DestroyAddr() *ResourceAddress
|
||||
}
|
||||
|
||||
// DestroyEdgeTransformer is a GraphTransformer that creates the proper
|
||||
// references for destroy resources. Destroy resources are more complex
|
||||
// in that they must be depend on the destruction of resources that
|
||||
// in turn depend on the CREATION of the node being destroy.
|
||||
//
|
||||
// That is complicated. Visually:
|
||||
//
|
||||
// B_d -> A_d -> A -> B
|
||||
//
|
||||
// Notice that A destroy depends on B destroy, while B create depends on
|
||||
// A create. They're inverted. This must be done for example because often
|
||||
// dependent resources will block parent resources from deleting. Concrete
|
||||
// example: VPC with subnets, the VPC can't be deleted while there are
|
||||
// still subnets.
|
||||
type DestroyEdgeTransformer struct{}
|
||||
|
||||
func (t *DestroyEdgeTransformer) Transform(g *Graph) error {
|
||||
// Build a map of what is being destroyed (by address string) to
|
||||
// the list of destroyers. In general there will only be one destroyer
|
||||
// but to make it more robust we support multiple.
|
||||
destroyers := make(map[string][]GraphNodeDestroyer)
|
||||
for _, v := range g.Vertices() {
|
||||
dn, ok := v.(GraphNodeDestroyer)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
addr := dn.DestroyAddr()
|
||||
if addr == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
key := addr.String()
|
||||
destroyers[key] = append(destroyers[key], dn)
|
||||
}
|
||||
|
||||
// If we aren't destroying anything, there will be no edges to make
|
||||
// so just exit early and avoid future work.
|
||||
if len(destroyers) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Go through the all destroyers and find what they're destroying.
|
||||
// Use this to find the dependencies, look up if any of them are being
|
||||
// destroyed, and to make the proper edge.
|
||||
for _, ds := range destroyers {
|
||||
for _, d := range ds {
|
||||
// TODO
|
||||
println(d)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
|
@ -69,34 +69,6 @@ func (t *DiffTransformer) Transform(g *Graph) error {
|
|||
}
|
||||
}
|
||||
|
||||
// NOTE: Lots of room for performance optimizations below. For
|
||||
// resource-heavy diffs this part alone is probably pretty slow.
|
||||
|
||||
// Annotate all nodes with their config and state
|
||||
for _, n := range nodes {
|
||||
// Grab the configuration at this path.
|
||||
if t := t.Module.Child(n.Addr.Path); t != nil {
|
||||
for _, r := range t.Config().Resources {
|
||||
// Get a resource address so we can compare
|
||||
addr, err := parseResourceAddressConfig(r)
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf(
|
||||
"Error parsing config address, this is a bug: %#v", r))
|
||||
}
|
||||
addr.Path = n.Addr.Path
|
||||
|
||||
// If this is not the same resource, then continue
|
||||
if !addr.Equals(n.Addr) {
|
||||
continue
|
||||
}
|
||||
|
||||
// Same resource! Mark it and exit
|
||||
n.Config = r
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Add all the nodes to the graph
|
||||
for _, n := range nodes {
|
||||
g.Add(n)
|
||||
|
|
Loading…
Reference in New Issue