diff --git a/builder/builder-next/worker/worker.go b/builder/builder-next/worker/worker.go index 271192cc7a..64771973a2 100644 --- a/builder/builder-next/worker/worker.go +++ b/builder/builder-next/worker/worker.go @@ -158,15 +158,7 @@ func (w *Worker) Labels() map[string]string { // Platforms returns one or more platforms supported by the image. func (w *Worker) Platforms(noCache bool) []ocispec.Platform { if noCache { - pm := make(map[string]struct{}, len(w.Opt.Platforms)) - for _, p := range w.Opt.Platforms { - pm[platforms.Format(p)] = struct{}{} - } - for _, p := range archutil.SupportedPlatforms(noCache) { - if _, ok := pm[platforms.Format(p)]; !ok { - w.Opt.Platforms = append(w.Opt.Platforms, p) - } - } + w.Opt.Platforms = mergePlatforms(w.Opt.Platforms, archutil.SupportedPlatforms(noCache)) } if len(w.Opt.Platforms) == 0 { return []ocispec.Platform{platforms.DefaultSpec()} @@ -174,6 +166,30 @@ func (w *Worker) Platforms(noCache bool) []ocispec.Platform { return w.Opt.Platforms } +// mergePlatforms merges the defined platforms with the supported platforms +// and returns a new slice of platforms. It ensures no duplicates. +func mergePlatforms(defined, supported []ocispec.Platform) []ocispec.Platform { + result := []ocispec.Platform{} + matchers := make([]platforms.MatchComparer, len(defined)) + for i, p := range defined { + result = append(result, p) + matchers[i] = platforms.Only(p) + } + for _, p := range supported { + exists := false + for _, m := range matchers { + if m.Match(p) { + exists = true + break + } + } + if !exists { + result = append(result, p) + } + } + return result +} + // GCPolicy returns automatic GC Policy func (w *Worker) GCPolicy() []client.PruneInfo { return w.Opt.GCPolicy diff --git a/builder/builder-next/worker/worker_test.go b/builder/builder-next/worker/worker_test.go new file mode 100644 index 0000000000..f076068c1f --- /dev/null +++ b/builder/builder-next/worker/worker_test.go @@ -0,0 +1,76 @@ +package worker + +import ( + "testing" + + ocispec "github.com/opencontainers/image-spec/specs-go/v1" + "gotest.tools/v3/assert" + is "gotest.tools/v3/assert/cmp" +) + +func TestMergePlatforms(t *testing.T) { + defaultPlatform := ocispec.Platform{OS: "linux", Architecture: "amd64"} + otherPlatform := ocispec.Platform{OS: "windows", Architecture: "amd64"} + thirdPlatform := ocispec.Platform{OS: "darwin", Architecture: "arm64"} + + tests := []struct { + name string + defined []ocispec.Platform + supported []ocispec.Platform + contains []ocispec.Platform + wantLen int + }{ + { + name: "AllUnique", + defined: []ocispec.Platform{defaultPlatform}, + supported: []ocispec.Platform{otherPlatform, thirdPlatform}, + contains: []ocispec.Platform{defaultPlatform, otherPlatform, thirdPlatform}, + wantLen: 3, + }, + { + name: "SomeOverlap", + defined: []ocispec.Platform{defaultPlatform, otherPlatform}, + supported: []ocispec.Platform{otherPlatform, thirdPlatform}, + contains: []ocispec.Platform{defaultPlatform, otherPlatform, thirdPlatform}, + wantLen: 3, + }, + { + name: "AllOverlap", + defined: []ocispec.Platform{defaultPlatform, otherPlatform}, + supported: []ocispec.Platform{defaultPlatform, otherPlatform}, + contains: []ocispec.Platform{defaultPlatform, otherPlatform}, + wantLen: 2, + }, + { + name: "EmptySupported", + defined: []ocispec.Platform{defaultPlatform}, + supported: []ocispec.Platform{}, + contains: []ocispec.Platform{defaultPlatform}, + wantLen: 1, + }, + { + name: "EmptyDefined", + defined: []ocispec.Platform{}, + supported: []ocispec.Platform{defaultPlatform, otherPlatform}, + contains: []ocispec.Platform{defaultPlatform, otherPlatform}, + wantLen: 2, + }, + { + name: "BothEmpty", + defined: []ocispec.Platform{}, + supported: []ocispec.Platform{}, + contains: []ocispec.Platform{}, + wantLen: 0, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + got := mergePlatforms(tc.defined, tc.supported) + assert.Equal(t, len(got), tc.wantLen) + for _, p := range tc.contains { + assert.Assert(t, is.Contains(got, p)) + } + }) + } +}