github.com/kanishk98/terraform@v1.3.0-dev.0.20220917174235-661ca8088a6a/internal/terraform/transform_targets.go (about) 1 package terraform 2 3 import ( 4 "log" 5 6 "github.com/hashicorp/terraform/internal/addrs" 7 "github.com/hashicorp/terraform/internal/dag" 8 ) 9 10 // GraphNodeTargetable is an interface for graph nodes to implement when they 11 // need to be told about incoming targets. This is useful for nodes that need 12 // to respect targets as they dynamically expand. Note that the list of targets 13 // provided will contain every target provided, and each implementing graph 14 // node must filter this list to targets considered relevant. 15 type GraphNodeTargetable interface { 16 SetTargets([]addrs.Targetable) 17 } 18 19 // TargetsTransformer is a GraphTransformer that, when the user specifies a 20 // list of resources to target, limits the graph to only those resources and 21 // their dependencies. 22 type TargetsTransformer struct { 23 // List of targeted resource names specified by the user 24 Targets []addrs.Targetable 25 } 26 27 func (t *TargetsTransformer) Transform(g *Graph) error { 28 if len(t.Targets) > 0 { 29 targetedNodes, err := t.selectTargetedNodes(g, t.Targets) 30 if err != nil { 31 return err 32 } 33 34 for _, v := range g.Vertices() { 35 if !targetedNodes.Include(v) { 36 log.Printf("[DEBUG] Removing %q, filtered by targeting.", dag.VertexName(v)) 37 g.Remove(v) 38 } 39 } 40 } 41 42 return nil 43 } 44 45 // Returns a set of targeted nodes. A targeted node is either addressed 46 // directly, address indirectly via its container, or it's a dependency of a 47 // targeted node. 48 func (t *TargetsTransformer) selectTargetedNodes(g *Graph, addrs []addrs.Targetable) (dag.Set, error) { 49 targetedNodes := make(dag.Set) 50 51 vertices := g.Vertices() 52 53 for _, v := range vertices { 54 if t.nodeIsTarget(v, addrs) { 55 targetedNodes.Add(v) 56 57 // We inform nodes that ask about the list of targets - helps for nodes 58 // that need to dynamically expand. Note that this only occurs for nodes 59 // that are already directly targeted. 60 if tn, ok := v.(GraphNodeTargetable); ok { 61 tn.SetTargets(addrs) 62 } 63 64 deps, _ := g.Ancestors(v) 65 for _, d := range deps { 66 targetedNodes.Add(d) 67 } 68 } 69 } 70 71 // It is expected that outputs which are only derived from targeted 72 // resources are also updated. While we don't include any other possible 73 // side effects from the targeted nodes, these are added because outputs 74 // cannot be targeted on their own. 75 // Start by finding the root module output nodes themselves 76 for _, v := range vertices { 77 // outputs are all temporary value types 78 tv, ok := v.(graphNodeTemporaryValue) 79 if !ok { 80 continue 81 } 82 83 // root module outputs indicate that while they are an output type, 84 // they not temporary and will return false here. 85 if tv.temporaryValue() { 86 continue 87 } 88 89 // If this output is descended only from targeted resources, then we 90 // will keep it 91 deps, _ := g.Ancestors(v) 92 found := 0 93 for _, d := range deps { 94 switch d.(type) { 95 case GraphNodeResourceInstance: 96 case GraphNodeConfigResource: 97 default: 98 continue 99 } 100 101 if !targetedNodes.Include(d) { 102 // this dependency isn't being targeted, so we can't process this 103 // output 104 found = 0 105 break 106 } 107 108 found++ 109 } 110 111 if found > 0 { 112 // we found an output we can keep; add it, and all it's dependencies 113 targetedNodes.Add(v) 114 for _, d := range deps { 115 targetedNodes.Add(d) 116 } 117 } 118 } 119 120 return targetedNodes, nil 121 } 122 123 func (t *TargetsTransformer) nodeIsTarget(v dag.Vertex, targets []addrs.Targetable) bool { 124 var vertexAddr addrs.Targetable 125 switch r := v.(type) { 126 case GraphNodeResourceInstance: 127 vertexAddr = r.ResourceInstanceAddr() 128 case GraphNodeConfigResource: 129 vertexAddr = r.ResourceAddr() 130 131 default: 132 // Only resource and resource instance nodes can be targeted. 133 return false 134 } 135 136 for _, targetAddr := range targets { 137 switch vertexAddr.(type) { 138 case addrs.ConfigResource: 139 // Before expansion happens, we only have nodes that know their 140 // ConfigResource address. We need to take the more specific 141 // target addresses and generalize them in order to compare with a 142 // ConfigResource. 143 switch target := targetAddr.(type) { 144 case addrs.AbsResourceInstance: 145 targetAddr = target.ContainingResource().Config() 146 case addrs.AbsResource: 147 targetAddr = target.Config() 148 case addrs.ModuleInstance: 149 targetAddr = target.Module() 150 } 151 } 152 153 if targetAddr.TargetContains(vertexAddr) { 154 return true 155 } 156 } 157 158 return false 159 }