diff options
| author | jannfis <jann@mistrust.net> | 2022-01-11 16:35:51 +0100 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2022-01-11 16:35:51 +0100 |
| commit | e1f65012575eac6cd46fc6fdfc2d1ef03ad9b930 (patch) | |
| tree | 7e4bd77d6e96ee77d8cd3f383165448c9ad01501 /pkg/image | |
| parent | 77a6e8f1f8afa1d5b5b5f440dd9341830e14f7ef (diff) | |
feat: Support manifestlist and multi-arch images (#341)
* feat: Support manifestlist and multi-arch images
Signed-off-by: jannfis <jann@mistrust.net>
* Add unit test
Signed-off-by: jannfis <jann@mistrust.net>
* Fix linter issue
Signed-off-by: jannfis <jann@mistrust.net>
Diffstat (limited to 'pkg/image')
| -rw-r--r-- | pkg/image/options.go | 61 | ||||
| -rw-r--r-- | pkg/image/options_test.go | 78 | ||||
| -rw-r--r-- | pkg/image/version.go | 2 |
3 files changed, 141 insertions, 0 deletions
diff --git a/pkg/image/options.go b/pkg/image/options.go index b1bd6a6..a8908b8 100644 --- a/pkg/image/options.go +++ b/pkg/image/options.go @@ -3,10 +3,12 @@ package image import ( "fmt" "regexp" + "runtime" "strings" "github.com/argoproj-labs/argocd-image-updater/pkg/common" "github.com/argoproj-labs/argocd-image-updater/pkg/log" + "github.com/argoproj-labs/argocd-image-updater/pkg/options" ) // GetParameterHelmImageName gets the value for image-name option for the image @@ -177,6 +179,65 @@ func (img *ContainerImage) GetParameterIgnoreTags(annotations map[string]string) return ignoreList } +// GetPlatformOptions sets up platform constraints for an image. If no platform +// is specified in the annotations, we restrict the platform for images to the +// platform we're executed on unless unrestricted is set to true, in which case +// we do not setup a platform restriction if no platform annotation is found. +func (img *ContainerImage) GetPlatformOptions(annotations map[string]string, unrestricted bool) *options.ManifestOptions { + var opts *options.ManifestOptions = options.NewManifestOptions() + key := fmt.Sprintf(common.PlatformsAnnotation, img.normalizedSymbolicName()) + logCtx := log.WithContext(). + AddField("image", img.ImageName). + AddField("registry", img.RegistryURL) + + val, ok := annotations[key] + if !ok { + if !unrestricted { + os := runtime.GOOS + arch := runtime.GOARCH + variant := "" + if strings.Contains(runtime.GOARCH, "/") { + a := strings.SplitN(runtime.GOARCH, "/", 2) + arch = a[0] + variant = a[1] + } + logCtx.Tracef("Using runtime platform constraint %s", options.PlatformKey(os, arch, variant)) + opts = opts.WithPlatform(os, arch, variant) + } + } else { + platforms := strings.Split(val, ",") + for _, ps := range platforms { + pt := strings.TrimSpace(ps) + os, arch, variant, err := ParsePlatform(pt) + if err != nil { + // If the platform identifier could not be parsed, we set the + // constraint intentionally to the invalid value so we don't + // end up updating to the wrong architecture possibly. + os = ps + logCtx.Warnf("could not parse platform identifier '%v': invalid format", pt) + } + logCtx.Tracef("Adding platform constraint %s", options.PlatformKey(os, arch, variant)) + opts = opts.WithPlatform(os, arch, variant) + } + } + + return opts +} + +func ParsePlatform(platformID string) (string, string, string, error) { + p := strings.SplitN(platformID, "/", 3) + if len(p) < 2 { + return "", "", "", fmt.Errorf("could not parse platform constraint '%s'", platformID) + } + os := p[0] + arch := p[1] + variant := "" + if len(p) == 3 { + variant = p[2] + } + return os, arch, variant, nil +} + func (img *ContainerImage) normalizedSymbolicName() string { return strings.ReplaceAll(img.ImageAlias, "/", "_") } diff --git a/pkg/image/options_test.go b/pkg/image/options_test.go index 88a0266..4313ed5 100644 --- a/pkg/image/options_test.go +++ b/pkg/image/options_test.go @@ -3,9 +3,11 @@ package image import ( "fmt" "regexp" + "runtime" "testing" "github.com/argoproj-labs/argocd-image-updater/pkg/common" + "github.com/argoproj-labs/argocd-image-updater/pkg/options" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -193,3 +195,79 @@ func Test_GetIgnoreTags(t *testing.T) { assert.Equal(t, "tag4", tags[3]) }) } + +func Test_GetPlatformOptions(t *testing.T) { + t.Run("Empty platform options with restriction", func(t *testing.T) { + annotations := map[string]string{} + img := NewFromIdentifier("dummy=foo/bar:1.12") + opts := img.GetPlatformOptions(annotations, false) + os := runtime.GOOS + arch := runtime.GOARCH + assert.True(t, opts.WantsPlatform(os, arch, "")) + assert.False(t, opts.WantsPlatform(os, arch, "invalid")) + }) + t.Run("Empty platform options without restriction", func(t *testing.T) { + annotations := map[string]string{} + img := NewFromIdentifier("dummy=foo/bar:1.12") + opts := img.GetPlatformOptions(annotations, true) + os := runtime.GOOS + arch := runtime.GOARCH + assert.True(t, opts.WantsPlatform(os, arch, "")) + assert.True(t, opts.WantsPlatform(os, arch, "invalid")) + assert.True(t, opts.WantsPlatform("windows", "amd64", "")) + }) + t.Run("Single platform without variant requested", func(t *testing.T) { + os := "linux" + arch := "arm64" + variant := "" + annotations := map[string]string{ + fmt.Sprintf(common.PlatformsAnnotation, "dummy"): options.PlatformKey(os, arch, variant), + } + img := NewFromIdentifier("dummy=foo/bar:1.12") + opts := img.GetPlatformOptions(annotations, false) + assert.True(t, opts.WantsPlatform(os, arch, variant)) + assert.False(t, opts.WantsPlatform(os, arch, "invalid")) + }) + t.Run("Single platform with variant requested", func(t *testing.T) { + os := "linux" + arch := "arm" + variant := "v6" + annotations := map[string]string{ + fmt.Sprintf(common.PlatformsAnnotation, "dummy"): options.PlatformKey(os, arch, variant), + } + img := NewFromIdentifier("dummy=foo/bar:1.12") + opts := img.GetPlatformOptions(annotations, false) + assert.True(t, opts.WantsPlatform(os, arch, variant)) + assert.False(t, opts.WantsPlatform(os, arch, "")) + assert.False(t, opts.WantsPlatform(runtime.GOOS, runtime.GOARCH, "")) + assert.False(t, opts.WantsPlatform(runtime.GOOS, runtime.GOARCH, variant)) + }) + t.Run("Multiple platforms requested", func(t *testing.T) { + os := "linux" + arch := "arm" + variant := "v6" + annotations := map[string]string{ + fmt.Sprintf(common.PlatformsAnnotation, "dummy"): options.PlatformKey(os, arch, variant) + ", " + options.PlatformKey(runtime.GOOS, runtime.GOARCH, ""), + } + img := NewFromIdentifier("dummy=foo/bar:1.12") + opts := img.GetPlatformOptions(annotations, false) + assert.True(t, opts.WantsPlatform(os, arch, variant)) + assert.True(t, opts.WantsPlatform(runtime.GOOS, runtime.GOARCH, "")) + assert.False(t, opts.WantsPlatform(os, arch, "")) + assert.False(t, opts.WantsPlatform(runtime.GOOS, runtime.GOARCH, variant)) + }) + t.Run("Invalid platform requested", func(t *testing.T) { + os := "linux" + arch := "arm" + variant := "v6" + annotations := map[string]string{ + fmt.Sprintf(common.PlatformsAnnotation, "dummy"): "invalid", + } + img := NewFromIdentifier("dummy=foo/bar:1.12") + opts := img.GetPlatformOptions(annotations, false) + assert.False(t, opts.WantsPlatform(os, arch, variant)) + assert.False(t, opts.WantsPlatform(runtime.GOOS, runtime.GOARCH, "")) + assert.False(t, opts.WantsPlatform(os, arch, "")) + assert.False(t, opts.WantsPlatform(runtime.GOOS, runtime.GOARCH, variant)) + }) +} diff --git a/pkg/image/version.go b/pkg/image/version.go index 8461e17..3e1b39f 100644 --- a/pkg/image/version.go +++ b/pkg/image/version.go @@ -4,6 +4,7 @@ import ( "path/filepath" "github.com/argoproj-labs/argocd-image-updater/pkg/log" + "github.com/argoproj-labs/argocd-image-updater/pkg/options" "github.com/argoproj-labs/argocd-image-updater/pkg/tag" "github.com/Masterminds/semver" @@ -42,6 +43,7 @@ type VersionConstraint struct { MatchArgs interface{} IgnoreList []string SortMode VersionSortMode + Options *options.ManifestOptions } type MatchFuncFn func(tagName string, pattern interface{}) bool |
