182 lines
5.0 KiB
Go
182 lines
5.0 KiB
Go
package discovery
|
|
|
|
import (
|
|
"github.com/blang/semver"
|
|
)
|
|
|
|
// 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.VersionObj(); 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
|
|
}
|
|
|
|
// 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 semver.Version
|
|
for p := range s {
|
|
version, err := p.VersionObj()
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
if first == true || version.GT(winnerVersion) {
|
|
winner = p
|
|
winnerVersion = version
|
|
first = false
|
|
}
|
|
}
|
|
|
|
return winner
|
|
}
|
|
|
|
// ConstrainVersions takes a map of version constraints by name 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 constraints match *no* plugins then its PluginMetaSet
|
|
// in the returned map will be nil.
|
|
//
|
|
// 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 map[string]semver.Range) map[string]PluginMetaSet {
|
|
ret := make(map[string]PluginMetaSet)
|
|
for p := range s {
|
|
name := p.Name
|
|
constraint, ok := reqd[name]
|
|
if !ok {
|
|
continue
|
|
}
|
|
if _, ok := ret[p.Name]; !ok {
|
|
ret[p.Name] = make(PluginMetaSet)
|
|
}
|
|
version, err := p.VersionObj()
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
if constraint(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: "0.0.0",
|
|
Path: path,
|
|
})
|
|
}
|
|
|
|
return ret
|
|
}
|