diff --git a/argocd-image-updater b/argocd-image-updater new file mode 100755 index 00000000..9ac368df Binary files /dev/null and b/argocd-image-updater differ diff --git a/docs/basics/update-strategies.md b/docs/basics/update-strategies.md index 453cbf1c..0963a534 100644 --- a/docs/basics/update-strategies.md +++ b/docs/basics/update-strategies.md @@ -15,9 +15,13 @@ The following update strategies are currently supported: * [semver](#strategy-semver) - Update to the latest version of an image considering semantic versioning constraints -* [latest](#strategy-latest) - Update to the most recently built image found in a registry +* [latest/newest-build](#strategy-latest) - Update to the most recently built image found in a registry * [digest](#strategy-digest) - Update to the latest version of a given version (tag), using the tag's SHA digest -* [name](#strategy-name) - Sorts tags alphabetically and update to the one with the highest cardinality +* [name/alphabetical](#strategy-name) - Sorts tags alphabetically and update to the one with the highest cardinality + +!!!warning "Renamed image update strategies + The `latest` strategy has been renamed to `newest-build`, and `name` strategy has been renamed to `alphabetical`. + Please switch to the new convention as support for the old naming convention will be removed in future releases. Some of the strategies will require additional configuration, or can be tweaked with additional parameters. Please have a look at the @@ -97,23 +101,29 @@ Image Updater will pick the highest version number found in the registry. Argo CD Image Updater will omit any tags from your registry that do not match a semantic version when using the `semver` update strategy. -### latest - Update to the most recently built image +### latest/newest-build - Update to the most recently built image + + +!!!warning "Renamed image update strategies" + The `latest` strategy has been renamed to `newest-build`. + Please switch to the new convention as support for the old naming convention will be removed in future releases. + Detected usage of `latest` will result in a warning message within the image-updater controller logs. !!!warning As of November 2020, Docker Hub has introduced pull limits for accounts on - the free plan and unauthenticated requests. The `latest` update strategy + the free plan and unauthenticated requests. The `latest` or `newest-build` update strategy will perform manifest pulls for determining the most recently pushed tags, and these will count into your pull limits. So unless you are not affected - by these pull limits, it is **not recommended** to use the `latest` update + by these pull limits, it is **not recommended** to use the `latest` or `newest-build` update strategy with images hosted on Docker Hub. !!!note If you are using *reproducible builds* for your container images (e.g. if your build pipeline always sets the creation date of the image to the same - value), the `latest` strategy will not be able to determine which tag to + value), the `latest` or `newest-build` strategy will not be able to determine which tag to update to. -Strategy name: `latest` +Strategy name: `latest` or `newest-build` Basic configuration: @@ -121,6 +131,12 @@ Basic configuration: argocd-image-updater.argoproj.io/image-list: =some/image argocd-image-updater.argoproj.io/.update-strategy: latest ``` +or + +```yaml +argocd-image-updater.argoproj.io/image-list: =some/image +argocd-image-updater.argoproj.io/.update-strategy: newest-build +``` Argo CD Image Updater can update to the image that has the most recent build date, and is tagged with an arbitrary name (e.g. a Git commit SHA, or even a @@ -147,6 +163,14 @@ argocd-image-updater.argoproj.io/myimage.update-strategy: latest argocd-image-updater.argoproj.io/myimage.allow-tags: regexp:^[0-9a-f]{7}$ ``` +or + +```yaml +argocd-image-updater.argoproj.io/image-list: myimage=some/image +argocd-image-updater.argoproj.io/myimage.update-strategy: newest-build +argocd-image-updater.argoproj.io/myimage.allow-tags: regexp:^[0-9a-f]{7}$ +``` + would only consider tags that match a given regular expression for update. In this case, the regular expression matches a 7-digit hexadecimal string that could represent the short version of a Git commit SHA, so it would match tags @@ -160,6 +184,14 @@ argocd-image-updater.argoproj.io/myimage.update-strategy: latest argocd-image-updater.argoproj.io/myimage.ignore-tags: latest, master ``` +or + +```yaml +argocd-image-updater.argoproj.io/image-list: myimage=some/image +argocd-image-updater.argoproj.io/myimage.update-strategy: newest-build +argocd-image-updater.argoproj.io/myimage.ignore-tags: latest, master +``` + This would allow for considering all tags found but `latest` and `master`. You can read more about filtering tags [here](../../configuration/images/#filtering-tags). @@ -206,7 +238,13 @@ argocd-image-updater.argoproj.io/myimage.update-strategy: digest ### Update according to lexical sort -Strategy name: `name` +!!!warning "Renamed image update strategies" + The `name` strategy has been renamed to `alphabetical`. + Please switch to the new convention as support for the old naming convention will be removed in future releases. + Detected usage of `name` will result in a warning message within the image-updater controller logs. + + +Strategy name: `name` or `alphabetical` Basic configuration: @@ -214,6 +252,12 @@ Basic configuration: argocd-image-updater.argoproj.io/image-list: =some/image argocd-image-updater.argoproj.io/.update-strategy: name ``` +or + +```yaml +argocd-image-updater.argoproj.io/image-list: =some/image +argocd-image-updater.argoproj.io/.update-strategy: alphabetical +``` This update strategy sorts the tags returned by the registry in a lexical way (by name, in descending order) and picks the last tag in the list for update. @@ -231,6 +275,14 @@ argocd-image-updater.argoproj.io/myimage.update-strategy: name argocd-image-updater.argoproj.io/myimage.allow-tags: regexp:^[0-9]{4}-[0-9]{2}[0-9]{2}$ ``` +or + +```yaml +argocd-image-updater.argoproj.io/image-list: myimage=some/image +argocd-image-updater.argoproj.io/myimage.update-strategy: alphabetical +argocd-image-updater.argoproj.io/myimage.allow-tags: regexp:^[0-9]{4}-[0-9]{2}[0-9]{2}$ +``` + would only consider tags that match a given regular expression for update. In this case, only tags matching a date specification of `YYYY-MM-DD` would be considered for update. diff --git a/docs/index.md b/docs/index.md index 511c77fc..32eb091e 100644 --- a/docs/index.md +++ b/docs/index.md @@ -42,14 +42,18 @@ RBAC authorization on Application resources etc. are fully supported. ## Features +!!!warning "Renamed image update strategies" + The `latest` strategy has been renamed to `newest-build`, and `name` strategy has been renamed to `alphabetical`. + Please switch to the new convention as support for the old naming convention will be removed in future releases. + * Updates images of apps that are managed by Argo CD and are either generated from *Helm* or *Kustomize* tooling * Update app images according to different [update strategies](./basics/update-strategies.md) * `semver`: update to highest allowed version according to given image constraint, - * `latest`: update to the most recently created image tag, - * `name`: update to the last tag in an alphabetically sorted list + * `latest/newest-build`: update to the most recently created image tag, + * `name/alphabetical`: update to the last tag in an alphabetically sorted list * `digest`: update to the most recent pushed version of a mutable tag * Support for [widely used container registries](./configuration/registries.md#supported-registries) diff --git a/pkg/image/options.go b/pkg/image/options.go index df357c38..af6d12a0 100644 --- a/pkg/image/options.go +++ b/pkg/image/options.go @@ -101,9 +101,15 @@ func (img *ContainerImage) ParseUpdateStrategy(val string) UpdateStrategy { case "semver": return StrategySemVer case "latest": - return StrategyLatest + logCtx.Warnf("\"latest\" strategy has been renamed to \"newest-build\". Please switch to the new convention as support for the old naming convention will be removed in future versions.") + fallthrough + case "newest-build": + return StrategyNewestBuild case "name": - return StrategyName + logCtx.Warnf("\"name\" strategy has been renamed to \"alphabetical\". Please switch to the new convention as support for the old naming convention will be removed in future versions.") + fallthrough + case "alphabetical": + return StrategyAlphabetical case "digest": return StrategyDigest default: diff --git a/pkg/image/options_test.go b/pkg/image/options_test.go index a563eec3..1d346de1 100644 --- a/pkg/image/options_test.go +++ b/pkg/image/options_test.go @@ -85,13 +85,22 @@ func Test_GetSortOption(t *testing.T) { assert.Equal(t, StrategySemVer, sortMode) }) + t.Run("Use update strategy newest-build for configured application", func(t *testing.T) { + annotations := map[string]string{ + fmt.Sprintf(common.UpdateStrategyAnnotation, "dummy"): "newest-build", + } + img := NewFromIdentifier("dummy=foo/bar:1.12") + sortMode := img.GetParameterUpdateStrategy(annotations) + assert.Equal(t, StrategyNewestBuild, sortMode) + }) + t.Run("Get update strategy date for configured application", func(t *testing.T) { annotations := map[string]string{ fmt.Sprintf(common.UpdateStrategyAnnotation, "dummy"): "latest", } img := NewFromIdentifier("dummy=foo/bar:1.12") sortMode := img.GetParameterUpdateStrategy(annotations) - assert.Equal(t, StrategyLatest, sortMode) + assert.Equal(t, StrategyNewestBuild, sortMode) }) t.Run("Get update strategy name for configured application", func(t *testing.T) { @@ -100,7 +109,16 @@ func Test_GetSortOption(t *testing.T) { } img := NewFromIdentifier("dummy=foo/bar:1.12") sortMode := img.GetParameterUpdateStrategy(annotations) - assert.Equal(t, StrategyName, sortMode) + assert.Equal(t, StrategyAlphabetical, sortMode) + }) + + t.Run("Use update strategy alphabetical for configured application", func(t *testing.T) { + annotations := map[string]string{ + fmt.Sprintf(common.UpdateStrategyAnnotation, "dummy"): "alphabetical", + } + img := NewFromIdentifier("dummy=foo/bar:1.12") + sortMode := img.GetParameterUpdateStrategy(annotations) + assert.Equal(t, StrategyAlphabetical, sortMode) }) t.Run("Get update strategy option configured application because of invalid option", func(t *testing.T) { @@ -121,21 +139,21 @@ func Test_GetSortOption(t *testing.T) { t.Run("Prefer update strategy option from image-specific annotation", func(t *testing.T) { annotations := map[string]string{ - fmt.Sprintf(common.UpdateStrategyAnnotation, "dummy"): "name", - common.ApplicationWideUpdateStrategyAnnotation: "latest", + fmt.Sprintf(common.UpdateStrategyAnnotation, "dummy"): "alphabetical", + common.ApplicationWideUpdateStrategyAnnotation: "newest-build", } img := NewFromIdentifier("dummy=foo/bar:1.12") sortMode := img.GetParameterUpdateStrategy(annotations) - assert.Equal(t, StrategyName, sortMode) + assert.Equal(t, StrategyAlphabetical, sortMode) }) t.Run("Get update strategy option from application-wide annotation", func(t *testing.T) { annotations := map[string]string{ - common.ApplicationWideUpdateStrategyAnnotation: "latest", + common.ApplicationWideUpdateStrategyAnnotation: "newest-build", } img := NewFromIdentifier("dummy=foo/bar:1.12") sortMode := img.GetParameterUpdateStrategy(annotations) - assert.Equal(t, StrategyLatest, sortMode) + assert.Equal(t, StrategyNewestBuild, sortMode) }) } diff --git a/pkg/image/version.go b/pkg/image/version.go index 6312d23f..43b3f5c2 100644 --- a/pkg/image/version.go +++ b/pkg/image/version.go @@ -17,9 +17,9 @@ const ( // VersionSortSemVer sorts tags using semver sorting (the default) StrategySemVer UpdateStrategy = 0 // VersionSortLatest sorts tags after their creation date - StrategyLatest UpdateStrategy = 1 + StrategyNewestBuild UpdateStrategy = 1 // VersionSortName sorts tags alphabetically by name - StrategyName UpdateStrategy = 2 + StrategyAlphabetical UpdateStrategy = 2 // VersionSortDigest uses latest digest of an image StrategyDigest UpdateStrategy = 3 ) @@ -28,10 +28,10 @@ func (us UpdateStrategy) String() string { switch us { case StrategySemVer: return "semver" - case StrategyLatest: - return "latest" - case StrategyName: - return "name" + case StrategyNewestBuild: + return "newest-build" + case StrategyAlphabetical: + return "alphabetical" case StrategyDigest: return "digest" } @@ -87,12 +87,12 @@ func (img *ContainerImage) GetNewestVersionFromTags(vc *VersionConstraint, tagLi switch vc.Strategy { case StrategySemVer: availableTags = tagList.SortBySemVer() - case StrategyName: - availableTags = tagList.SortByName() - case StrategyLatest: + case StrategyAlphabetical: + availableTags = tagList.SortAlphabetically() + case StrategyNewestBuild: availableTags = tagList.SortByDate() case StrategyDigest: - availableTags = tagList.SortByName() + availableTags = tagList.SortAlphabetically() } considerTags := tag.SortableImageTagList{} @@ -192,7 +192,7 @@ func (s UpdateStrategy) IsCacheable() bool { // NeedsMetadata returns true if strategy s requires image metadata to work correctly func (s UpdateStrategy) NeedsMetadata() bool { switch s { - case StrategyLatest: + case StrategyNewestBuild: return true default: return false diff --git a/pkg/image/version_test.go b/pkg/image/version_test.go index 8cdc1f54..a54808fe 100644 --- a/pkg/image/version_test.go +++ b/pkg/image/version_test.go @@ -88,7 +88,7 @@ func Test_LatestVersion(t *testing.T) { t.Run("Find the latest version using latest sortmode", func(t *testing.T) { tagList := newImageTagListWithDate([]string{"zz", "bb", "yy", "cc", "yy", "aa", "ll"}) img := NewFromIdentifier("jannfis/test:bb") - vc := VersionConstraint{Strategy: StrategyLatest} + vc := VersionConstraint{Strategy: StrategyNewestBuild} newTag, err := img.GetNewestVersionFromTags(&vc, tagList) require.NoError(t, err) require.NotNil(t, newTag) diff --git a/pkg/registry/registry.go b/pkg/registry/registry.go index 11c061be..3feb8a38 100644 --- a/pkg/registry/registry.go +++ b/pkg/registry/registry.go @@ -80,7 +80,7 @@ func (endpoint *RegistryEndpoint) GetTags(img *image.ContainerImage, regClient R // // We just create a dummy time stamp according to the registry's sort mode, if // set. - if (vc.Strategy != image.StrategyLatest && vc.Strategy != image.StrategyDigest) || endpoint.TagListSort.IsTimeSorted() { + if (vc.Strategy != image.StrategyNewestBuild && vc.Strategy != image.StrategyDigest) || endpoint.TagListSort.IsTimeSorted() { for i, tagStr := range tags { var ts int if endpoint.TagListSort == TagListSortLatestFirst { diff --git a/pkg/registry/registry_test.go b/pkg/registry/registry_test.go index 31164f7a..d558944b 100644 --- a/pkg/registry/registry_test.go +++ b/pkg/registry/registry_test.go @@ -70,7 +70,7 @@ func Test_GetTags(t *testing.T) { img := image.NewFromIdentifier("foo/bar:1.2.0") - tl, err := ep.GetTags(img, ®Client, &image.VersionConstraint{Strategy: image.StrategyName, Options: options.NewManifestOptions()}) + tl, err := ep.GetTags(img, ®Client, &image.VersionConstraint{Strategy: image.StrategyAlphabetical, Options: options.NewManifestOptions()}) require.NoError(t, err) assert.NotEmpty(t, tl) @@ -102,7 +102,7 @@ func Test_GetTags(t *testing.T) { ep.Cache.ClearCache() img := image.NewFromIdentifier("foo/bar:1.2.0") - tl, err := ep.GetTags(img, ®Client, &image.VersionConstraint{Strategy: image.StrategyLatest, Options: options.NewManifestOptions()}) + tl, err := ep.GetTags(img, ®Client, &image.VersionConstraint{Strategy: image.StrategyNewestBuild, Options: options.NewManifestOptions()}) require.NoError(t, err) assert.NotEmpty(t, tl) diff --git a/pkg/tag/tag.go b/pkg/tag/tag.go index 2a89aecd..5d16024e 100644 --- a/pkg/tag/tag.go +++ b/pkg/tag/tag.go @@ -123,7 +123,7 @@ func (il ImageTagList) Add(tag *ImageTag) { } // SortByName returns an array of ImageTag objects, sorted by the tag's name -func (il ImageTagList) SortByName() SortableImageTagList { +func (il ImageTagList) SortAlphabetically() SortableImageTagList { sil := SortableImageTagList{} for _, v := range il.items { sil = append(sil, v) diff --git a/pkg/tag/tag_test.go b/pkg/tag/tag_test.go index f5985878..fb5bc02c 100644 --- a/pkg/tag/tag_test.go +++ b/pkg/tag/tag_test.go @@ -96,7 +96,7 @@ func Test_SortableImageTagList(t *testing.T) { tag := NewImageTag(name, time.Now(), "") il.Add(tag) } - sil := il.SortByName() + sil := il.SortAlphabetically() require.Len(t, sil, len(names)) assert.Equal(t, "alpha", sil[0].TagName) assert.Equal(t, "bazar", sil[1].TagName)