]>
Commit | Line | Data |
---|---|---|
c680a8e1 RS |
1 | package discovery |
2 | ||
3 | // A PluginMetaSet is a set of PluginMeta objects meeting a certain criteria. | |
4 | // | |
5 | // Methods on this type allow filtering of the set to produce subsets that | |
6 | // meet more restrictive criteria. | |
7 | type PluginMetaSet map[PluginMeta]struct{} | |
8 | ||
9 | // Add inserts the given PluginMeta into the receiving set. This is a no-op | |
10 | // if the given meta is already present. | |
11 | func (s PluginMetaSet) Add(p PluginMeta) { | |
12 | s[p] = struct{}{} | |
13 | } | |
14 | ||
15 | // Remove removes the given PluginMeta from the receiving set. This is a no-op | |
16 | // if the given meta is not already present. | |
17 | func (s PluginMetaSet) Remove(p PluginMeta) { | |
18 | delete(s, p) | |
19 | } | |
20 | ||
21 | // Has returns true if the given meta is in the receiving set, or false | |
22 | // otherwise. | |
23 | func (s PluginMetaSet) Has(p PluginMeta) bool { | |
24 | _, ok := s[p] | |
25 | return ok | |
26 | } | |
27 | ||
28 | // Count returns the number of metas in the set | |
29 | func (s PluginMetaSet) Count() int { | |
30 | return len(s) | |
31 | } | |
32 | ||
33 | // ValidateVersions returns two new PluginMetaSets, separating those with | |
34 | // versions that have syntax-valid semver versions from those that don't. | |
35 | // | |
36 | // Eliminating invalid versions from consideration (and possibly warning about | |
37 | // them) is usually the first step of working with a meta set after discovery | |
38 | // has completed. | |
39 | func (s PluginMetaSet) ValidateVersions() (valid, invalid PluginMetaSet) { | |
40 | valid = make(PluginMetaSet) | |
41 | invalid = make(PluginMetaSet) | |
42 | for p := range s { | |
43 | if _, err := p.Version.Parse(); err == nil { | |
44 | valid.Add(p) | |
45 | } else { | |
46 | invalid.Add(p) | |
47 | } | |
48 | } | |
49 | return | |
50 | } | |
51 | ||
52 | // WithName returns the subset of metas that have the given name. | |
53 | func (s PluginMetaSet) WithName(name string) PluginMetaSet { | |
54 | ns := make(PluginMetaSet) | |
55 | for p := range s { | |
56 | if p.Name == name { | |
57 | ns.Add(p) | |
58 | } | |
59 | } | |
60 | return ns | |
61 | } | |
62 | ||
63 | // WithVersion returns the subset of metas that have the given version. | |
64 | // | |
65 | // This should be used only with the "valid" result from ValidateVersions; | |
107c1cdb | 66 | // it will ignore any plugin metas that have invalid version strings. |
c680a8e1 RS |
67 | func (s PluginMetaSet) WithVersion(version Version) PluginMetaSet { |
68 | ns := make(PluginMetaSet) | |
69 | for p := range s { | |
70 | gotVersion, err := p.Version.Parse() | |
71 | if err != nil { | |
72 | continue | |
73 | } | |
74 | if gotVersion.Equal(version) { | |
75 | ns.Add(p) | |
76 | } | |
77 | } | |
78 | return ns | |
79 | } | |
80 | ||
81 | // ByName groups the metas in the set by their Names, returning a map. | |
82 | func (s PluginMetaSet) ByName() map[string]PluginMetaSet { | |
83 | ret := make(map[string]PluginMetaSet) | |
84 | for p := range s { | |
85 | if _, ok := ret[p.Name]; !ok { | |
86 | ret[p.Name] = make(PluginMetaSet) | |
87 | } | |
88 | ret[p.Name].Add(p) | |
89 | } | |
90 | return ret | |
91 | } | |
92 | ||
93 | // Newest returns the one item from the set that has the newest Version value. | |
94 | // | |
95 | // The result is meaningful only if the set is already filtered such that | |
96 | // all of the metas have the same Name. | |
97 | // | |
98 | // If there isn't at least one meta in the set then this function will panic. | |
99 | // Use Count() to ensure that there is at least one value before calling. | |
100 | // | |
101 | // If any of the metas have invalid version strings then this function will | |
102 | // panic. Use ValidateVersions() first to filter out metas with invalid | |
103 | // versions. | |
104 | // | |
105 | // If two metas have the same Version then one is arbitrarily chosen. This | |
106 | // situation should be avoided by pre-filtering the set. | |
107 | func (s PluginMetaSet) Newest() PluginMeta { | |
108 | if len(s) == 0 { | |
109 | panic("can't call NewestStable on empty PluginMetaSet") | |
110 | } | |
111 | ||
112 | var first = true | |
113 | var winner PluginMeta | |
114 | var winnerVersion Version | |
115 | for p := range s { | |
116 | version, err := p.Version.Parse() | |
117 | if err != nil { | |
118 | panic(err) | |
119 | } | |
120 | ||
121 | if first == true || version.NewerThan(winnerVersion) { | |
122 | winner = p | |
123 | winnerVersion = version | |
124 | first = false | |
125 | } | |
126 | } | |
127 | ||
128 | return winner | |
129 | } | |
130 | ||
131 | // ConstrainVersions takes a set of requirements and attempts to | |
132 | // return a map from name to a set of metas that have the matching | |
133 | // name and an appropriate version. | |
134 | // | |
135 | // If any of the given requirements match *no* plugins then its PluginMetaSet | |
136 | // in the returned map will be empty. | |
137 | // | |
138 | // All viable metas are returned, so the caller can apply any desired filtering | |
139 | // to reduce down to a single option. For example, calling Newest() to obtain | |
140 | // the highest available version. | |
141 | // | |
142 | // If any of the metas in the set have invalid version strings then this | |
143 | // function will panic. Use ValidateVersions() first to filter out metas with | |
144 | // invalid versions. | |
145 | func (s PluginMetaSet) ConstrainVersions(reqd PluginRequirements) map[string]PluginMetaSet { | |
146 | ret := make(map[string]PluginMetaSet) | |
147 | for p := range s { | |
148 | name := p.Name | |
149 | allowedVersions, ok := reqd[name] | |
150 | if !ok { | |
151 | continue | |
152 | } | |
153 | if _, ok := ret[p.Name]; !ok { | |
154 | ret[p.Name] = make(PluginMetaSet) | |
155 | } | |
156 | version, err := p.Version.Parse() | |
157 | if err != nil { | |
158 | panic(err) | |
159 | } | |
160 | if allowedVersions.Allows(version) { | |
161 | ret[p.Name].Add(p) | |
162 | } | |
163 | } | |
164 | return ret | |
165 | } | |
166 | ||
167 | // OverridePaths returns a new set where any existing plugins with the given | |
168 | // names are removed and replaced with the single path given in the map. | |
169 | // | |
170 | // This is here only to continue to support the legacy way of overriding | |
171 | // plugin binaries in the .terraformrc file. It treats all given plugins | |
172 | // as pre-versioning (version 0.0.0). This mechanism will eventually be | |
173 | // phased out, with vendor directories being the intended replacement. | |
174 | func (s PluginMetaSet) OverridePaths(paths map[string]string) PluginMetaSet { | |
175 | ret := make(PluginMetaSet) | |
176 | for p := range s { | |
177 | if _, ok := paths[p.Name]; ok { | |
178 | // Skip plugins that we're overridding | |
179 | continue | |
180 | } | |
181 | ||
182 | ret.Add(p) | |
183 | } | |
184 | ||
185 | // Now add the metadata for overriding plugins | |
186 | for name, path := range paths { | |
187 | ret.Add(PluginMeta{ | |
188 | Name: name, | |
189 | Version: VersionZero, | |
190 | Path: path, | |
191 | }) | |
192 | } | |
193 | ||
194 | return ret | |
195 | } |