forked from hashicorp/terraform
/
transform_provisioner.go
153 lines (125 loc) · 4.17 KB
/
transform_provisioner.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
package terraform
import (
"fmt"
"github.com/hashicorp/go-multierror"
"github.com/hashicorp/terraform/dag"
)
// GraphNodeProvisioner is an interface that nodes that can be a provisioner
// must implement. The ProvisionerName returned is the name of the provisioner
// they satisfy.
type GraphNodeProvisioner interface {
ProvisionerName() string
}
// GraphNodeProvisionerConsumer is an interface that nodes that require
// a provisioner must implement. ProvisionedBy must return the name of the
// provisioner to use.
type GraphNodeProvisionerConsumer interface {
ProvisionedBy() []string
}
// ProvisionerTransformer is a GraphTransformer that maps resources to
// provisioners within the graph. This will error if there are any resources
// that don't map to proper resources.
type ProvisionerTransformer struct{}
func (t *ProvisionerTransformer) Transform(g *Graph) error {
// Go through the other nodes and match them to provisioners they need
var err error
m := provisionerVertexMap(g)
for _, v := range g.Vertices() {
if pv, ok := v.(GraphNodeProvisionerConsumer); ok {
for _, provisionerName := range pv.ProvisionedBy() {
target := m[provisionerName]
if target == nil {
err = multierror.Append(err, fmt.Errorf(
"%s: provisioner %s couldn't be found",
dag.VertexName(v), provisionerName))
continue
}
g.Connect(dag.BasicEdge(v, target))
}
}
}
return err
}
// MissingProvisionerTransformer is a GraphTransformer that adds nodes
// for missing provisioners into the graph. Specifically, it creates provisioner
// configuration nodes for all the provisioners that we support. These are
// pruned later during an optimization pass.
type MissingProvisionerTransformer struct {
// Provisioners is the list of provisioners we support.
Provisioners []string
}
func (t *MissingProvisionerTransformer) Transform(g *Graph) error {
m := provisionerVertexMap(g)
for _, p := range t.Provisioners {
if _, ok := m[p]; ok {
// This provisioner already exists as a configured node
continue
}
// Add our own missing provisioner node to the graph
g.Add(&graphNodeMissingProvisioner{ProvisionerNameValue: p})
}
return nil
}
// PruneProvisionerTransformer is a GraphTransformer that prunes all the
// provisioners that aren't needed from the graph. A provisioner is unneeded if
// no resource or module is using that provisioner.
type PruneProvisionerTransformer struct{}
func (t *PruneProvisionerTransformer) Transform(g *Graph) error {
for _, v := range g.Vertices() {
// We only care about the provisioners
if _, ok := v.(GraphNodeProvisioner); !ok {
continue
}
// Does anything depend on this? If not, then prune it.
if s := g.UpEdges(v); s.Len() == 0 {
g.Remove(v)
}
}
return nil
}
type graphNodeMissingProvisioner struct {
ProvisionerNameValue string
}
func (n *graphNodeMissingProvisioner) Name() string {
return fmt.Sprintf("provisioner.%s", n.ProvisionerNameValue)
}
// GraphNodeEvalable impl.
func (n *graphNodeMissingProvisioner) EvalTree() EvalNode {
return &EvalInitProvisioner{Name: n.ProvisionerNameValue}
}
func (n *graphNodeMissingProvisioner) ProvisionerName() string {
return n.ProvisionerNameValue
}
// GraphNodeFlattenable impl.
func (n *graphNodeMissingProvisioner) Flatten(p []string) (dag.Vertex, error) {
return &graphNodeMissingProvisionerFlat{
graphNodeMissingProvisioner: n,
PathValue: p,
}, nil
}
func provisionerVertexMap(g *Graph) map[string]dag.Vertex {
m := make(map[string]dag.Vertex)
for _, v := range g.Vertices() {
if pv, ok := v.(GraphNodeProvisioner); ok {
m[pv.ProvisionerName()] = v
}
}
return m
}
// Same as graphNodeMissingProvisioner, but for flattening
type graphNodeMissingProvisionerFlat struct {
*graphNodeMissingProvisioner
PathValue []string
}
func (n *graphNodeMissingProvisionerFlat) Name() string {
return fmt.Sprintf(
"%s.%s", modulePrefixStr(n.PathValue), n.graphNodeMissingProvisioner.Name())
}
func (n *graphNodeMissingProvisionerFlat) Path() []string {
return n.PathValue
}
func (n *graphNodeMissingProvisionerFlat) ProvisionerName() string {
return fmt.Sprintf(
"%s.%s", modulePrefixStr(n.PathValue),
n.graphNodeMissingProvisioner.ProvisionerName())
}