]> git.immae.eu Git - github/fretlink/terraform-provider-statuscake.git/blob - vendor/github.com/hashicorp/terraform/terraform/transform_provisioner.go
Upgrade to 0.12
[github/fretlink/terraform-provider-statuscake.git] / vendor / github.com / hashicorp / terraform / terraform / transform_provisioner.go
1 package terraform
2
3 import (
4 "fmt"
5 "log"
6
7 "github.com/hashicorp/terraform/addrs"
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
28 // a provisioner must implement. ProvisionedBy must return the names of the
29 // provisioners to use.
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
54 log.Printf("[TRACE] ProvisionerTransformer: %s is provisioned by %s (%q)", dag.VertexName(v), key, dag.VertexName(m[key]))
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.
90 path := addrs.RootModuleInstance
91 if sp, ok := pv.(GraphNodeSubPath); ok {
92 path = sp.Path()
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 {
105 // If we don't support the provisioner type, we skip it.
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)
118 log.Printf("[TRACE] MissingProviderTransformer: added implicit provisioner %s, first implied by %s", key, dag.VertexName(v))
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 {
161 pathPrefix = sp.Path().String() + "."
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 }