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