github.com/skyscape-cloud-services/terraform@v0.9.2-0.20170609144644-7ece028a1747/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 = &NodeProvisioner{ 111 NameValue: p, 112 PathValue: path, 113 } 114 115 // Add the missing provisioner node to the graph 116 m[key] = g.Add(newV) 117 } 118 } 119 120 return nil 121 } 122 123 // CloseProvisionerTransformer is a GraphTransformer that adds nodes to the 124 // graph that will close open provisioner connections that aren't needed 125 // anymore. A provisioner connection is not needed anymore once all depended 126 // resources in the graph are evaluated. 127 type CloseProvisionerTransformer struct{} 128 129 func (t *CloseProvisionerTransformer) Transform(g *Graph) error { 130 m := closeProvisionerVertexMap(g) 131 for _, v := range g.Vertices() { 132 if pv, ok := v.(GraphNodeProvisionerConsumer); ok { 133 for _, p := range pv.ProvisionedBy() { 134 source := m[p] 135 136 if source == nil { 137 // Create a new graphNodeCloseProvisioner and add it to the graph 138 source = &graphNodeCloseProvisioner{ProvisionerNameValue: p} 139 g.Add(source) 140 141 // Make sure we also add the new graphNodeCloseProvisioner to the map 142 // so we don't create and add any duplicate graphNodeCloseProvisioners. 143 m[p] = source 144 } 145 146 g.Connect(dag.BasicEdge(source, v)) 147 } 148 } 149 } 150 151 return nil 152 } 153 154 // provisionerMapKey is a helper that gives us the key to use for the 155 // maps returned by things such as provisionerVertexMap. 156 func provisionerMapKey(k string, v dag.Vertex) string { 157 pathPrefix := "" 158 if sp, ok := v.(GraphNodeSubPath); ok { 159 raw := normalizeModulePath(sp.Path()) 160 if len(raw) > len(rootModulePath) { 161 pathPrefix = modulePrefixStr(raw) + "." 162 } 163 } 164 165 return pathPrefix + k 166 } 167 168 func provisionerVertexMap(g *Graph) map[string]dag.Vertex { 169 m := make(map[string]dag.Vertex) 170 for _, v := range g.Vertices() { 171 if pv, ok := v.(GraphNodeProvisioner); ok { 172 key := provisionerMapKey(pv.ProvisionerName(), v) 173 m[key] = v 174 } 175 } 176 177 return m 178 } 179 180 func closeProvisionerVertexMap(g *Graph) map[string]dag.Vertex { 181 m := make(map[string]dag.Vertex) 182 for _, v := range g.Vertices() { 183 if pv, ok := v.(GraphNodeCloseProvisioner); ok { 184 m[pv.CloseProvisionerName()] = v 185 } 186 } 187 188 return m 189 } 190 191 type graphNodeCloseProvisioner struct { 192 ProvisionerNameValue string 193 } 194 195 func (n *graphNodeCloseProvisioner) Name() string { 196 return fmt.Sprintf("provisioner.%s (close)", n.ProvisionerNameValue) 197 } 198 199 // GraphNodeEvalable impl. 200 func (n *graphNodeCloseProvisioner) EvalTree() EvalNode { 201 return &EvalCloseProvisioner{Name: n.ProvisionerNameValue} 202 } 203 204 func (n *graphNodeCloseProvisioner) CloseProvisionerName() string { 205 return n.ProvisionerNameValue 206 }