]> git.immae.eu Git - github/fretlink/terraform-provider-statuscake.git/blobdiff - vendor/github.com/hashicorp/terraform/terraform/transform_targets.go
Merge pull request #3 from terraform-providers/vendor-tf-0.10
[github/fretlink/terraform-provider-statuscake.git] / vendor / github.com / hashicorp / terraform / terraform / transform_targets.go
index 225ac4b4ae1572073911af49a629a32b3a2c68ca..4f117b4f732be059fa37439589f3edc5ec9a24fd 100644 (file)
@@ -15,6 +15,21 @@ type GraphNodeTargetable interface {
        SetTargets([]ResourceAddress)
 }
 
+// GraphNodeTargetDownstream is an interface for graph nodes that need to
+// be remain present under targeting if any of their dependencies are targeted.
+// TargetDownstream is called with the set of vertices that are direct
+// dependencies for the node, and it should return true if the node must remain
+// in the graph in support of those dependencies.
+//
+// This is used in situations where the dependency edges are representing an
+// ordering relationship but the dependency must still be visited if its
+// dependencies are visited. This is true for outputs, for example, since
+// they must get updated if any of their dependent resources get updated,
+// which would not normally be true if one of their dependencies were targeted.
+type GraphNodeTargetDownstream interface {
+       TargetDownstream(targeted, untargeted *dag.Set) bool
+}
+
 // TargetsTransformer is a GraphTransformer that, when the user specifies a
 // list of resources to target, limits the graph to only those resources and
 // their dependencies.
@@ -26,6 +41,12 @@ type TargetsTransformer struct {
        // that already have the targets parsed
        ParsedTargets []ResourceAddress
 
+       // If set, the index portions of resource addresses will be ignored
+       // for comparison. This is used when transforming a graph where
+       // counted resources have not yet been expanded, since otherwise
+       // the unexpanded nodes (which never have indices) would not match.
+       IgnoreIndices bool
+
        // Set to true when we're in a `terraform destroy` or a
        // `terraform plan -destroy`
        Destroy bool
@@ -84,7 +105,10 @@ func (t *TargetsTransformer) parseTargetAddresses() ([]ResourceAddress, error) {
 func (t *TargetsTransformer) selectTargetedNodes(
        g *Graph, addrs []ResourceAddress) (*dag.Set, error) {
        targetedNodes := new(dag.Set)
-       for _, v := range g.Vertices() {
+
+       vertices := g.Vertices()
+
+       for _, v := range vertices {
                if t.nodeIsTarget(v, addrs) {
                        targetedNodes.Add(v)
 
@@ -112,6 +136,63 @@ func (t *TargetsTransformer) selectTargetedNodes(
                }
        }
 
+       // Handle nodes that need to be included if their dependencies are included.
+       // This requires multiple passes since we need to catch transitive
+       // dependencies if and only if they are via other nodes that also
+       // support TargetDownstream. For example:
+       // output -> output -> targeted-resource: both outputs need to be targeted
+       // output -> non-targeted-resource -> targeted-resource: output not targeted
+       //
+       // We'll keep looping until we stop targeting more nodes.
+       queue := targetedNodes.List()
+       for len(queue) > 0 {
+               vertices := queue
+               queue = nil // ready to append for next iteration if neccessary
+               for _, v := range vertices {
+                       dependers := g.UpEdges(v)
+                       if dependers == nil {
+                               // indicates that there are no up edges for this node, so
+                               // we have nothing to do here.
+                               continue
+                       }
+
+                       dependers = dependers.Filter(func(dv interface{}) bool {
+                               // Can ignore nodes that are already targeted
+                               /*if targetedNodes.Include(dv) {
+                                       return false
+                               }*/
+
+                               _, ok := dv.(GraphNodeTargetDownstream)
+                               return ok
+                       })
+
+                       if dependers.Len() == 0 {
+                               continue
+                       }
+
+                       for _, dv := range dependers.List() {
+                               if targetedNodes.Include(dv) {
+                                       // Already present, so nothing to do
+                                       continue
+                               }
+
+                               // We'll give the node some information about what it's
+                               // depending on in case that informs its decision about whether
+                               // it is safe to be targeted.
+                               deps := g.DownEdges(v)
+                               depsTargeted := deps.Intersection(targetedNodes)
+                               depsUntargeted := deps.Difference(depsTargeted)
+
+                               if dv.(GraphNodeTargetDownstream).TargetDownstream(depsTargeted, depsUntargeted) {
+                                       targetedNodes.Add(dv)
+                                       // Need to visit this node on the next pass to see if it
+                                       // has any transitive dependers.
+                                       queue = append(queue, dv)
+                               }
+                       }
+               }
+       }
+
        return targetedNodes, nil
 }
 
@@ -124,7 +205,12 @@ func (t *TargetsTransformer) nodeIsTarget(
 
        addr := r.ResourceAddr()
        for _, targetAddr := range addrs {
-               if targetAddr.Equals(addr) {
+               if t.IgnoreIndices {
+                       // targetAddr is not a pointer, so we can safely mutate it without
+                       // interfering with references elsewhere.
+                       targetAddr.Index = -1
+               }
+               if targetAddr.Contains(addr) {
                        return true
                }
        }