summaryrefslogtreecommitdiff
path: root/pkg/image
diff options
context:
space:
mode:
authorjannfis <jann@mistrust.net>2022-01-11 16:35:51 +0100
committerGitHub <noreply@github.com>2022-01-11 16:35:51 +0100
commite1f65012575eac6cd46fc6fdfc2d1ef03ad9b930 (patch)
tree7e4bd77d6e96ee77d8cd3f383165448c9ad01501 /pkg/image
parent77a6e8f1f8afa1d5b5b5f440dd9341830e14f7ef (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.go61
-rw-r--r--pkg/image/options_test.go78
-rw-r--r--pkg/image/version.go2
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