github.com/inge4pres/terraform@v0.7.5-0.20160930053151-bd083f84f376/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 90 // Select any variables that we depend on in case we need them later for 91 // interpolating in the count 92 ancestors, _ := g.Ancestors(v) 93 for _, a := range ancestors.List() { 94 if _, ok := a.(*GraphNodeConfigVariableFlat); ok { 95 deps.Add(a) 96 } 97 } 98 } else { 99 deps, err = g.Ancestors(v) 100 } 101 if err != nil { 102 return nil, err 103 } 104 105 for _, d := range deps.List() { 106 targetedNodes.Add(d) 107 } 108 } 109 } 110 return targetedNodes, nil 111 } 112 113 func (t *TargetsTransformer) nodeIsTarget( 114 v dag.Vertex, addrs []ResourceAddress) bool { 115 r, ok := v.(GraphNodeAddressable) 116 if !ok { 117 return false 118 } 119 addr := r.ResourceAddress() 120 for _, targetAddr := range addrs { 121 if targetAddr.Equals(addr) { 122 return true 123 } 124 } 125 return false 126 } 127 128 // RemovableIfNotTargeted is a special interface for graph nodes that 129 // aren't directly addressable, but need to be removed from the graph when they 130 // are not targeted. (Nodes that are not directly targeted end up in the set of 131 // targeted nodes because something that _is_ targeted depends on them.) The 132 // initial use case for this interface is GraphNodeConfigVariable, which was 133 // having trouble interpolating for module variables in targeted scenarios that 134 // filtered out the resource node being referenced. 135 type RemovableIfNotTargeted interface { 136 RemoveIfNotTargeted() bool 137 }