aboutsummaryrefslogtreecommitdiffhomepage
path: root/vendor/github.com/hashicorp/terraform/moduledeps/module.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/hashicorp/terraform/moduledeps/module.go')
-rw-r--r--vendor/github.com/hashicorp/terraform/moduledeps/module.go204
1 files changed, 204 insertions, 0 deletions
diff --git a/vendor/github.com/hashicorp/terraform/moduledeps/module.go b/vendor/github.com/hashicorp/terraform/moduledeps/module.go
new file mode 100644
index 0000000..d6cbaf5
--- /dev/null
+++ b/vendor/github.com/hashicorp/terraform/moduledeps/module.go
@@ -0,0 +1,204 @@
1package moduledeps
2
3import (
4 "sort"
5 "strings"
6
7 "github.com/hashicorp/terraform/plugin/discovery"
8)
9
10// Module represents the dependencies of a single module, as well being
11// a node in a tree of such structures representing the dependencies of
12// an entire configuration.
13type Module struct {
14 Name string
15 Providers Providers
16 Children []*Module
17}
18
19// WalkFunc is a callback type for use with Module.WalkTree
20type WalkFunc func(path []string, parent *Module, current *Module) error
21
22// WalkTree calls the given callback once for the receiver and then
23// once for each descendent, in an order such that parents are called
24// before their children and siblings are called in the order they
25// appear in the Children slice.
26//
27// When calling the callback, parent will be nil for the first call
28// for the receiving module, and then set to the direct parent of
29// each module for the subsequent calls.
30//
31// The path given to the callback is valid only until the callback
32// returns, after which it will be mutated and reused. Callbacks must
33// therefore copy the path slice if they wish to retain it.
34//
35// If the given callback returns an error, the walk will be aborted at
36// that point and that error returned to the caller.
37//
38// This function is not thread-safe for concurrent modifications of the
39// data structure, so it's the caller's responsibility to arrange for that
40// should it be needed.
41//
42// It is safe for a callback to modify the descendents of the "current"
43// module, including the ordering of the Children slice itself, but the
44// callback MUST NOT modify the parent module.
45func (m *Module) WalkTree(cb WalkFunc) error {
46 return walkModuleTree(make([]string, 0, 1), nil, m, cb)
47}
48
49func walkModuleTree(path []string, parent *Module, current *Module, cb WalkFunc) error {
50 path = append(path, current.Name)
51 err := cb(path, parent, current)
52 if err != nil {
53 return err
54 }
55
56 for _, child := range current.Children {
57 err := walkModuleTree(path, current, child, cb)
58 if err != nil {
59 return err
60 }
61 }
62 return nil
63}
64
65// SortChildren sorts the Children slice into lexicographic order by
66// name, in-place.
67//
68// This is primarily useful prior to calling WalkTree so that the walk
69// will proceed in a consistent order.
70func (m *Module) SortChildren() {
71 sort.Sort(sortModules{m.Children})
72}
73
74// SortDescendents is a convenience wrapper for calling SortChildren on
75// the receiver and all of its descendent modules.
76func (m *Module) SortDescendents() {
77 m.WalkTree(func(path []string, parent *Module, current *Module) error {
78 current.SortChildren()
79 return nil
80 })
81}
82
83type sortModules struct {
84 modules []*Module
85}
86
87func (s sortModules) Len() int {
88 return len(s.modules)
89}
90
91func (s sortModules) Less(i, j int) bool {
92 cmp := strings.Compare(s.modules[i].Name, s.modules[j].Name)
93 return cmp < 0
94}
95
96func (s sortModules) Swap(i, j int) {
97 s.modules[i], s.modules[j] = s.modules[j], s.modules[i]
98}
99
100// PluginRequirements produces a PluginRequirements structure that can
101// be used with discovery.PluginMetaSet.ConstrainVersions to identify
102// suitable plugins to satisfy the module's provider dependencies.
103//
104// This method only considers the direct requirements of the receiver.
105// Use AllPluginRequirements to flatten the dependencies for the
106// entire tree of modules.
107//
108// Requirements returned by this method include only version constraints,
109// and apply no particular SHA256 hash constraint.
110func (m *Module) PluginRequirements() discovery.PluginRequirements {
111 ret := make(discovery.PluginRequirements)
112 for inst, dep := range m.Providers {
113 // m.Providers is keyed on provider names, such as "aws.foo".
114 // a PluginRequirements wants keys to be provider *types*, such
115 // as "aws". If there are multiple aliases for the same
116 // provider then we will flatten them into a single requirement
117 // by combining their constraint sets.
118 pty := inst.Type()
119 if existing, exists := ret[pty]; exists {
120 ret[pty].Versions = existing.Versions.Append(dep.Constraints)
121 } else {
122 ret[pty] = &discovery.PluginConstraints{
123 Versions: dep.Constraints,
124 }
125 }
126 }
127 return ret
128}
129
130// AllPluginRequirements calls PluginRequirements for the receiver and all
131// of its descendents, and merges the result into a single PluginRequirements
132// structure that would satisfy all of the modules together.
133//
134// Requirements returned by this method include only version constraints,
135// and apply no particular SHA256 hash constraint.
136func (m *Module) AllPluginRequirements() discovery.PluginRequirements {
137 var ret discovery.PluginRequirements
138 m.WalkTree(func(path []string, parent *Module, current *Module) error {
139 ret = ret.Merge(current.PluginRequirements())
140 return nil
141 })
142 return ret
143}
144
145// Equal returns true if the receiver is the root of an identical tree
146// to the other given Module. This is a deep comparison that considers
147// the equality of all downstream modules too.
148//
149// The children are considered to be ordered, so callers may wish to use
150// SortDescendents first to normalize the order of the slices of child nodes.
151//
152// The implementation of this function is not optimized since it is provided
153// primarily for use in tests.
154func (m *Module) Equal(other *Module) bool {
155 // take care of nils first
156 if m == nil && other == nil {
157 return true
158 } else if (m == nil && other != nil) || (m != nil && other == nil) {
159 return false
160 }
161
162 if m.Name != other.Name {
163 return false
164 }
165
166 if len(m.Providers) != len(other.Providers) {
167 return false
168 }
169 if len(m.Children) != len(other.Children) {
170 return false
171 }
172
173 // Can't use reflect.DeepEqual on this provider structure because
174 // the nested Constraints objects contain function pointers that
175 // never compare as equal. So we'll need to walk it the long way.
176 for inst, dep := range m.Providers {
177 if _, exists := other.Providers[inst]; !exists {
178 return false
179 }
180
181 if dep.Reason != other.Providers[inst].Reason {
182 return false
183 }
184
185 // Constraints are not too easy to compare robustly, so
186 // we'll just use their string representations as a proxy
187 // for now.
188 if dep.Constraints.String() != other.Providers[inst].Constraints.String() {
189 return false
190 }
191 }
192
193 // Above we already checked that we have the same number of children
194 // in each module, so now we just need to check that they are
195 // recursively equal.
196 for i := range m.Children {
197 if !m.Children[i].Equal(other.Children[i]) {
198 return false
199 }
200 }
201
202 // If we fall out here then they are equal
203 return true
204}