github.com/turtlemonvh/terraform@v0.6.9-0.20151204001754-8e40b6b855e8/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 _, provisionerName := range pv.ProvisionedBy() { 43 target := m[provisionerName] 44 if target == nil { 45 err = multierror.Append(err, fmt.Errorf( 46 "%s: provisioner %s couldn't be found", 47 dag.VertexName(v), provisionerName)) 48 continue 49 } 50 51 g.Connect(dag.BasicEdge(v, target)) 52 } 53 } 54 } 55 56 return err 57 } 58 59 // CloseProvisionerTransformer is a GraphTransformer that adds nodes to the 60 // graph that will close open provisioner connections that aren't needed 61 // anymore. A provisioner connection is not needed anymore once all depended 62 // resources in the graph are evaluated. 63 type CloseProvisionerTransformer struct{} 64 65 func (t *CloseProvisionerTransformer) Transform(g *Graph) error { 66 m := closeProvisionerVertexMap(g) 67 for _, v := range g.Vertices() { 68 if pv, ok := v.(GraphNodeProvisionerConsumer); ok { 69 for _, p := range pv.ProvisionedBy() { 70 source := m[p] 71 72 if source == nil { 73 // Create a new graphNodeCloseProvisioner and add it to the graph 74 source = &graphNodeCloseProvisioner{ProvisionerNameValue: p} 75 g.Add(source) 76 77 // Make sure we also add the new graphNodeCloseProvisioner to the map 78 // so we don't create and add any duplicate graphNodeCloseProvisioners. 79 m[p] = source 80 } 81 82 g.Connect(dag.BasicEdge(source, v)) 83 } 84 } 85 } 86 87 return nil 88 } 89 90 // MissingProvisionerTransformer is a GraphTransformer that adds nodes 91 // for missing provisioners into the graph. Specifically, it creates provisioner 92 // configuration nodes for all the provisioners that we support. These are 93 // pruned later during an optimization pass. 94 type MissingProvisionerTransformer struct { 95 // Provisioners is the list of provisioners we support. 96 Provisioners []string 97 } 98 99 func (t *MissingProvisionerTransformer) Transform(g *Graph) error { 100 m := provisionerVertexMap(g) 101 for _, p := range t.Provisioners { 102 if _, ok := m[p]; ok { 103 // This provisioner already exists as a configured node 104 continue 105 } 106 107 // Add our own missing provisioner node to the graph 108 g.Add(&graphNodeMissingProvisioner{ProvisionerNameValue: p}) 109 } 110 111 return nil 112 } 113 114 // PruneProvisionerTransformer is a GraphTransformer that prunes all the 115 // provisioners that aren't needed from the graph. A provisioner is unneeded if 116 // no resource or module is using that provisioner. 117 type PruneProvisionerTransformer struct{} 118 119 func (t *PruneProvisionerTransformer) Transform(g *Graph) error { 120 for _, v := range g.Vertices() { 121 // We only care about the provisioners 122 if _, ok := v.(GraphNodeProvisioner); !ok { 123 continue 124 } 125 126 // Does anything depend on this? If not, then prune it. 127 if s := g.UpEdges(v); s.Len() == 0 { 128 g.Remove(v) 129 } 130 } 131 132 return nil 133 } 134 135 func provisionerVertexMap(g *Graph) map[string]dag.Vertex { 136 m := make(map[string]dag.Vertex) 137 for _, v := range g.Vertices() { 138 if pv, ok := v.(GraphNodeProvisioner); ok { 139 m[pv.ProvisionerName()] = v 140 } 141 } 142 143 return m 144 } 145 146 func closeProvisionerVertexMap(g *Graph) map[string]dag.Vertex { 147 m := make(map[string]dag.Vertex) 148 for _, v := range g.Vertices() { 149 if pv, ok := v.(GraphNodeCloseProvisioner); ok { 150 m[pv.CloseProvisionerName()] = v 151 } 152 } 153 154 return m 155 } 156 157 type graphNodeCloseProvisioner struct { 158 ProvisionerNameValue string 159 } 160 161 func (n *graphNodeCloseProvisioner) Name() string { 162 return fmt.Sprintf("provisioner.%s (close)", n.ProvisionerNameValue) 163 } 164 165 // GraphNodeEvalable impl. 166 func (n *graphNodeCloseProvisioner) EvalTree() EvalNode { 167 return &EvalCloseProvisioner{Name: n.ProvisionerNameValue} 168 } 169 170 func (n *graphNodeCloseProvisioner) CloseProvisionerName() string { 171 return n.ProvisionerNameValue 172 } 173 174 type graphNodeMissingProvisioner struct { 175 ProvisionerNameValue string 176 } 177 178 func (n *graphNodeMissingProvisioner) Name() string { 179 return fmt.Sprintf("provisioner.%s", n.ProvisionerNameValue) 180 } 181 182 // GraphNodeEvalable impl. 183 func (n *graphNodeMissingProvisioner) EvalTree() EvalNode { 184 return &EvalInitProvisioner{Name: n.ProvisionerNameValue} 185 } 186 187 func (n *graphNodeMissingProvisioner) ProvisionerName() string { 188 return n.ProvisionerNameValue 189 } 190 191 // GraphNodeFlattenable impl. 192 func (n *graphNodeMissingProvisioner) Flatten(p []string) (dag.Vertex, error) { 193 return &graphNodeMissingProvisionerFlat{ 194 graphNodeMissingProvisioner: n, 195 PathValue: p, 196 }, nil 197 } 198 199 // Same as graphNodeMissingProvisioner, but for flattening 200 type graphNodeMissingProvisionerFlat struct { 201 *graphNodeMissingProvisioner 202 203 PathValue []string 204 } 205 206 func (n *graphNodeMissingProvisionerFlat) Name() string { 207 return fmt.Sprintf( 208 "%s.%s", modulePrefixStr(n.PathValue), n.graphNodeMissingProvisioner.Name()) 209 } 210 211 func (n *graphNodeMissingProvisionerFlat) Path() []string { 212 return n.PathValue 213 } 214 215 func (n *graphNodeMissingProvisionerFlat) ProvisionerName() string { 216 return fmt.Sprintf( 217 "%s.%s", modulePrefixStr(n.PathValue), 218 n.graphNodeMissingProvisioner.ProvisionerName()) 219 }