]>
Commit | Line | Data |
---|---|---|
bae9f6d2 JC |
1 | package terraform |
2 | ||
3 | import ( | |
4 | "fmt" | |
107c1cdb ND |
5 | "log" |
6 | ||
7 | "github.com/hashicorp/terraform/addrs" | |
bae9f6d2 JC |
8 | |
9 | "github.com/hashicorp/go-multierror" | |
10 | "github.com/hashicorp/terraform/dag" | |
11 | ) | |
12 | ||
13 | // GraphNodeProvisioner is an interface that nodes that can be a provisioner | |
14 | // must implement. The ProvisionerName returned is the name of the provisioner | |
15 | // they satisfy. | |
16 | type GraphNodeProvisioner interface { | |
17 | ProvisionerName() string | |
18 | } | |
19 | ||
20 | // GraphNodeCloseProvisioner is an interface that nodes that can be a close | |
21 | // provisioner must implement. The CloseProvisionerName returned is the name | |
22 | // of the provisioner they satisfy. | |
23 | type GraphNodeCloseProvisioner interface { | |
24 | CloseProvisionerName() string | |
25 | } | |
26 | ||
27 | // GraphNodeProvisionerConsumer is an interface that nodes that require | |
107c1cdb ND |
28 | // a provisioner must implement. ProvisionedBy must return the names of the |
29 | // provisioners to use. | |
bae9f6d2 JC |
30 | type GraphNodeProvisionerConsumer interface { |
31 | ProvisionedBy() []string | |
32 | } | |
33 | ||
34 | // ProvisionerTransformer is a GraphTransformer that maps resources to | |
35 | // provisioners within the graph. This will error if there are any resources | |
36 | // that don't map to proper resources. | |
37 | type ProvisionerTransformer struct{} | |
38 | ||
39 | func (t *ProvisionerTransformer) Transform(g *Graph) error { | |
40 | // Go through the other nodes and match them to provisioners they need | |
41 | var err error | |
42 | m := provisionerVertexMap(g) | |
43 | for _, v := range g.Vertices() { | |
44 | if pv, ok := v.(GraphNodeProvisionerConsumer); ok { | |
45 | for _, p := range pv.ProvisionedBy() { | |
46 | key := provisionerMapKey(p, pv) | |
47 | if m[key] == nil { | |
48 | err = multierror.Append(err, fmt.Errorf( | |
49 | "%s: provisioner %s couldn't be found", | |
50 | dag.VertexName(v), p)) | |
51 | continue | |
52 | } | |
53 | ||
107c1cdb | 54 | log.Printf("[TRACE] ProvisionerTransformer: %s is provisioned by %s (%q)", dag.VertexName(v), key, dag.VertexName(m[key])) |
bae9f6d2 JC |
55 | g.Connect(dag.BasicEdge(v, m[key])) |
56 | } | |
57 | } | |
58 | } | |
59 | ||
60 | return err | |
61 | } | |
62 | ||
63 | // MissingProvisionerTransformer is a GraphTransformer that adds nodes | |
64 | // for missing provisioners into the graph. | |
65 | type MissingProvisionerTransformer struct { | |
66 | // Provisioners is the list of provisioners we support. | |
67 | Provisioners []string | |
68 | } | |
69 | ||
70 | func (t *MissingProvisionerTransformer) Transform(g *Graph) error { | |
71 | // Create a set of our supported provisioners | |
72 | supported := make(map[string]struct{}, len(t.Provisioners)) | |
73 | for _, v := range t.Provisioners { | |
74 | supported[v] = struct{}{} | |
75 | } | |
76 | ||
77 | // Get the map of provisioners we already have in our graph | |
78 | m := provisionerVertexMap(g) | |
79 | ||
80 | // Go through all the provisioner consumers and make sure we add | |
81 | // that provisioner if it is missing. | |
82 | for _, v := range g.Vertices() { | |
83 | pv, ok := v.(GraphNodeProvisionerConsumer) | |
84 | if !ok { | |
85 | continue | |
86 | } | |
87 | ||
88 | // If this node has a subpath, then we use that as a prefix | |
89 | // into our map to check for an existing provider. | |
107c1cdb | 90 | path := addrs.RootModuleInstance |
bae9f6d2 | 91 | if sp, ok := pv.(GraphNodeSubPath); ok { |
107c1cdb | 92 | path = sp.Path() |
bae9f6d2 JC |
93 | } |
94 | ||
95 | for _, p := range pv.ProvisionedBy() { | |
96 | // Build the key for storing in the map | |
97 | key := provisionerMapKey(p, pv) | |
98 | ||
99 | if _, ok := m[key]; ok { | |
100 | // This provisioner already exists as a configure node | |
101 | continue | |
102 | } | |
103 | ||
104 | if _, ok := supported[p]; !ok { | |
107c1cdb | 105 | // If we don't support the provisioner type, we skip it. |
bae9f6d2 JC |
106 | // Validation later will catch this as an error. |
107 | continue | |
108 | } | |
109 | ||
110 | // Build the vertex | |
111 | var newV dag.Vertex = &NodeProvisioner{ | |
112 | NameValue: p, | |
113 | PathValue: path, | |
114 | } | |
115 | ||
116 | // Add the missing provisioner node to the graph | |
117 | m[key] = g.Add(newV) | |
107c1cdb | 118 | log.Printf("[TRACE] MissingProviderTransformer: added implicit provisioner %s, first implied by %s", key, dag.VertexName(v)) |
bae9f6d2 JC |
119 | } |
120 | } | |
121 | ||
122 | return nil | |
123 | } | |
124 | ||
125 | // CloseProvisionerTransformer is a GraphTransformer that adds nodes to the | |
126 | // graph that will close open provisioner connections that aren't needed | |
127 | // anymore. A provisioner connection is not needed anymore once all depended | |
128 | // resources in the graph are evaluated. | |
129 | type CloseProvisionerTransformer struct{} | |
130 | ||
131 | func (t *CloseProvisionerTransformer) Transform(g *Graph) error { | |
132 | m := closeProvisionerVertexMap(g) | |
133 | for _, v := range g.Vertices() { | |
134 | if pv, ok := v.(GraphNodeProvisionerConsumer); ok { | |
135 | for _, p := range pv.ProvisionedBy() { | |
136 | source := m[p] | |
137 | ||
138 | if source == nil { | |
139 | // Create a new graphNodeCloseProvisioner and add it to the graph | |
140 | source = &graphNodeCloseProvisioner{ProvisionerNameValue: p} | |
141 | g.Add(source) | |
142 | ||
143 | // Make sure we also add the new graphNodeCloseProvisioner to the map | |
144 | // so we don't create and add any duplicate graphNodeCloseProvisioners. | |
145 | m[p] = source | |
146 | } | |
147 | ||
148 | g.Connect(dag.BasicEdge(source, v)) | |
149 | } | |
150 | } | |
151 | } | |
152 | ||
153 | return nil | |
154 | } | |
155 | ||
156 | // provisionerMapKey is a helper that gives us the key to use for the | |
157 | // maps returned by things such as provisionerVertexMap. | |
158 | func provisionerMapKey(k string, v dag.Vertex) string { | |
159 | pathPrefix := "" | |
160 | if sp, ok := v.(GraphNodeSubPath); ok { | |
107c1cdb | 161 | pathPrefix = sp.Path().String() + "." |
bae9f6d2 JC |
162 | } |
163 | ||
164 | return pathPrefix + k | |
165 | } | |
166 | ||
167 | func provisionerVertexMap(g *Graph) map[string]dag.Vertex { | |
168 | m := make(map[string]dag.Vertex) | |
169 | for _, v := range g.Vertices() { | |
170 | if pv, ok := v.(GraphNodeProvisioner); ok { | |
171 | key := provisionerMapKey(pv.ProvisionerName(), v) | |
172 | m[key] = v | |
173 | } | |
174 | } | |
175 | ||
176 | return m | |
177 | } | |
178 | ||
179 | func closeProvisionerVertexMap(g *Graph) map[string]dag.Vertex { | |
180 | m := make(map[string]dag.Vertex) | |
181 | for _, v := range g.Vertices() { | |
182 | if pv, ok := v.(GraphNodeCloseProvisioner); ok { | |
183 | m[pv.CloseProvisionerName()] = v | |
184 | } | |
185 | } | |
186 | ||
187 | return m | |
188 | } | |
189 | ||
190 | type graphNodeCloseProvisioner struct { | |
191 | ProvisionerNameValue string | |
192 | } | |
193 | ||
194 | func (n *graphNodeCloseProvisioner) Name() string { | |
195 | return fmt.Sprintf("provisioner.%s (close)", n.ProvisionerNameValue) | |
196 | } | |
197 | ||
198 | // GraphNodeEvalable impl. | |
199 | func (n *graphNodeCloseProvisioner) EvalTree() EvalNode { | |
200 | return &EvalCloseProvisioner{Name: n.ProvisionerNameValue} | |
201 | } | |
202 | ||
203 | func (n *graphNodeCloseProvisioner) CloseProvisionerName() string { | |
204 | return n.ProvisionerNameValue | |
205 | } |