6 "github.com/hashicorp/go-multierror"
7 "github.com/hashicorp/terraform/dag"
10 // GraphNodeProvisioner is an interface that nodes that can be a provisioner
11 // must implement. The ProvisionerName returned is the name of the provisioner
13 type GraphNodeProvisioner interface {
14 ProvisionerName() string
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
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
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{}
36 func (t *ProvisionerTransformer) Transform(g *Graph) error {
37 // Go through the other nodes and match them to provisioners they need
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)
45 err = multierror.Append(err, fmt.Errorf(
46 "%s: provisioner %s couldn't be found",
47 dag.VertexName(v), p))
51 g.Connect(dag.BasicEdge(v, m[key]))
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.
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{}{}
73 // Get the map of provisioners we already have in our graph
74 m := provisionerVertexMap(g)
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)
84 // If this node has a subpath, then we use that as a prefix
85 // into our map to check for an existing provider.
87 if sp, ok := pv.(GraphNodeSubPath); ok {
88 raw := normalizeModulePath(sp.Path())
89 if len(raw) > len(rootModulePath) {
94 for _, p := range pv.ProvisionedBy() {
95 // Build the key for storing in the map
96 key := provisionerMapKey(p, pv)
98 if _, ok := m[key]; ok {
99 // This provisioner already exists as a configure node
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.
110 var newV dag.Vertex = &NodeProvisioner{
115 // Add the missing provisioner node to the graph
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{}
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() {
137 // Create a new graphNodeCloseProvisioner and add it to the graph
138 source = &graphNodeCloseProvisioner{ProvisionerNameValue: p}
141 // Make sure we also add the new graphNodeCloseProvisioner to the map
142 // so we don't create and add any duplicate graphNodeCloseProvisioners.
146 g.Connect(dag.BasicEdge(source, v))
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 {
158 if sp, ok := v.(GraphNodeSubPath); ok {
159 raw := normalizeModulePath(sp.Path())
160 if len(raw) > len(rootModulePath) {
161 pathPrefix = modulePrefixStr(raw) + "."
165 return pathPrefix + k
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)
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
191 type graphNodeCloseProvisioner struct {
192 ProvisionerNameValue string
195 func (n *graphNodeCloseProvisioner) Name() string {
196 return fmt.Sprintf("provisioner.%s (close)", n.ProvisionerNameValue)
199 // GraphNodeEvalable impl.
200 func (n *graphNodeCloseProvisioner) EvalTree() EvalNode {
201 return &EvalCloseProvisioner{Name: n.ProvisionerNameValue}
204 func (n *graphNodeCloseProvisioner) CloseProvisionerName() string {
205 return n.ProvisionerNameValue