aboutsummaryrefslogtreecommitdiffhomepage
path: root/vendor/github.com/hashicorp/terraform/plugin/discovery/meta_set.go
blob: 181ea1fcd67a0baa43c6f24e92c9d99035f466d2 (plain) (blame)
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
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
package discovery

// A PluginMetaSet is a set of PluginMeta objects meeting a certain criteria.
//
// Methods on this type allow filtering of the set to produce subsets that
// meet more restrictive criteria.
type PluginMetaSet map[PluginMeta]struct{}

// Add inserts the given PluginMeta into the receiving set. This is a no-op
// if the given meta is already present.
func (s PluginMetaSet) Add(p PluginMeta) {
	s[p] = struct{}{}
}

// Remove removes the given PluginMeta from the receiving set. This is a no-op
// if the given meta is not already present.
func (s PluginMetaSet) Remove(p PluginMeta) {
	delete(s, p)
}

// Has returns true if the given meta is in the receiving set, or false
// otherwise.
func (s PluginMetaSet) Has(p PluginMeta) bool {
	_, ok := s[p]
	return ok
}

// Count returns the number of metas in the set
func (s PluginMetaSet) Count() int {
	return len(s)
}

// ValidateVersions returns two new PluginMetaSets, separating those with
// versions that have syntax-valid semver versions from those that don't.
//
// Eliminating invalid versions from consideration (and possibly warning about
// them) is usually the first step of working with a meta set after discovery
// has completed.
func (s PluginMetaSet) ValidateVersions() (valid, invalid PluginMetaSet) {
	valid = make(PluginMetaSet)
	invalid = make(PluginMetaSet)
	for p := range s {
		if _, err := p.Version.Parse(); err == nil {
			valid.Add(p)
		} else {
			invalid.Add(p)
		}
	}
	return
}

// WithName returns the subset of metas that have the given name.
func (s PluginMetaSet) WithName(name string) PluginMetaSet {
	ns := make(PluginMetaSet)
	for p := range s {
		if p.Name == name {
			ns.Add(p)
		}
	}
	return ns
}

// WithVersion returns the subset of metas that have the given version.
//
// This should be used only with the "valid" result from ValidateVersions;
// it will ignore any plugin metas that have a invalid version strings.
func (s PluginMetaSet) WithVersion(version Version) PluginMetaSet {
	ns := make(PluginMetaSet)
	for p := range s {
		gotVersion, err := p.Version.Parse()
		if err != nil {
			continue
		}
		if gotVersion.Equal(version) {
			ns.Add(p)
		}
	}
	return ns
}

// ByName groups the metas in the set by their Names, returning a map.
func (s PluginMetaSet) ByName() map[string]PluginMetaSet {
	ret := make(map[string]PluginMetaSet)
	for p := range s {
		if _, ok := ret[p.Name]; !ok {
			ret[p.Name] = make(PluginMetaSet)
		}
		ret[p.Name].Add(p)
	}
	return ret
}

// Newest returns the one item from the set that has the newest Version value.
//
// The result is meaningful only if the set is already filtered such that
// all of the metas have the same Name.
//
// If there isn't at least one meta in the set then this function will panic.
// Use Count() to ensure that there is at least one value before calling.
//
// If any of the metas have invalid version strings then this function will
// panic. Use ValidateVersions() first to filter out metas with invalid
// versions.
//
// If two metas have the same Version then one is arbitrarily chosen. This
// situation should be avoided by pre-filtering the set.
func (s PluginMetaSet) Newest() PluginMeta {
	if len(s) == 0 {
		panic("can't call NewestStable on empty PluginMetaSet")
	}

	var first = true
	var winner PluginMeta
	var winnerVersion Version
	for p := range s {
		version, err := p.Version.Parse()
		if err != nil {
			panic(err)
		}

		if first == true || version.NewerThan(winnerVersion) {
			winner = p
			winnerVersion = version
			first = false
		}
	}

	return winner
}

// ConstrainVersions takes a set of requirements and attempts to
// return a map from name to a set of metas that have the matching
// name and an appropriate version.
//
// If any of the given requirements match *no* plugins then its PluginMetaSet
// in the returned map will be empty.
//
// All viable metas are returned, so the caller can apply any desired filtering
// to reduce down to a single option. For example, calling Newest() to obtain
// the highest available version.
//
// If any of the metas in the set have invalid version strings then this
// function will panic. Use ValidateVersions() first to filter out metas with
// invalid versions.
func (s PluginMetaSet) ConstrainVersions(reqd PluginRequirements) map[string]PluginMetaSet {
	ret := make(map[string]PluginMetaSet)
	for p := range s {
		name := p.Name
		allowedVersions, ok := reqd[name]
		if !ok {
			continue
		}
		if _, ok := ret[p.Name]; !ok {
			ret[p.Name] = make(PluginMetaSet)
		}
		version, err := p.Version.Parse()
		if err != nil {
			panic(err)
		}
		if allowedVersions.Allows(version) {
			ret[p.Name].Add(p)
		}
	}
	return ret
}

// OverridePaths returns a new set where any existing plugins with the given
// names are removed and replaced with the single path given in the map.
//
// This is here only to continue to support the legacy way of overriding
// plugin binaries in the .terraformrc file. It treats all given plugins
// as pre-versioning (version 0.0.0). This mechanism will eventually be
// phased out, with vendor directories being the intended replacement.
func (s PluginMetaSet) OverridePaths(paths map[string]string) PluginMetaSet {
	ret := make(PluginMetaSet)
	for p := range s {
		if _, ok := paths[p.Name]; ok {
			// Skip plugins that we're overridding
			continue
		}

		ret.Add(p)
	}

	// Now add the metadata for overriding plugins
	for name, path := range paths {
		ret.Add(PluginMeta{
			Name:    name,
			Version: VersionZero,
			Path:    path,
		})
	}

	return ret
}