version.go 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484
  1. /*
  2. Copyright 2016 The Kubernetes Authors.
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. */
  13. package version
  14. import (
  15. "bytes"
  16. "errors"
  17. "fmt"
  18. "regexp"
  19. "strconv"
  20. "strings"
  21. apimachineryversion "k8s.io/apimachinery/pkg/version"
  22. )
  23. // Version is an opaque representation of a version number
  24. type Version struct {
  25. components []uint
  26. semver bool
  27. preRelease string
  28. buildMetadata string
  29. }
  30. var (
  31. // versionMatchRE splits a version string into numeric and "extra" parts
  32. versionMatchRE = regexp.MustCompile(`^\s*v?([0-9]+(?:\.[0-9]+)*)(.*)*$`)
  33. // extraMatchRE splits the "extra" part of versionMatchRE into semver pre-release and build metadata; it does not validate the "no leading zeroes" constraint for pre-release
  34. extraMatchRE = regexp.MustCompile(`^(?:-([0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?(?:\+([0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?\s*$`)
  35. )
  36. func parse(str string, semver bool) (*Version, error) {
  37. parts := versionMatchRE.FindStringSubmatch(str)
  38. if parts == nil {
  39. return nil, fmt.Errorf("could not parse %q as version", str)
  40. }
  41. numbers, extra := parts[1], parts[2]
  42. components := strings.Split(numbers, ".")
  43. if (semver && len(components) != 3) || (!semver && len(components) < 2) {
  44. return nil, fmt.Errorf("illegal version string %q", str)
  45. }
  46. v := &Version{
  47. components: make([]uint, len(components)),
  48. semver: semver,
  49. }
  50. for i, comp := range components {
  51. if (i == 0 || semver) && strings.HasPrefix(comp, "0") && comp != "0" {
  52. return nil, fmt.Errorf("illegal zero-prefixed version component %q in %q", comp, str)
  53. }
  54. num, err := strconv.ParseUint(comp, 10, 0)
  55. if err != nil {
  56. return nil, fmt.Errorf("illegal non-numeric version component %q in %q: %v", comp, str, err)
  57. }
  58. v.components[i] = uint(num)
  59. }
  60. if semver && extra != "" {
  61. extraParts := extraMatchRE.FindStringSubmatch(extra)
  62. if extraParts == nil {
  63. return nil, fmt.Errorf("could not parse pre-release/metadata (%s) in version %q", extra, str)
  64. }
  65. v.preRelease, v.buildMetadata = extraParts[1], extraParts[2]
  66. for _, comp := range strings.Split(v.preRelease, ".") {
  67. if _, err := strconv.ParseUint(comp, 10, 0); err == nil {
  68. if strings.HasPrefix(comp, "0") && comp != "0" {
  69. return nil, fmt.Errorf("illegal zero-prefixed version component %q in %q", comp, str)
  70. }
  71. }
  72. }
  73. }
  74. return v, nil
  75. }
  76. // HighestSupportedVersion returns the highest supported version
  77. // This function assumes that the highest supported version must be v1.x.
  78. func HighestSupportedVersion(versions []string) (*Version, error) {
  79. if len(versions) == 0 {
  80. return nil, errors.New("empty array for supported versions")
  81. }
  82. var (
  83. highestSupportedVersion *Version
  84. theErr error
  85. )
  86. for i := len(versions) - 1; i >= 0; i-- {
  87. currentHighestVer, err := ParseGeneric(versions[i])
  88. if err != nil {
  89. theErr = err
  90. continue
  91. }
  92. if currentHighestVer.Major() > 1 {
  93. continue
  94. }
  95. if highestSupportedVersion == nil || highestSupportedVersion.LessThan(currentHighestVer) {
  96. highestSupportedVersion = currentHighestVer
  97. }
  98. }
  99. if highestSupportedVersion == nil {
  100. return nil, fmt.Errorf(
  101. "could not find a highest supported version from versions (%v) reported: %+v",
  102. versions, theErr)
  103. }
  104. if highestSupportedVersion.Major() != 1 {
  105. return nil, fmt.Errorf("highest supported version reported is %v, must be v1.x", highestSupportedVersion)
  106. }
  107. return highestSupportedVersion, nil
  108. }
  109. // ParseGeneric parses a "generic" version string. The version string must consist of two
  110. // or more dot-separated numeric fields (the first of which can't have leading zeroes),
  111. // followed by arbitrary uninterpreted data (which need not be separated from the final
  112. // numeric field by punctuation). For convenience, leading and trailing whitespace is
  113. // ignored, and the version can be preceded by the letter "v". See also ParseSemantic.
  114. func ParseGeneric(str string) (*Version, error) {
  115. return parse(str, false)
  116. }
  117. // MustParseGeneric is like ParseGeneric except that it panics on error
  118. func MustParseGeneric(str string) *Version {
  119. v, err := ParseGeneric(str)
  120. if err != nil {
  121. panic(err)
  122. }
  123. return v
  124. }
  125. // Parse tries to do ParseSemantic first to keep more information.
  126. // If ParseSemantic fails, it would just do ParseGeneric.
  127. func Parse(str string) (*Version, error) {
  128. v, err := parse(str, true)
  129. if err != nil {
  130. return parse(str, false)
  131. }
  132. return v, err
  133. }
  134. // MustParse is like Parse except that it panics on error
  135. func MustParse(str string) *Version {
  136. v, err := Parse(str)
  137. if err != nil {
  138. panic(err)
  139. }
  140. return v
  141. }
  142. // ParseMajorMinor parses a "generic" version string and returns a version with the major and minor version.
  143. func ParseMajorMinor(str string) (*Version, error) {
  144. v, err := ParseGeneric(str)
  145. if err != nil {
  146. return nil, err
  147. }
  148. return MajorMinor(v.Major(), v.Minor()), nil
  149. }
  150. // MustParseMajorMinor is like ParseMajorMinor except that it panics on error
  151. func MustParseMajorMinor(str string) *Version {
  152. v, err := ParseMajorMinor(str)
  153. if err != nil {
  154. panic(err)
  155. }
  156. return v
  157. }
  158. // ParseSemantic parses a version string that exactly obeys the syntax and semantics of
  159. // the "Semantic Versioning" specification (http://semver.org/) (although it ignores
  160. // leading and trailing whitespace, and allows the version to be preceded by "v"). For
  161. // version strings that are not guaranteed to obey the Semantic Versioning syntax, use
  162. // ParseGeneric.
  163. func ParseSemantic(str string) (*Version, error) {
  164. return parse(str, true)
  165. }
  166. // MustParseSemantic is like ParseSemantic except that it panics on error
  167. func MustParseSemantic(str string) *Version {
  168. v, err := ParseSemantic(str)
  169. if err != nil {
  170. panic(err)
  171. }
  172. return v
  173. }
  174. // MajorMinor returns a version with the provided major and minor version.
  175. func MajorMinor(major, minor uint) *Version {
  176. return &Version{components: []uint{major, minor}}
  177. }
  178. // Major returns the major release number
  179. func (v *Version) Major() uint {
  180. return v.components[0]
  181. }
  182. // Minor returns the minor release number
  183. func (v *Version) Minor() uint {
  184. return v.components[1]
  185. }
  186. // Patch returns the patch release number if v is a Semantic Version, or 0
  187. func (v *Version) Patch() uint {
  188. if len(v.components) < 3 {
  189. return 0
  190. }
  191. return v.components[2]
  192. }
  193. // BuildMetadata returns the build metadata, if v is a Semantic Version, or ""
  194. func (v *Version) BuildMetadata() string {
  195. return v.buildMetadata
  196. }
  197. // PreRelease returns the prerelease metadata, if v is a Semantic Version, or ""
  198. func (v *Version) PreRelease() string {
  199. return v.preRelease
  200. }
  201. // Components returns the version number components
  202. func (v *Version) Components() []uint {
  203. return v.components
  204. }
  205. // WithMajor returns copy of the version object with requested major number
  206. func (v *Version) WithMajor(major uint) *Version {
  207. result := *v
  208. result.components = []uint{major, v.Minor(), v.Patch()}
  209. return &result
  210. }
  211. // WithMinor returns copy of the version object with requested minor number
  212. func (v *Version) WithMinor(minor uint) *Version {
  213. result := *v
  214. result.components = []uint{v.Major(), minor, v.Patch()}
  215. return &result
  216. }
  217. // SubtractMinor returns the version with offset from the original minor, with the same major and no patch.
  218. // If -offset >= current minor, the minor would be 0.
  219. func (v *Version) OffsetMinor(offset int) *Version {
  220. var minor uint
  221. if offset >= 0 {
  222. minor = v.Minor() + uint(offset)
  223. } else {
  224. diff := uint(-offset)
  225. if diff < v.Minor() {
  226. minor = v.Minor() - diff
  227. }
  228. }
  229. return MajorMinor(v.Major(), minor)
  230. }
  231. // SubtractMinor returns the version diff minor versions back, with the same major and no patch.
  232. // If diff >= current minor, the minor would be 0.
  233. func (v *Version) SubtractMinor(diff uint) *Version {
  234. return v.OffsetMinor(-int(diff))
  235. }
  236. // AddMinor returns the version diff minor versions forward, with the same major and no patch.
  237. func (v *Version) AddMinor(diff uint) *Version {
  238. return v.OffsetMinor(int(diff))
  239. }
  240. // WithPatch returns copy of the version object with requested patch number
  241. func (v *Version) WithPatch(patch uint) *Version {
  242. result := *v
  243. result.components = []uint{v.Major(), v.Minor(), patch}
  244. return &result
  245. }
  246. // WithPreRelease returns copy of the version object with requested prerelease
  247. func (v *Version) WithPreRelease(preRelease string) *Version {
  248. if len(preRelease) == 0 {
  249. return v
  250. }
  251. result := *v
  252. result.components = []uint{v.Major(), v.Minor(), v.Patch()}
  253. result.preRelease = preRelease
  254. return &result
  255. }
  256. // WithBuildMetadata returns copy of the version object with requested buildMetadata
  257. func (v *Version) WithBuildMetadata(buildMetadata string) *Version {
  258. result := *v
  259. result.components = []uint{v.Major(), v.Minor(), v.Patch()}
  260. result.buildMetadata = buildMetadata
  261. return &result
  262. }
  263. // String converts a Version back to a string; note that for versions parsed with
  264. // ParseGeneric, this will not include the trailing uninterpreted portion of the version
  265. // number.
  266. func (v *Version) String() string {
  267. if v == nil {
  268. return "<nil>"
  269. }
  270. var buffer bytes.Buffer
  271. for i, comp := range v.components {
  272. if i > 0 {
  273. buffer.WriteString(".")
  274. }
  275. buffer.WriteString(fmt.Sprintf("%d", comp))
  276. }
  277. if v.preRelease != "" {
  278. buffer.WriteString("-")
  279. buffer.WriteString(v.preRelease)
  280. }
  281. if v.buildMetadata != "" {
  282. buffer.WriteString("+")
  283. buffer.WriteString(v.buildMetadata)
  284. }
  285. return buffer.String()
  286. }
  287. // compareInternal returns -1 if v is less than other, 1 if it is greater than other, or 0
  288. // if they are equal
  289. func (v *Version) compareInternal(other *Version) int {
  290. vLen := len(v.components)
  291. oLen := len(other.components)
  292. for i := 0; i < vLen && i < oLen; i++ {
  293. switch {
  294. case other.components[i] < v.components[i]:
  295. return 1
  296. case other.components[i] > v.components[i]:
  297. return -1
  298. }
  299. }
  300. // If components are common but one has more items and they are not zeros, it is bigger
  301. switch {
  302. case oLen < vLen && !onlyZeros(v.components[oLen:]):
  303. return 1
  304. case oLen > vLen && !onlyZeros(other.components[vLen:]):
  305. return -1
  306. }
  307. if !v.semver || !other.semver {
  308. return 0
  309. }
  310. switch {
  311. case v.preRelease == "" && other.preRelease != "":
  312. return 1
  313. case v.preRelease != "" && other.preRelease == "":
  314. return -1
  315. case v.preRelease == other.preRelease: // includes case where both are ""
  316. return 0
  317. }
  318. vPR := strings.Split(v.preRelease, ".")
  319. oPR := strings.Split(other.preRelease, ".")
  320. for i := 0; i < len(vPR) && i < len(oPR); i++ {
  321. vNum, err := strconv.ParseUint(vPR[i], 10, 0)
  322. if err == nil {
  323. oNum, err := strconv.ParseUint(oPR[i], 10, 0)
  324. if err == nil {
  325. switch {
  326. case oNum < vNum:
  327. return 1
  328. case oNum > vNum:
  329. return -1
  330. default:
  331. continue
  332. }
  333. }
  334. }
  335. if oPR[i] < vPR[i] {
  336. return 1
  337. } else if oPR[i] > vPR[i] {
  338. return -1
  339. }
  340. }
  341. switch {
  342. case len(oPR) < len(vPR):
  343. return 1
  344. case len(oPR) > len(vPR):
  345. return -1
  346. }
  347. return 0
  348. }
  349. // returns false if array contain any non-zero element
  350. func onlyZeros(array []uint) bool {
  351. for _, num := range array {
  352. if num != 0 {
  353. return false
  354. }
  355. }
  356. return true
  357. }
  358. // EqualTo tests if a version is equal to a given version.
  359. func (v *Version) EqualTo(other *Version) bool {
  360. if v == nil {
  361. return other == nil
  362. }
  363. if other == nil {
  364. return false
  365. }
  366. return v.compareInternal(other) == 0
  367. }
  368. // AtLeast tests if a version is at least equal to a given minimum version. If both
  369. // Versions are Semantic Versions, this will use the Semantic Version comparison
  370. // algorithm. Otherwise, it will compare only the numeric components, with non-present
  371. // components being considered "0" (ie, "1.4" is equal to "1.4.0").
  372. func (v *Version) AtLeast(min *Version) bool {
  373. return v.compareInternal(min) != -1
  374. }
  375. // LessThan tests if a version is less than a given version. (It is exactly the opposite
  376. // of AtLeast, for situations where asking "is v too old?" makes more sense than asking
  377. // "is v new enough?".)
  378. func (v *Version) LessThan(other *Version) bool {
  379. return v.compareInternal(other) == -1
  380. }
  381. // GreaterThan tests if a version is greater than a given version.
  382. func (v *Version) GreaterThan(other *Version) bool {
  383. return v.compareInternal(other) == 1
  384. }
  385. // Compare compares v against a version string (which will be parsed as either Semantic
  386. // or non-Semantic depending on v). On success it returns -1 if v is less than other, 1 if
  387. // it is greater than other, or 0 if they are equal.
  388. func (v *Version) Compare(other string) (int, error) {
  389. ov, err := parse(other, v.semver)
  390. if err != nil {
  391. return 0, err
  392. }
  393. return v.compareInternal(ov), nil
  394. }
  395. // WithInfo returns copy of the version object.
  396. // Deprecated: The Info field has been removed from the Version struct. This method no longer modifies the Version object.
  397. func (v *Version) WithInfo(info apimachineryversion.Info) *Version {
  398. result := *v
  399. return &result
  400. }
  401. // Info returns the version information of a component.
  402. // Deprecated: Use Info() from effective version instead.
  403. func (v *Version) Info() *apimachineryversion.Info {
  404. if v == nil {
  405. return nil
  406. }
  407. // in case info is empty, or the major and minor in info is different from the actual major and minor
  408. return &apimachineryversion.Info{
  409. Major: Itoa(v.Major()),
  410. Minor: Itoa(v.Minor()),
  411. GitVersion: v.String(),
  412. }
  413. }
  414. func Itoa(i uint) string {
  415. if i == 0 {
  416. return ""
  417. }
  418. return strconv.Itoa(int(i))
  419. }