]> git.immae.eu Git - github/fretlink/terraform-provider-statuscake.git/blob - vendor/github.com/hashicorp/terraform/terraform/transform_targets.go
Initial transfer of provider code
[github/fretlink/terraform-provider-statuscake.git] / vendor / github.com / hashicorp / terraform / terraform / transform_targets.go
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 }