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 }