]> git.immae.eu Git - github/fretlink/terraform-provider-statuscake.git/blobdiff - vendor/github.com/hashicorp/terraform/terraform/transform_diff.go
Upgrade to 0.12
[github/fretlink/terraform-provider-statuscake.git] / vendor / github.com / hashicorp / terraform / terraform / transform_diff.go
index ad46d3c61269df62bec8f935563e4bb9ed9264b3..6fb915f8719baa10fd1792c914212c2a0c4cde85 100644 (file)
@@ -4,83 +4,189 @@ import (
        "fmt"
        "log"
 
-       "github.com/hashicorp/terraform/config/module"
        "github.com/hashicorp/terraform/dag"
+       "github.com/hashicorp/terraform/plans"
+       "github.com/hashicorp/terraform/states"
+       "github.com/hashicorp/terraform/tfdiags"
 )
 
-// DiffTransformer is a GraphTransformer that adds the elements of
-// the diff to the graph.
-//
-// This transform is used for example by the ApplyGraphBuilder to ensure
-// that only resources that are being modified are represented in the graph.
-//
-// Module and State is still required for the DiffTransformer for annotations
-// since the Diff doesn't contain all the information required to build the
-// complete graph (such as create-before-destroy information). The graph
-// is built based on the diff first, though, ensuring that only resources
-// that are being modified are present in the graph.
+// DiffTransformer is a GraphTransformer that adds graph nodes representing
+// each of the resource changes described in the given Changes object.
 type DiffTransformer struct {
-       Concrete ConcreteResourceNodeFunc
-
-       Diff   *Diff
-       Module *module.Tree
-       State  *State
+       Concrete ConcreteResourceInstanceNodeFunc
+       State    *states.State
+       Changes  *plans.Changes
 }
 
 func (t *DiffTransformer) Transform(g *Graph) error {
-       // If the diff is nil or empty (nil is empty) then do nothing
-       if t.Diff.Empty() {
+       if t.Changes == nil || len(t.Changes.Resources) == 0 {
+               // Nothing to do!
                return nil
        }
 
        // Go through all the modules in the diff.
-       log.Printf("[TRACE] DiffTransformer: starting")
-       var nodes []dag.Vertex
-       for _, m := range t.Diff.Modules {
-               log.Printf("[TRACE] DiffTransformer: Module: %s", m)
-               // TODO: If this is a destroy diff then add a module destroy node
-
-               // Go through all the resources in this module.
-               for name, inst := range m.Resources {
-                       log.Printf("[TRACE] DiffTransformer: Resource %q: %#v", name, inst)
-
-                       // We have changes! This is a create or update operation.
-                       // First grab the address so we have a unique way to
-                       // reference this resource.
-                       addr, err := parseResourceAddressInternal(name)
-                       if err != nil {
-                               panic(fmt.Sprintf(
-                                       "Error parsing internal name, this is a bug: %q", name))
-                       }
+       log.Printf("[TRACE] DiffTransformer starting")
+
+       var diags tfdiags.Diagnostics
+       state := t.State
+       changes := t.Changes
+
+       // DiffTransformer creates resource _instance_ nodes. If there are any
+       // whole-resource nodes already in the graph, we must ensure that they
+       // get evaluated before any of the corresponding instances by creating
+       // dependency edges, so we'll do some prep work here to ensure we'll only
+       // create connections to nodes that existed before we started here.
+       resourceNodes := map[string][]GraphNodeResource{}
+       for _, node := range g.Vertices() {
+               rn, ok := node.(GraphNodeResource)
+               if !ok {
+                       continue
+               }
+               // We ignore any instances that _also_ implement
+               // GraphNodeResourceInstance, since in the unlikely event that they
+               // do exist we'd probably end up creating cycles by connecting them.
+               if _, ok := node.(GraphNodeResourceInstance); ok {
+                       continue
+               }
+
+               addr := rn.ResourceAddr().String()
+               resourceNodes[addr] = append(resourceNodes[addr], rn)
+       }
+
+       for _, rc := range changes.Resources {
+               addr := rc.Addr
+               dk := rc.DeposedKey
+
+               log.Printf("[TRACE] DiffTransformer: found %s change for %s %s", rc.Action, addr, dk)
+
+               // Depending on the action we'll need some different combinations of
+               // nodes, because destroying uses a special node type separate from
+               // other actions.
+               var update, delete, createBeforeDestroy bool
+               switch rc.Action {
+               case plans.NoOp:
+                       continue
+               case plans.Delete:
+                       delete = true
+               case plans.DeleteThenCreate, plans.CreateThenDelete:
+                       update = true
+                       delete = true
+                       createBeforeDestroy = (rc.Action == plans.CreateThenDelete)
+               default:
+                       update = true
+               }
+
+               if dk != states.NotDeposed && update {
+                       diags = diags.Append(tfdiags.Sourceless(
+                               tfdiags.Error,
+                               "Invalid planned change for deposed object",
+                               fmt.Sprintf("The plan contains a non-delete change for %s deposed object %s. The only valid action for a deposed object is to destroy it, so this is a bug in Terraform.", addr, dk),
+                       ))
+                       continue
+               }
 
-                       // Very important: add the module path for this resource to
-                       // the address. Remove "root" from it.
-                       addr.Path = m.Path[1:]
+               // If we're going to do a create_before_destroy Replace operation then
+               // we need to allocate a DeposedKey to use to retain the
+               // not-yet-destroyed prior object, so that the delete node can destroy
+               // _that_ rather than the newly-created node, which will be current
+               // by the time the delete node is visited.
+               if update && delete && createBeforeDestroy {
+                       // In this case, variable dk will be the _pre-assigned_ DeposedKey
+                       // that must be used if the update graph node deposes the current
+                       // instance, which will then align with the same key we pass
+                       // into the destroy node to ensure we destroy exactly the deposed
+                       // object we expect.
+                       if state != nil {
+                               ris := state.ResourceInstance(addr)
+                               if ris == nil {
+                                       // Should never happen, since we don't plan to replace an
+                                       // instance that doesn't exist yet.
+                                       diags = diags.Append(tfdiags.Sourceless(
+                                               tfdiags.Error,
+                                               "Invalid planned change",
+                                               fmt.Sprintf("The plan contains a replace change for %s, which doesn't exist yet. This is a bug in Terraform.", addr),
+                                       ))
+                                       continue
+                               }
+
+                               // Allocating a deposed key separately from using it can be racy
+                               // in general, but we assume here that nothing except the apply
+                               // node we instantiate below will actually make new deposed objects
+                               // in practice, and so the set of already-used keys will not change
+                               // between now and then.
+                               dk = ris.FindUnusedDeposedKey()
+                       } else {
+                               // If we have no state at all yet then we can use _any_
+                               // DeposedKey.
+                               dk = states.NewDeposedKey()
+                       }
+               }
 
-                       // If we're destroying, add the destroy node
-                       if inst.Destroy || inst.GetDestroyDeposed() {
-                               abstract := &NodeAbstractResource{Addr: addr}
-                               g.Add(&NodeDestroyResource{NodeAbstractResource: abstract})
+               if update {
+                       // All actions except destroying the node type chosen by t.Concrete
+                       abstract := NewNodeAbstractResourceInstance(addr)
+                       var node dag.Vertex = abstract
+                       if f := t.Concrete; f != nil {
+                               node = f(abstract)
                        }
 
-                       // If we have changes, then add the applyable version
-                       if len(inst.Attributes) > 0 {
-                               // Add the resource to the graph
-                               abstract := &NodeAbstractResource{Addr: addr}
-                               var node dag.Vertex = abstract
-                               if f := t.Concrete; f != nil {
-                                       node = f(abstract)
+                       if createBeforeDestroy {
+                               // We'll attach our pre-allocated DeposedKey to the node if
+                               // it supports that. NodeApplyableResourceInstance is the
+                               // specific concrete node type we are looking for here really,
+                               // since that's the only node type that might depose objects.
+                               if dn, ok := node.(GraphNodeDeposer); ok {
+                                       dn.SetPreallocatedDeposedKey(dk)
                                }
+                               log.Printf("[TRACE] DiffTransformer: %s will be represented by %s, deposing prior object to %s", addr, dag.VertexName(node), dk)
+                       } else {
+                               log.Printf("[TRACE] DiffTransformer: %s will be represented by %s", addr, dag.VertexName(node))
+                       }
 
-                               nodes = append(nodes, node)
+                       g.Add(node)
+                       rsrcAddr := addr.ContainingResource().String()
+                       for _, rsrcNode := range resourceNodes[rsrcAddr] {
+                               g.Connect(dag.BasicEdge(node, rsrcNode))
+                       }
+               }
+
+               if delete {
+                       // Destroying always uses a destroy-specific node type, though
+                       // which one depends on whether we're destroying a current object
+                       // or a deposed object.
+                       var node GraphNodeResourceInstance
+                       abstract := NewNodeAbstractResourceInstance(addr)
+                       if dk == states.NotDeposed {
+                               node = &NodeDestroyResourceInstance{
+                                       NodeAbstractResourceInstance: abstract,
+                                       DeposedKey:                   dk,
+                               }
+                               node.(*NodeDestroyResourceInstance).ModifyCreateBeforeDestroy(createBeforeDestroy)
+                       } else {
+                               node = &NodeDestroyDeposedResourceInstanceObject{
+                                       NodeAbstractResourceInstance: abstract,
+                                       DeposedKey:                   dk,
+                               }
+                       }
+                       if dk == states.NotDeposed {
+                               log.Printf("[TRACE] DiffTransformer: %s will be represented for destruction by %s", addr, dag.VertexName(node))
+                       } else {
+                               log.Printf("[TRACE] DiffTransformer: %s deposed object %s will be represented for destruction by %s", addr, dk, dag.VertexName(node))
+                       }
+                       g.Add(node)
+                       rsrcAddr := addr.ContainingResource().String()
+                       for _, rsrcNode := range resourceNodes[rsrcAddr] {
+                               // We connect this edge "forwards" (even though destroy dependencies
+                               // are often inverted) because evaluating the resource node
+                               // after the destroy node could cause an unnecessary husk of
+                               // a resource state to be re-added.
+                               g.Connect(dag.BasicEdge(node, rsrcNode))
                        }
                }
-       }
 
-       // Add all the nodes to the graph
-       for _, n := range nodes {
-               g.Add(n)
        }
 
-       return nil
+       log.Printf("[TRACE] DiffTransformer complete")
+
+       return diags.Err()
 }