summaryrefslogtreecommitdiff
path: root/pkg
diff options
context:
space:
mode:
authorjannfis <jann@mistrust.net>2020-08-09 20:21:49 +0200
committerGitHub <noreply@github.com>2020-08-09 20:21:49 +0200
commite2aeb7fbfef6eb0d82ec1445bbf3dba725abece4 (patch)
tree74711954fe2f63d7fbff74c332a88ff3c4b23e38 /pkg
parent50d925aae850ac8c0a5e9f345478439ec85eed7a (diff)
enhancement: Allow sorting of tags by date, semver or name (#23)
Diffstat (limited to 'pkg')
-rw-r--r--pkg/image/version.go36
-rw-r--r--pkg/tag/tag.go47
-rw-r--r--pkg/tag/tag_test.go45
3 files changed, 112 insertions, 16 deletions
diff --git a/pkg/image/version.go b/pkg/image/version.go
index 09c9871..fb8e16f 100644
--- a/pkg/image/version.go
+++ b/pkg/image/version.go
@@ -1,9 +1,6 @@
package image
import (
- "sort"
- "time"
-
"github.com/argoproj-labs/argocd-image-updater/pkg/log"
"github.com/argoproj-labs/argocd-image-updater/pkg/tag"
@@ -14,12 +11,12 @@ import (
type VersionSort int
const (
- // VersionSortSemVer sorts tags using semver sorting
- VersionSortSemVer = 1
+ // VersionSortSemVer sorts tags using semver sorting (the default)
+ VersionSortSemVer = 0
// VersionSortLatest sorts tags after their creation date
- VersionSortLatest = 2
+ VersionSortLatest = 1
// VersionSortName sorts tags alphabetically by name
- VersionSortName = 3
+ VersionSortName = 2
)
// VersionConstraint defines a constraint for comparing versions
@@ -41,7 +38,17 @@ func (img *ContainerImage) GetNewestVersionFromTags(vc *VersionConstraint, tagLi
logCtx := log.NewContext()
logCtx.AddField("image", img.String())
- availableTags := tagList.Tags()
+ var availableTags tag.SortableImageTagList
+ switch vc.SortMode {
+ case VersionSortSemVer:
+ availableTags = tagList.SortBySemVer()
+ case VersionSortName:
+ availableTags = tagList.SortByName()
+ case VersionSortLatest:
+ availableTags = tagList.SortByDate()
+ }
+
+ considerTags := tag.SortableImageTagList{}
// It makes no sense to proceed if we have no available tags
if len(availableTags) == 0 {
@@ -63,13 +70,11 @@ func (img *ContainerImage) GetNewestVersionFromTags(vc *VersionConstraint, tagLi
}
}
- tagVersions := make([]*semver.Version, 0)
-
// Loop through all tags to check whether it's an update candidate.
for _, tag := range availableTags {
// Non-parseable tag does not mean error - just skip it
- ver, err := semver.NewVersion(tag)
+ ver, err := semver.NewVersion(tag.TagName)
if err != nil {
continue
}
@@ -83,16 +88,15 @@ func (img *ContainerImage) GetNewestVersionFromTags(vc *VersionConstraint, tagLi
}
// Append tag as update candidate
- tagVersions = append(tagVersions, ver)
+ considerTags = append(considerTags, tag)
}
- logCtx.Debugf("found %d from %d tags eligible for consideration", len(tagVersions), len(availableTags))
+ logCtx.Debugf("found %d from %d tags eligible for consideration", len(considerTags), len(availableTags))
// Sort update candidates and return the most recent version in its original
// form, so we can later fetch it from the registry.
- if len(tagVersions) > 0 {
- sort.Sort(semver.Collection(tagVersions))
- return tag.NewImageTag(tagVersions[len(tagVersions)-1].Original(), time.Unix(0, 0)), nil
+ if len(considerTags) > 0 {
+ return considerTags[len(considerTags)-1], nil
} else {
return img.ImageTag, nil
}
diff --git a/pkg/tag/tag.go b/pkg/tag/tag.go
index 512962b..69edc6f 100644
--- a/pkg/tag/tag.go
+++ b/pkg/tag/tag.go
@@ -4,6 +4,10 @@ import (
"sort"
"sync"
"time"
+
+ "github.com/argoproj-labs/argocd-image-updater/pkg/log"
+
+ "github.com/Masterminds/semver"
)
// ImageTag is a representation of an image tag with metadata
@@ -60,6 +64,15 @@ func (il *ImageTagList) Tags() []string {
return tagList
}
+// Tags returns a list of verbatim tag names as string slice
+func (sil *SortableImageTagList) Tags() []string {
+ tagList := []string{}
+ for _, t := range *sil {
+ tagList = append(tagList, t.TagName)
+ }
+ return tagList
+}
+
// String returns the tag name of the ImageTag
func (tag *ImageTag) String() string {
return tag.TagName
@@ -92,6 +105,40 @@ func (il ImageTagList) SortByName() SortableImageTagList {
return sil
}
+// SortByDate returns a SortableImageTagList, sorted by the tag's date
+func (il ImageTagList) SortByDate() SortableImageTagList {
+ sil := SortableImageTagList{}
+ for _, v := range il.items {
+ sil = append(sil, v)
+ }
+ sort.Slice(sil, func(i, j int) bool {
+ return sil[i].TagDate.Before(*sil[j].TagDate)
+ })
+ return sil
+}
+
+func (il ImageTagList) SortBySemVer() SortableImageTagList {
+ // We need a read lock, because we access the items hash after sorting
+ il.lock.RLock()
+ defer il.lock.RUnlock()
+
+ sil := SortableImageTagList{}
+ svl := make([]*semver.Version, 0)
+ for _, v := range il.items {
+ svi, err := semver.NewVersion(v.TagName)
+ if err != nil {
+ log.Debugf("could not parse input tag %s as semver: %v", v.TagName, err)
+ continue
+ }
+ svl = append(svl, svi)
+ }
+ sort.Sort(semver.Collection(svl))
+ for _, svi := range svl {
+ sil = append(sil, NewImageTag(svi.Original(), *il.items[svi.Original()].TagDate))
+ }
+ return sil
+}
+
// Should only be used in a method that holds a lock on the ImageTagList
func (il ImageTagList) unlockedContains(tag *ImageTag) bool {
if _, ok := il.items[tag.TagName]; ok {
diff --git a/pkg/tag/tag_test.go b/pkg/tag/tag_test.go
index eb21f1c..28128d8 100644
--- a/pkg/tag/tag_test.go
+++ b/pkg/tag/tag_test.go
@@ -65,6 +65,39 @@ func Test_SortableImageTagList(t *testing.T) {
assert.Equal(t, "wohoo", sil[3].TagName)
assert.Equal(t, "zebra", sil[4].TagName)
})
+
+ t.Run("Sort by semver", func(t *testing.T) {
+ names := []string{"v2.0.2", "v1.0", "v1.0.1", "v2.0.3", "v2.0"}
+ il := NewImageTagList()
+ for _, name := range names {
+ tag := NewImageTag(name, time.Now())
+ il.Add(tag)
+ }
+ sil := il.SortBySemVer()
+ require.Len(t, sil, len(names))
+ assert.Equal(t, "v1.0", sil[0].TagName)
+ assert.Equal(t, "v1.0.1", sil[1].TagName)
+ assert.Equal(t, "v2.0", sil[2].TagName)
+ assert.Equal(t, "v2.0.2", sil[3].TagName)
+ assert.Equal(t, "v2.0.3", sil[4].TagName)
+ })
+
+ t.Run("Sort by date", func(t *testing.T) {
+ names := []string{"v2.0.2", "v1.0", "v1.0.1", "v2.0.3", "v2.0"}
+ dates := []int64{4, 1, 0, 3, 2}
+ il := NewImageTagList()
+ for i, name := range names {
+ tag := NewImageTag(name, time.Unix(dates[i], 0))
+ il.Add(tag)
+ }
+ sil := il.SortByDate()
+ require.Len(t, sil, len(names))
+ assert.Equal(t, "v1.0.1", sil[0].TagName)
+ assert.Equal(t, "v1.0", sil[1].TagName)
+ assert.Equal(t, "v2.0", sil[2].TagName)
+ assert.Equal(t, "v2.0.3", sil[3].TagName)
+ assert.Equal(t, "v2.0.2", sil[4].TagName)
+ })
}
func Test_TagsFromTagList(t *testing.T) {
@@ -79,4 +112,16 @@ func Test_TagsFromTagList(t *testing.T) {
assert.NotEmpty(t, tl)
assert.Len(t, tl, len(names))
})
+
+ t.Run("Get list of tags from SortableImageTagList", func(t *testing.T) {
+ names := []string{"wohoo", "bazar", "alpha", "jesus", "zebra"}
+ sil := SortableImageTagList{}
+ for _, name := range names {
+ tag := NewImageTag(name, time.Now())
+ sil = append(sil, tag)
+ }
+ tl := sil.Tags()
+ assert.NotEmpty(t, tl)
+ assert.Len(t, tl, len(names))
+ })
}