diff options
Diffstat (limited to 'pkg')
| -rw-r--r-- | pkg/image/version.go | 36 | ||||
| -rw-r--r-- | pkg/tag/tag.go | 47 | ||||
| -rw-r--r-- | pkg/tag/tag_test.go | 45 |
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)) + }) } |
