github.com/r3labs/terraform@v0.8.4/terraform/transform_provisioner.go (about) 1 package terraform 2 3 import ( 4 "fmt" 5 6 "github.com/hashicorp/go-multierror" 7 "github.com/hashicorp/terraform/dag" 8 ) 9 10 // GraphNodeProvisioner is an interface that nodes that can be a provisioner 11 // must implement. The ProvisionerName returned is the name of the provisioner 12 // they satisfy. 13 type GraphNodeProvisioner interface { 14 ProvisionerName() string 15 } 16 17 // GraphNodeCloseProvisioner is an interface that nodes that can be a close 18 // provisioner must implement. The CloseProvisionerName returned is the name 19 // of the provisioner they satisfy. 20 type GraphNodeCloseProvisioner interface { 21 CloseProvisionerName() string 22 } 23 24 // GraphNodeProvisionerConsumer is an interface that nodes that require 25 // a provisioner must implement. ProvisionedBy must return the name of the 26 // provisioner to use. 27 type GraphNodeProvisionerConsumer interface { 28 ProvisionedBy() []string 29 } 30 31 // ProvisionerTransformer is a GraphTransformer that maps resources to 32 // provisioners within the graph. This will error if there are any resources 33 // that don't map to proper resources. 34 type ProvisionerTransformer struct{} 35 36 func (t *ProvisionerTransformer) Transform(g *Graph) error { 37 // Go through the other nodes and match them to provisioners they need 38 var err error 39 m := provisionerVertexMap(g) 40 for _, v := range g.Vertices() { 41 if pv, ok := v.(GraphNodeProvisionerConsumer); ok { 42 for _, p := range pv.ProvisionedBy() { 43 key := provisionerMapKey(p, pv) 44 if m[key] == nil { 45 err = multierror.Append(err, fmt.Errorf( 46 "%s: provisioner %s couldn't be found", 47 dag.VertexName(v), p)) 48 continue 49 } 50 51 g.Connect(dag.BasicEdge(v, m[key])) 52 } 53 } 54 } 55 56 return err 57 } 58 59 // MissingProvisionerTransformer is a GraphTransformer that adds nodes 60 // for missing provisioners into the graph. 61 type MissingProvisionerTransformer struct { 62 // Provisioners is the list of provisioners we support. 63 Provisioners []string 64 } 65 66 func (t *MissingProvisionerTransformer) Transform(g *Graph) error { 67 // Create a set of our supported provisioners 68 supported := make(map[string]struct{}, len(t.Provisioners)) 69 for _, v := range t.Provisioners { 70 supported[v] = struct{}{} 71 } 72 73 // Get the map of provisioners we already have in our graph 74 m := provisionerVertexMap(g) 75 76 // Go through all the provisioner consumers and make sure we add 77 // that provisioner if it is missing. 78 for _, v := range g.Vertices() { 79 pv, ok := v.(GraphNodeProvisionerConsumer) 80 if !ok { 81 continue 82 } 83 84 // If this node has a subpath, then we use that as a prefix 85 // into our map to check for an existing provider. 86 var path []string 87 if sp, ok := pv.(GraphNodeSubPath); ok { 88 raw := normalizeModulePath(sp.Path()) 89 if len(raw) > len(rootModulePath) { 90 path = raw 91 } 92 } 93 94 for _, p := range pv.ProvisionedBy() { 95 // Build the key for storing in the map 96 key := provisionerMapKey(p, pv) 97 98 if _, ok := m[key]; ok { 99 // This provisioner already exists as a configure node 100 continue 101 } 102 103 if _, ok := supported[p]; !ok { 104 // If we don't support the provisioner type, skip it. 105 // Validation later will catch this as an error. 106 continue 107 } 108 109 // Build the vertex 110 var newV dag.Vertex = &graphNodeProvisioner{ProvisionerNameValue: p} 111 if len(path) > 0 { 112 // If we have a path, we do the flattening immediately. This 113 // is to support new-style graph nodes that are already 114 // flattened. 115 if fn, ok := newV.(GraphNodeFlattenable); ok { 116 var err error 117 newV, err = fn.Flatten(path) 118 if err != nil { 119 return err 120 } 121 } 122 } 123 124 // Add the missing provisioner node to the graph 125 m[key] = g.Add(newV) 126 } 127 } 128 129 return nil 130 } 131 132 // CloseProvisionerTransformer is a GraphTransformer that adds nodes to the 133 // graph that will close open provisioner connections that aren't needed 134 // anymore. A provisioner connection is not needed anymore once all depended 135 // resources in the graph are evaluated. 136 type CloseProvisionerTransformer struct{} 137 138 func (t *CloseProvisionerTransformer) Transform(g *Graph) error { 139 m := closeProvisionerVertexMap(g) 140 for _, v := range g.Vertices() { 141 if pv, ok := v.(GraphNodeProvisionerConsumer); ok { 142 for _, p := range pv.ProvisionedBy() { 143 source := m[p] 144 145 if source == nil { 146 // Create a new graphNodeCloseProvisioner and add it to the graph 147 source = &graphNodeCloseProvisioner{ProvisionerNameValue: p} 148 g.Add(source) 149 150 // Make sure we also add the new graphNodeCloseProvisioner to the map 151 // so we don't create and add any duplicate graphNodeCloseProvisioners. 152 m[p] = source 153 } 154 155 g.Connect(dag.BasicEdge(source, v)) 156 } 157 } 158 } 159 160 return nil 161 } 162 163 // provisionerMapKey is a helper that gives us the key to use for the 164 // maps returned by things such as provisionerVertexMap. 165 func provisionerMapKey(k string, v dag.Vertex) string { 166 pathPrefix := "" 167 if sp, ok := v.(GraphNodeSubPath); ok { 168 raw := normalizeModulePath(sp.Path()) 169 if len(raw) > len(rootModulePath) { 170 pathPrefix = modulePrefixStr(raw) + "." 171 } 172 } 173 174 return pathPrefix + k 175 } 176 177 func provisionerVertexMap(g *Graph) map[string]dag.Vertex { 178 m := make(map[string]dag.Vertex) 179 for _, v := range g.Vertices() { 180 if pv, ok := v.(GraphNodeProvisioner); ok { 181 m[pv.ProvisionerName()] = v 182 } 183 } 184 185 return m 186 } 187 188 func closeProvisionerVertexMap(g *Graph) map[string]dag.Vertex { 189 m := make(map[string]dag.Vertex) 190 for _, v := range g.Vertices() { 191 if pv, ok := v.(GraphNodeCloseProvisioner); ok { 192 m[pv.CloseProvisionerName()] = v 193 } 194 } 195 196 return m 197 } 198 199 type graphNodeCloseProvisioner struct { 200 ProvisionerNameValue string 201 } 202 203 func (n *graphNodeCloseProvisioner) Name() string { 204 return fmt.Sprintf("provisioner.%s (close)", n.ProvisionerNameValue) 205 } 206 207 // GraphNodeEvalable impl. 208 func (n *graphNodeCloseProvisioner) EvalTree() EvalNode { 209 return &EvalCloseProvisioner{Name: n.ProvisionerNameValue} 210 } 211 212 func (n *graphNodeCloseProvisioner) CloseProvisionerName() string { 213 return n.ProvisionerNameValue 214 } 215 216 type graphNodeProvisioner struct { 217 ProvisionerNameValue string 218 } 219 220 func (n *graphNodeProvisioner) Name() string { 221 return fmt.Sprintf("provisioner.%s", n.ProvisionerNameValue) 222 } 223 224 // GraphNodeEvalable impl. 225 func (n *graphNodeProvisioner) EvalTree() EvalNode { 226 return &EvalInitProvisioner{Name: n.ProvisionerNameValue} 227 } 228 229 func (n *graphNodeProvisioner) ProvisionerName() string { 230 return n.ProvisionerNameValue 231 } 232 233 // GraphNodeFlattenable impl. 234 func (n *graphNodeProvisioner) Flatten(p []string) (dag.Vertex, error) { 235 return &graphNodeProvisionerFlat{ 236 graphNodeProvisioner: n, 237 PathValue: p, 238 }, nil 239 } 240 241 // Same as graphNodeMissingProvisioner, but for flattening 242 type graphNodeProvisionerFlat struct { 243 *graphNodeProvisioner 244 245 PathValue []string 246 } 247 248 func (n *graphNodeProvisionerFlat) Name() string { 249 return fmt.Sprintf( 250 "%s.%s", modulePrefixStr(n.PathValue), n.graphNodeProvisioner.Name()) 251 } 252 253 func (n *graphNodeProvisionerFlat) Path() []string { 254 return n.PathValue 255 } 256 257 func (n *graphNodeProvisionerFlat) ProvisionerName() string { 258 return fmt.Sprintf( 259 "%s.%s", modulePrefixStr(n.PathValue), 260 n.graphNodeProvisioner.ProvisionerName()) 261 }