github.com/lymingtonprecision/terraform@v0.9.9-0.20170613092852-62acef9611a9/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  // ByName groups the metas in the set by their Names, returning a map.
    64  func (s PluginMetaSet) ByName() map[string]PluginMetaSet {
    65  	ret := make(map[string]PluginMetaSet)
    66  	for p := range s {
    67  		if _, ok := ret[p.Name]; !ok {
    68  			ret[p.Name] = make(PluginMetaSet)
    69  		}
    70  		ret[p.Name].Add(p)
    71  	}
    72  	return ret
    73  }
    74  
    75  // Newest returns the one item from the set that has the newest Version value.
    76  //
    77  // The result is meaningful only if the set is already filtered such that
    78  // all of the metas have the same Name.
    79  //
    80  // If there isn't at least one meta in the set then this function will panic.
    81  // Use Count() to ensure that there is at least one value before calling.
    82  //
    83  // If any of the metas have invalid version strings then this function will
    84  // panic. Use ValidateVersions() first to filter out metas with invalid
    85  // versions.
    86  //
    87  // If two metas have the same Version then one is arbitrarily chosen. This
    88  // situation should be avoided by pre-filtering the set.
    89  func (s PluginMetaSet) Newest() PluginMeta {
    90  	if len(s) == 0 {
    91  		panic("can't call NewestStable on empty PluginMetaSet")
    92  	}
    93  
    94  	var first = true
    95  	var winner PluginMeta
    96  	var winnerVersion Version
    97  	for p := range s {
    98  		version, err := p.Version.Parse()
    99  		if err != nil {
   100  			panic(err)
   101  		}
   102  
   103  		if first == true || version.NewerThan(winnerVersion) {
   104  			winner = p
   105  			winnerVersion = version
   106  			first = false
   107  		}
   108  	}
   109  
   110  	return winner
   111  }
   112  
   113  // ConstrainVersions takes a set of requirements and attempts to
   114  // return a map from name to a set of metas that have the matching
   115  // name and an appropriate version.
   116  //
   117  // If any of the given requirements match *no* plugins then its PluginMetaSet
   118  // in the returned map will be empty.
   119  //
   120  // All viable metas are returned, so the caller can apply any desired filtering
   121  // to reduce down to a single option. For example, calling Newest() to obtain
   122  // the highest available version.
   123  //
   124  // If any of the metas in the set have invalid version strings then this
   125  // function will panic. Use ValidateVersions() first to filter out metas with
   126  // invalid versions.
   127  func (s PluginMetaSet) ConstrainVersions(reqd PluginRequirements) map[string]PluginMetaSet {
   128  	ret := make(map[string]PluginMetaSet)
   129  	for p := range s {
   130  		name := p.Name
   131  		allowedVersions, ok := reqd[name]
   132  		if !ok {
   133  			continue
   134  		}
   135  		if _, ok := ret[p.Name]; !ok {
   136  			ret[p.Name] = make(PluginMetaSet)
   137  		}
   138  		version, err := p.Version.Parse()
   139  		if err != nil {
   140  			panic(err)
   141  		}
   142  		if allowedVersions.Allows(version) {
   143  			ret[p.Name].Add(p)
   144  		}
   145  	}
   146  	return ret
   147  }
   148  
   149  // OverridePaths returns a new set where any existing plugins with the given
   150  // names are removed and replaced with the single path given in the map.
   151  //
   152  // This is here only to continue to support the legacy way of overriding
   153  // plugin binaries in the .terraformrc file. It treats all given plugins
   154  // as pre-versioning (version 0.0.0). This mechanism will eventually be
   155  // phased out, with vendor directories being the intended replacement.
   156  func (s PluginMetaSet) OverridePaths(paths map[string]string) PluginMetaSet {
   157  	ret := make(PluginMetaSet)
   158  	for p := range s {
   159  		if _, ok := paths[p.Name]; ok {
   160  			// Skip plugins that we're overridding
   161  			continue
   162  		}
   163  
   164  		ret.Add(p)
   165  	}
   166  
   167  	// Now add the metadata for overriding plugins
   168  	for name, path := range paths {
   169  		ret.Add(PluginMeta{
   170  			Name:    name,
   171  			Version: "0.0.0",
   172  			Path:    path,
   173  		})
   174  	}
   175  
   176  	return ret
   177  }