Skip to content

Commit c225984

Browse files
Merge pull request #122891 from siyuanfoundation/api-comp-ver1
apimachinery: API Emulation Versioning Kubernetes-commit: 7a6062f4c13d0b7407876b325ce52c43de18f92f
2 parents a05248b + f7e861d commit c225984

File tree

2 files changed

+208
-0
lines changed

2 files changed

+208
-0
lines changed

pkg/util/version/version.go

+112
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ import (
2323
"regexp"
2424
"strconv"
2525
"strings"
26+
27+
apimachineryversion "k8s.io/apimachinery/pkg/version"
2628
)
2729

2830
// Version is an opaque representation of a version number
@@ -31,6 +33,7 @@ type Version struct {
3133
semver bool
3234
preRelease string
3335
buildMetadata string
36+
info apimachineryversion.Info
3437
}
3538

3639
var (
@@ -145,6 +148,43 @@ func MustParseGeneric(str string) *Version {
145148
return v
146149
}
147150

151+
// Parse tries to do ParseSemantic first to keep more information.
152+
// If ParseSemantic fails, it would just do ParseGeneric.
153+
func Parse(str string) (*Version, error) {
154+
v, err := parse(str, true)
155+
if err != nil {
156+
return parse(str, false)
157+
}
158+
return v, err
159+
}
160+
161+
// MustParse is like Parse except that it panics on error
162+
func MustParse(str string) *Version {
163+
v, err := Parse(str)
164+
if err != nil {
165+
panic(err)
166+
}
167+
return v
168+
}
169+
170+
// ParseMajorMinor parses a "generic" version string and returns a version with the major and minor version.
171+
func ParseMajorMinor(str string) (*Version, error) {
172+
v, err := ParseGeneric(str)
173+
if err != nil {
174+
return nil, err
175+
}
176+
return MajorMinor(v.Major(), v.Minor()), nil
177+
}
178+
179+
// MustParseMajorMinor is like ParseMajorMinor except that it panics on error
180+
func MustParseMajorMinor(str string) *Version {
181+
v, err := ParseMajorMinor(str)
182+
if err != nil {
183+
panic(err)
184+
}
185+
return v
186+
}
187+
148188
// ParseSemantic parses a version string that exactly obeys the syntax and semantics of
149189
// the "Semantic Versioning" specification (http://semver.org/) (although it ignores
150190
// leading and trailing whitespace, and allows the version to be preceded by "v"). For
@@ -215,6 +255,32 @@ func (v *Version) WithMinor(minor uint) *Version {
215255
return &result
216256
}
217257

258+
// SubtractMinor returns the version with offset from the original minor, with the same major and no patch.
259+
// If -offset >= current minor, the minor would be 0.
260+
func (v *Version) OffsetMinor(offset int) *Version {
261+
var minor uint
262+
if offset >= 0 {
263+
minor = v.Minor() + uint(offset)
264+
} else {
265+
diff := uint(-offset)
266+
if diff < v.Minor() {
267+
minor = v.Minor() - diff
268+
}
269+
}
270+
return MajorMinor(v.Major(), minor)
271+
}
272+
273+
// SubtractMinor returns the version diff minor versions back, with the same major and no patch.
274+
// If diff >= current minor, the minor would be 0.
275+
func (v *Version) SubtractMinor(diff uint) *Version {
276+
return v.OffsetMinor(-int(diff))
277+
}
278+
279+
// AddMinor returns the version diff minor versions forward, with the same major and no patch.
280+
func (v *Version) AddMinor(diff uint) *Version {
281+
return v.OffsetMinor(int(diff))
282+
}
283+
218284
// WithPatch returns copy of the version object with requested patch number
219285
func (v *Version) WithPatch(patch uint) *Version {
220286
result := *v
@@ -224,6 +290,9 @@ func (v *Version) WithPatch(patch uint) *Version {
224290

225291
// WithPreRelease returns copy of the version object with requested prerelease
226292
func (v *Version) WithPreRelease(preRelease string) *Version {
293+
if len(preRelease) == 0 {
294+
return v
295+
}
227296
result := *v
228297
result.components = []uint{v.Major(), v.Minor(), v.Patch()}
229298
result.preRelease = preRelease
@@ -345,6 +414,17 @@ func onlyZeros(array []uint) bool {
345414
return true
346415
}
347416

417+
// EqualTo tests if a version is equal to a given version.
418+
func (v *Version) EqualTo(other *Version) bool {
419+
if v == nil {
420+
return other == nil
421+
}
422+
if other == nil {
423+
return false
424+
}
425+
return v.compareInternal(other) == 0
426+
}
427+
348428
// AtLeast tests if a version is at least equal to a given minimum version. If both
349429
// Versions are Semantic Versions, this will use the Semantic Version comparison
350430
// algorithm. Otherwise, it will compare only the numeric components, with non-present
@@ -360,6 +440,11 @@ func (v *Version) LessThan(other *Version) bool {
360440
return v.compareInternal(other) == -1
361441
}
362442

443+
// GreaterThan tests if a version is greater than a given version.
444+
func (v *Version) GreaterThan(other *Version) bool {
445+
return v.compareInternal(other) == 1
446+
}
447+
363448
// Compare compares v against a version string (which will be parsed as either Semantic
364449
// or non-Semantic depending on v). On success it returns -1 if v is less than other, 1 if
365450
// it is greater than other, or 0 if they are equal.
@@ -370,3 +455,30 @@ func (v *Version) Compare(other string) (int, error) {
370455
}
371456
return v.compareInternal(ov), nil
372457
}
458+
459+
// WithInfo returns copy of the version object with requested info
460+
func (v *Version) WithInfo(info apimachineryversion.Info) *Version {
461+
result := *v
462+
result.info = info
463+
return &result
464+
}
465+
466+
func (v *Version) Info() *apimachineryversion.Info {
467+
if v == nil {
468+
return nil
469+
}
470+
// in case info is empty, or the major and minor in info is different from the actual major and minor
471+
v.info.Major = itoa(v.Major())
472+
v.info.Minor = itoa(v.Minor())
473+
if v.info.GitVersion == "" {
474+
v.info.GitVersion = v.String()
475+
}
476+
return &v.info
477+
}
478+
479+
func itoa(i uint) string {
480+
if i == 0 {
481+
return ""
482+
}
483+
return strconv.Itoa(int(i))
484+
}

pkg/util/version/version_test.go

+96
Original file line numberDiff line numberDiff line change
@@ -452,3 +452,99 @@ func TestHighestSupportedVersion(t *testing.T) {
452452
}
453453
}
454454
}
455+
456+
func TestOffsetMinor(t *testing.T) {
457+
var tests = []struct {
458+
version string
459+
diff int
460+
expectedComponents []uint
461+
}{
462+
{
463+
version: "1.0.2",
464+
diff: -3,
465+
expectedComponents: []uint{1, 0},
466+
},
467+
{
468+
version: "1.3.2-alpha+001",
469+
diff: -2,
470+
expectedComponents: []uint{1, 1},
471+
},
472+
{
473+
version: "1.3.2-alpha+001",
474+
diff: -3,
475+
expectedComponents: []uint{1, 0},
476+
},
477+
{
478+
version: "1.20",
479+
diff: -5,
480+
expectedComponents: []uint{1, 15},
481+
},
482+
{
483+
version: "1.20",
484+
diff: 5,
485+
expectedComponents: []uint{1, 25},
486+
},
487+
}
488+
489+
for _, test := range tests {
490+
version, _ := ParseGeneric(test.version)
491+
if !reflect.DeepEqual(test.expectedComponents, version.OffsetMinor(test.diff).Components()) {
492+
t.Error("parse returned un'expected components")
493+
}
494+
}
495+
}
496+
497+
func TestParse(t *testing.T) {
498+
499+
var tests = []struct {
500+
version string
501+
expectErr bool
502+
expectedComponents []uint
503+
expectedPreRelease string
504+
expectedBuildMetadata string
505+
}{
506+
{
507+
version: "1.0.2",
508+
expectedComponents: []uint{1, 0, 2},
509+
},
510+
{
511+
version: "1.0.2-alpha+001",
512+
expectedComponents: []uint{1, 0, 2},
513+
expectedPreRelease: "alpha",
514+
expectedBuildMetadata: "001",
515+
},
516+
{
517+
version: "1.2",
518+
expectedComponents: []uint{1, 2},
519+
},
520+
{
521+
version: "1.0.2-beta+exp.sha.5114f85",
522+
expectedComponents: []uint{1, 0, 2},
523+
expectedPreRelease: "beta",
524+
expectedBuildMetadata: "exp.sha.5114f85",
525+
},
526+
{
527+
version: "a.b.c",
528+
expectErr: true,
529+
},
530+
}
531+
532+
for _, test := range tests {
533+
version, err := Parse(test.version)
534+
if test.expectErr {
535+
if err == nil {
536+
t.Fatalf("got no err, expected err")
537+
}
538+
continue
539+
}
540+
if !reflect.DeepEqual(test.expectedComponents, version.Components()) {
541+
t.Error("parse returned un'expected components")
542+
}
543+
if test.expectedPreRelease != version.PreRelease() {
544+
t.Errorf("parse returned version.PreRelease %s, expected %s", test.expectedPreRelease, version.PreRelease())
545+
}
546+
if test.expectedBuildMetadata != version.BuildMetadata() {
547+
t.Errorf("parse returned version.BuildMetadata %s, expected %s", test.expectedBuildMetadata, version.BuildMetadata())
548+
}
549+
}
550+
}

0 commit comments

Comments
 (0)