github.com/ggriffiths/terraform@v0.9.0-beta1.0.20170222213024-79c4935604cb/terraform/transform_targets.go (about) 1 package terraform 2 3 import ( 4 "log" 5 6 "github.com/hashicorp/terraform/dag" 7 ) 8 9 // GraphNodeTargetable is an interface for graph nodes to implement when they 10 // need to be told about incoming targets. This is useful for nodes that need 11 // to respect targets as they dynamically expand. Note that the list of targets 12 // provided will contain every target provided, and each implementing graph 13 // node must filter this list to targets considered relevant. 14 type GraphNodeTargetable interface { 15 SetTargets([]ResourceAddress) 16 } 17 18 // TargetsTransformer is a GraphTransformer that, when the user specifies a 19 // list of resources to target, limits the graph to only those resources and 20 // their dependencies. 21 type TargetsTransformer struct { 22 // List of targeted resource names specified by the user 23 Targets []string 24 25 // List of parsed targets, provided by callers like ResourceCountTransform 26 // that already have the targets parsed 27 ParsedTargets []ResourceAddress 28 29 // Set to true when we're in a `terraform destroy` or a 30 // `terraform plan -destroy` 31 Destroy bool 32 } 33 34 func (t *TargetsTransformer) Transform(g *Graph) error { 35 if len(t.Targets) > 0 && len(t.ParsedTargets) == 0 { 36 addrs, err := t.parseTargetAddresses() 37 if err != nil { 38 return err 39 } 40 41 t.ParsedTargets = addrs 42 } 43 44 if len(t.ParsedTargets) > 0 { 45 targetedNodes, err := t.selectTargetedNodes(g, t.ParsedTargets) 46 if err != nil { 47 return err 48 } 49 50 for _, v := range g.Vertices() { 51 removable := false 52 if _, ok := v.(GraphNodeResource); ok { 53 removable = true 54 } 55 if vr, ok := v.(RemovableIfNotTargeted); ok { 56 removable = vr.RemoveIfNotTargeted() 57 } 58 if removable && !targetedNodes.Include(v) { 59 log.Printf("[DEBUG] Removing %q, filtered by targeting.", dag.VertexName(v)) 60 g.Remove(v) 61 } 62 } 63 } 64 65 return nil 66 } 67 68 func (t *TargetsTransformer) parseTargetAddresses() ([]ResourceAddress, error) { 69 addrs := make([]ResourceAddress, len(t.Targets)) 70 for i, target := range t.Targets { 71 ta, err := ParseResourceAddress(target) 72 if err != nil { 73 return nil, err 74 } 75 addrs[i] = *ta 76 } 77 78 return addrs, nil 79 } 80 81 // Returns the list of targeted nodes. A targeted node is either addressed 82 // directly, or is an Ancestor of a targeted node. Destroy mode keeps 83 // Descendents instead of Ancestors. 84 func (t *TargetsTransformer) selectTargetedNodes( 85 g *Graph, addrs []ResourceAddress) (*dag.Set, error) { 86 targetedNodes := new(dag.Set) 87 for _, v := range g.Vertices() { 88 if t.nodeIsTarget(v, addrs) { 89 targetedNodes.Add(v) 90 91 // We inform nodes that ask about the list of targets - helps for nodes 92 // that need to dynamically expand. Note that this only occurs for nodes 93 // that are already directly targeted. 94 if tn, ok := v.(GraphNodeTargetable); ok { 95 tn.SetTargets(addrs) 96 } 97 98 var deps *dag.Set 99 var err error 100 if t.Destroy { 101 deps, err = g.Descendents(v) 102 } else { 103 deps, err = g.Ancestors(v) 104 } 105 if err != nil { 106 return nil, err 107 } 108 109 for _, d := range deps.List() { 110 targetedNodes.Add(d) 111 } 112 } 113 } 114 115 return targetedNodes, nil 116 } 117 118 func (t *TargetsTransformer) nodeIsTarget( 119 v dag.Vertex, addrs []ResourceAddress) bool { 120 r, ok := v.(GraphNodeResource) 121 if !ok { 122 return false 123 } 124 125 addr := r.ResourceAddr() 126 for _, targetAddr := range addrs { 127 if targetAddr.Equals(addr) { 128 return true 129 } 130 } 131 132 return false 133 } 134 135 // RemovableIfNotTargeted is a special interface for graph nodes that 136 // aren't directly addressable, but need to be removed from the graph when they 137 // are not targeted. (Nodes that are not directly targeted end up in the set of 138 // targeted nodes because something that _is_ targeted depends on them.) The 139 // initial use case for this interface is GraphNodeConfigVariable, which was 140 // having trouble interpolating for module variables in targeted scenarios that 141 // filtered out the resource node being referenced. 142 type RemovableIfNotTargeted interface { 143 RemoveIfNotTargeted() bool 144 }