github.com/jhixson74/hashicorp-terraform@v0.11.12-beta1/plugin/discovery/meta_set.go (about)

     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;
    66  // it will ignore any plugin metas that have a invalid version strings.
    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  }