]> git.immae.eu Git - github/fretlink/terraform-provider-statuscake.git/blobdiff - vendor/github.com/hashicorp/terraform/terraform/transform_destroy_cbd.go
Upgrade to 0.12
[github/fretlink/terraform-provider-statuscake.git] / vendor / github.com / hashicorp / terraform / terraform / transform_destroy_cbd.go
index edfb460bfd54de2f7716abcd2fb293640bc9a211..2f4d5edeba9926d7b272eeb9eaa571bde250e926 100644 (file)
@@ -4,15 +4,15 @@ import (
        "fmt"
        "log"
 
-       "github.com/hashicorp/terraform/config/module"
+       "github.com/hashicorp/terraform/configs"
        "github.com/hashicorp/terraform/dag"
+       "github.com/hashicorp/terraform/states"
 )
 
 // GraphNodeDestroyerCBD must be implemented by nodes that might be
-// create-before-destroy destroyers.
+// create-before-destroy destroyers, or might plan a create-before-destroy
+// action.
 type GraphNodeDestroyerCBD interface {
-       GraphNodeDestroyer
-
        // CreateBeforeDestroy returns true if this node represents a node
        // that is doing a CBD.
        CreateBeforeDestroy() bool
@@ -23,6 +23,89 @@ type GraphNodeDestroyerCBD interface {
        ModifyCreateBeforeDestroy(bool) error
 }
 
+// GraphNodeAttachDestroyer is implemented by applyable nodes that have a
+// companion destroy node. This allows the creation node to look up the status
+// of the destroy node and determine if it needs to depose the existing state,
+// or replace it.
+// If a node is not marked as create-before-destroy in the configuration, but a
+// dependency forces that status, only the destroy node will be aware of that
+// status.
+type GraphNodeAttachDestroyer interface {
+       // AttachDestroyNode takes a destroy node and saves a reference to that
+       // node in the receiver, so it can later check the status of
+       // CreateBeforeDestroy().
+       AttachDestroyNode(n GraphNodeDestroyerCBD)
+}
+
+// ForcedCBDTransformer detects when a particular CBD-able graph node has
+// dependencies with another that has create_before_destroy set that require
+// it to be forced on, and forces it on.
+//
+// This must be used in the plan graph builder to ensure that
+// create_before_destroy settings are properly propagated before constructing
+// the planned changes. This requires that the plannable resource nodes
+// implement GraphNodeDestroyerCBD.
+type ForcedCBDTransformer struct {
+}
+
+func (t *ForcedCBDTransformer) Transform(g *Graph) error {
+       for _, v := range g.Vertices() {
+               dn, ok := v.(GraphNodeDestroyerCBD)
+               if !ok {
+                       continue
+               }
+
+               if !dn.CreateBeforeDestroy() {
+                       // If there are no CBD decendent (dependent nodes), then we
+                       // do nothing here.
+                       if !t.hasCBDDescendent(g, v) {
+                               log.Printf("[TRACE] ForcedCBDTransformer: %q (%T) has no CBD descendent, so skipping", dag.VertexName(v), v)
+                               continue
+                       }
+
+                       // If this isn't naturally a CBD node, this means that an descendent is
+                       // and we need to auto-upgrade this node to CBD. We do this because
+                       // a CBD node depending on non-CBD will result in cycles. To avoid this,
+                       // we always attempt to upgrade it.
+                       log.Printf("[TRACE] ForcedCBDTransformer: forcing create_before_destroy on for %q (%T)", dag.VertexName(v), v)
+                       if err := dn.ModifyCreateBeforeDestroy(true); err != nil {
+                               return fmt.Errorf(
+                                       "%s: must have create before destroy enabled because "+
+                                               "a dependent resource has CBD enabled. However, when "+
+                                               "attempting to automatically do this, an error occurred: %s",
+                                       dag.VertexName(v), err)
+                       }
+               } else {
+                       log.Printf("[TRACE] ForcedCBDTransformer: %q (%T) already has create_before_destroy set", dag.VertexName(v), v)
+               }
+       }
+       return nil
+}
+
+// hasCBDDescendent returns true if any descendent (node that depends on this)
+// has CBD set.
+func (t *ForcedCBDTransformer) hasCBDDescendent(g *Graph, v dag.Vertex) bool {
+       s, _ := g.Descendents(v)
+       if s == nil {
+               return true
+       }
+
+       for _, ov := range s.List() {
+               dn, ok := ov.(GraphNodeDestroyerCBD)
+               if !ok {
+                       continue
+               }
+
+               if dn.CreateBeforeDestroy() {
+                       // some descendent is CreateBeforeDestroy, so we need to follow suit
+                       log.Printf("[TRACE] ForcedCBDTransformer: %q has CBD descendent %q", dag.VertexName(v), dag.VertexName(ov))
+                       return true
+               }
+       }
+
+       return false
+}
+
 // CBDEdgeTransformer modifies the edges of CBD nodes that went through
 // the DestroyEdgeTransformer to have the right dependencies. There are
 // two real tasks here:
@@ -35,16 +118,25 @@ type GraphNodeDestroyerCBD interface {
 //      update to A. Example: adding a web server updates the load balancer
 //      before deleting the old web server.
 //
+// This transformer requires that a previous transformer has already forced
+// create_before_destroy on for nodes that are depended on by explicit CBD
+// nodes. This is the logic in ForcedCBDTransformer, though in practice we
+// will get here by recording the CBD-ness of each change in the plan during
+// the plan walk and then forcing the nodes into the appropriate setting during
+// DiffTransformer when building the apply graph.
 type CBDEdgeTransformer struct {
        // Module and State are only needed to look up dependencies in
        // any way possible. Either can be nil if not availabile.
-       Module *module.Tree
-       State  *State
+       Config *configs.Config
+       State  *states.State
+
+       // If configuration is present then Schemas is required in order to
+       // obtain schema information from providers and provisioners so we can
+       // properly resolve implicit dependencies.
+       Schemas *Schemas
 }
 
 func (t *CBDEdgeTransformer) Transform(g *Graph) error {
-       log.Printf("[TRACE] CBDEdgeTransformer: Beginning CBD transformation...")
-
        // Go through and reverse any destroy edges
        destroyMap := make(map[string][]dag.Vertex)
        for _, v := range g.Vertices() {
@@ -52,25 +144,13 @@ func (t *CBDEdgeTransformer) Transform(g *Graph) error {
                if !ok {
                        continue
                }
+               dern, ok := v.(GraphNodeDestroyer)
+               if !ok {
+                       continue
+               }
 
                if !dn.CreateBeforeDestroy() {
-                       // If there are no CBD ancestors (dependent nodes), then we
-                       // do nothing here.
-                       if !t.hasCBDAncestor(g, v) {
-                               continue
-                       }
-
-                       // If this isn't naturally a CBD node, this means that an ancestor is
-                       // and we need to auto-upgrade this node to CBD. We do this because
-                       // a CBD node depending on non-CBD will result in cycles. To avoid this,
-                       // we always attempt to upgrade it.
-                       if err := dn.ModifyCreateBeforeDestroy(true); err != nil {
-                               return fmt.Errorf(
-                                       "%s: must have create before destroy enabled because "+
-                                               "a dependent resource has CBD enabled. However, when "+
-                                               "attempting to automatically do this, an error occurred: %s",
-                                       dag.VertexName(v), err)
-                       }
+                       continue
                }
 
                // Find the destroy edge. There should only be one.
@@ -86,7 +166,9 @@ func (t *CBDEdgeTransformer) Transform(g *Graph) error {
 
                        // Found it! Invert.
                        g.RemoveEdge(de)
-                       g.Connect(&DestroyEdge{S: de.Target(), T: de.Source()})
+                       applyNode := de.Source()
+                       destroyNode := de.Target()
+                       g.Connect(&DestroyEdge{S: destroyNode, T: applyNode})
                }
 
                // If the address has an index, we strip that. Our depMap creation
@@ -94,15 +176,11 @@ func (t *CBDEdgeTransformer) Transform(g *Graph) error {
                // dependencies. One day when we limit dependencies more exactly
                // this will have to change. We have a test case covering this
                // (depNonCBDCountBoth) so it'll be caught.
-               addr := dn.DestroyAddr()
-               if addr.Index >= 0 {
-                       addr = addr.Copy() // Copy so that we don't modify any pointers
-                       addr.Index = -1
-               }
+               addr := dern.DestroyAddr()
+               key := addr.ContainingResource().String()
 
                // Add this to the list of nodes that we need to fix up
                // the edges for (step 2 above in the docs).
-               key := addr.String()
                destroyMap[key] = append(destroyMap[key], v)
        }
 
@@ -151,13 +229,9 @@ func (t *CBDEdgeTransformer) Transform(g *Graph) error {
                // dependencies. One day when we limit dependencies more exactly
                // this will have to change. We have a test case covering this
                // (depNonCBDCount) so it'll be caught.
-               if addr.Index >= 0 {
-                       addr = addr.Copy() // Copy so that we don't modify any pointers
-                       addr.Index = -1
-               }
+               key := addr.ContainingResource().String()
 
                // If there is nothing this resource should depend on, ignore it
-               key := addr.String()
                dns, ok := depMap[key]
                if !ok {
                        continue
@@ -174,21 +248,21 @@ func (t *CBDEdgeTransformer) Transform(g *Graph) error {
        return nil
 }
 
-func (t *CBDEdgeTransformer) depMap(
-       destroyMap map[string][]dag.Vertex) (map[string][]dag.Vertex, error) {
+func (t *CBDEdgeTransformer) depMap(destroyMap map[string][]dag.Vertex) (map[string][]dag.Vertex, error) {
        // Build the graph of our config, this ensures that all resources
        // are present in the graph.
-       g, err := (&BasicGraphBuilder{
+       g, diags := (&BasicGraphBuilder{
                Steps: []GraphTransformer{
-                       &FlatConfigTransformer{Module: t.Module},
-                       &AttachResourceConfigTransformer{Module: t.Module},
+                       &FlatConfigTransformer{Config: t.Config},
+                       &AttachResourceConfigTransformer{Config: t.Config},
                        &AttachStateTransformer{State: t.State},
+                       &AttachSchemaTransformer{Schemas: t.Schemas},
                        &ReferenceTransformer{},
                },
                Name: "CBDEdgeTransformer",
        }).Build(nil)
-       if err != nil {
-               return nil, err
+       if diags.HasErrors() {
+               return nil, diags.Err()
        }
 
        // Using this graph, build the list of destroy nodes that each resource
@@ -232,26 +306,3 @@ func (t *CBDEdgeTransformer) depMap(
 
        return depMap, nil
 }
-
-// hasCBDAncestor returns true if any ancestor (node that depends on this)
-// has CBD set.
-func (t *CBDEdgeTransformer) hasCBDAncestor(g *Graph, v dag.Vertex) bool {
-       s, _ := g.Ancestors(v)
-       if s == nil {
-               return true
-       }
-
-       for _, v := range s.List() {
-               dn, ok := v.(GraphNodeDestroyerCBD)
-               if !ok {
-                       continue
-               }
-
-               if dn.CreateBeforeDestroy() {
-                       // some ancestor is CreateBeforeDestroy, so we need to follow suit
-                       return true
-               }
-       }
-
-       return false
-}