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")
|
m := testModule(t, "apply-destroy")
|
||||||
h := new(HookRecordApplyOrder)
|
h := new(HookRecordApplyOrder)
|
||||||
p := testProvider("aws")
|
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
|
// https://github.com/hashicorp/terraform/issues/2767
|
||||||
func TestContext2Apply_destroyModulePrefix(t *testing.T) {
|
func TestContext2Apply_destroyModulePrefix(t *testing.T) {
|
||||||
m := testModule(t, "apply-destroy-module-resource-prefix")
|
m := testModule(t, "apply-destroy-module-resource-prefix")
|
||||||
|
|
|
@ -54,6 +54,9 @@ func (b *ApplyGraphBuilder) Steps() []GraphTransformer {
|
||||||
State: b.State,
|
State: b.State,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// Attach the configuration to any resources
|
||||||
|
&AttachResourceConfigTransformer{Module: b.Module},
|
||||||
|
|
||||||
// Create orphan output nodes
|
// Create orphan output nodes
|
||||||
&OrphanOutputTransformer{Module: b.Module, State: b.State},
|
&OrphanOutputTransformer{Module: b.Module, State: b.State},
|
||||||
|
|
||||||
|
|
|
@ -91,6 +91,11 @@ func (n *NodeApplyableResource) ResourceAddr() *ResourceAddress {
|
||||||
return n.Addr
|
return n.Addr
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GraphNodeAttachResource
|
||||||
|
func (n *NodeApplyableResource) AttachResourceConfig(c *config.Resource) {
|
||||||
|
n.Config = c
|
||||||
|
}
|
||||||
|
|
||||||
// GraphNodeAttachResourceState
|
// GraphNodeAttachResourceState
|
||||||
func (n *NodeApplyableResource) AttachResourceState(s *ResourceState) {
|
func (n *NodeApplyableResource) AttachResourceState(s *ResourceState) {
|
||||||
n.ResourceState = s
|
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
|
// Add all the nodes to the graph
|
||||||
for _, n := range nodes {
|
for _, n := range nodes {
|
||||||
g.Add(n)
|
g.Add(n)
|
||||||
|
|
Loading…
Reference in New Issue