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