github.com/wikibal01/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 }