Skip to content

Commit 068785e

Browse files
committed
Add compare and matching for OS features
Signed-off-by: Derek McGowan <[email protected]>
1 parent 3f1935b commit 068785e

File tree

3 files changed

+150
-3
lines changed

3 files changed

+150
-3
lines changed

compare.go

+23-1
Original file line numberDiff line numberDiff line change
@@ -156,9 +156,20 @@ func (c orderedPlatformComparer) Less(p1 specs.Platform, p2 specs.Platform) bool
156156
return true
157157
}
158158
if p1m || p2m {
159+
if p1m && p2m {
160+
// Prefer one with most matching features
161+
if len(p1.OSFeatures) != len(p2.OSFeatures) {
162+
return len(p1.OSFeatures) > len(p2.OSFeatures)
163+
}
164+
}
159165
return false
160166
}
161167
}
168+
if len(p1.OSFeatures) > 0 || len(p2.OSFeatures) > 0 {
169+
p1.OSFeatures = nil
170+
p2.OSFeatures = nil
171+
return c.Less(p1, p2)
172+
}
162173
return false
163174
}
164175

@@ -185,9 +196,20 @@ func (c anyPlatformComparer) Less(p1, p2 specs.Platform) bool {
185196
p2m = true
186197
}
187198
if p1m && p2m {
188-
return false
199+
if len(p1.OSFeatures) != len(p2.OSFeatures) {
200+
return len(p1.OSFeatures) > len(p2.OSFeatures)
201+
}
202+
break
189203
}
190204
}
205+
206+
// If neither match and has features, strip features and compare
207+
if !p1m && !p2m && (len(p1.OSFeatures) > 0 || len(p2.OSFeatures) > 0) {
208+
p1.OSFeatures = nil
209+
p2.OSFeatures = nil
210+
return c.Less(p1, p2)
211+
}
212+
191213
// If one matches, and the other does, sort match first
192214
return p1m && !p2m
193215
}

compare_test.go

+86
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,10 @@
1717
package platforms
1818

1919
import (
20+
"sort"
2021
"testing"
22+
23+
"github.com/stretchr/testify/assert"
2124
)
2225

2326
func TestOnly(t *testing.T) {
@@ -434,3 +437,86 @@ func TestOnlyStrict(t *testing.T) {
434437
})
435438
}
436439
}
440+
441+
func TestCompareOSFeatures(t *testing.T) {
442+
for _, tc := range []struct {
443+
platform string
444+
platforms []string
445+
expected []string
446+
}{
447+
{
448+
"linux/amd64",
449+
[]string{"windows/amd64", "linux/amd64", "linux(+other)/amd64", "linux/arm64"},
450+
[]string{"linux/amd64", "linux(+other)/amd64", "windows/amd64", "linux/arm64"},
451+
},
452+
{
453+
"linux(+none)/amd64",
454+
[]string{"windows/amd64", "linux/amd64", "linux/arm64", "linux(+other)/amd64"},
455+
[]string{"linux/amd64", "linux(+other)/amd64", "windows/amd64", "linux/arm64"},
456+
},
457+
{
458+
"linux(+other)/amd64",
459+
[]string{"windows/amd64", "linux/amd64", "linux/arm64", "linux(+other)/amd64"},
460+
[]string{"linux(+other)/amd64", "linux/amd64", "windows/amd64", "linux/arm64"},
461+
},
462+
{
463+
"linux(+af+other+zf)/amd64",
464+
[]string{"windows/amd64", "linux/amd64", "linux/arm64", "linux(+other)/amd64"},
465+
[]string{"linux(+other)/amd64", "linux/amd64", "windows/amd64", "linux/arm64"},
466+
},
467+
{
468+
"linux(+f1+f2)/amd64",
469+
[]string{"linux/amd64", "linux(+f2)/amd64", "linux(+f1)/amd64", "linux(+f1+f2)/amd64"},
470+
[]string{"linux(+f1+f2)/amd64", "linux(+f2)/amd64", "linux(+f1)/amd64", "linux/amd64"},
471+
},
472+
} {
473+
testcase := tc
474+
t.Run(testcase.platform, func(t *testing.T) {
475+
t.Parallel()
476+
p, err := Parse(testcase.platform)
477+
if err != nil {
478+
t.Fatal(err)
479+
}
480+
481+
for _, stc := range []struct {
482+
name string
483+
mc MatchComparer
484+
}{
485+
{
486+
name: "only",
487+
mc: Only(p),
488+
},
489+
{
490+
name: "only strict",
491+
mc: OnlyStrict(p),
492+
},
493+
{
494+
name: "ordered",
495+
mc: Ordered(p),
496+
},
497+
{
498+
name: "any",
499+
mc: Any(p),
500+
},
501+
} {
502+
mc := stc.mc
503+
testcase := testcase
504+
t.Run(stc.name, func(t *testing.T) {
505+
p, err := ParseAll(testcase.platforms)
506+
if err != nil {
507+
t.Fatal(err)
508+
}
509+
sort.Slice(p, func(i, j int) bool {
510+
return mc.Less(p[i], p[j])
511+
})
512+
actual := make([]string, len(p))
513+
for i, ps := range p {
514+
actual[i] = FormatAll(ps)
515+
}
516+
517+
assert.Equal(t, testcase.expected, actual)
518+
})
519+
}
520+
})
521+
}
522+
}

platforms.go

+41-2
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,7 @@ import (
114114
"path"
115115
"regexp"
116116
"runtime"
117+
"sort"
117118
"strconv"
118119
"strings"
119120

@@ -141,6 +142,10 @@ type Matcher interface {
141142
// functionality.
142143
//
143144
// Applications should opt to use `Match` over directly parsing specifiers.
145+
//
146+
// For OSFeatures, this matcher will match if the platform to match has
147+
// OSFeatures which are a subset of the OSFeatures of the platform
148+
// provided to NewMatcher.
144149
func NewMatcher(platform specs.Platform) Matcher {
145150
return newDefaultMatcher(platform)
146151
}
@@ -151,9 +156,40 @@ type matcher struct {
151156

152157
func (m *matcher) Match(platform specs.Platform) bool {
153158
normalized := Normalize(platform)
154-
return m.OS == normalized.OS &&
159+
if m.OS == normalized.OS &&
155160
m.Architecture == normalized.Architecture &&
156-
m.Variant == normalized.Variant
161+
m.Variant == normalized.Variant {
162+
if len(normalized.OSFeatures) == 0 {
163+
return true
164+
}
165+
if len(m.OSFeatures) >= len(normalized.OSFeatures) {
166+
// Ensure that normalized.OSFeatures is a subet of
167+
// m.OSFeatures
168+
j := 0
169+
for _, feature := range normalized.OSFeatures {
170+
for ; j < len(m.OSFeatures); j++ {
171+
if feature == m.OSFeatures[j] {
172+
// Don't increment j since the list is sorted
173+
// but may contain duplicates
174+
// TODO: Deduplicate list during normalize so
175+
// that j can be incremented here
176+
break
177+
}
178+
// Since both lists are ordered, if the feature is less
179+
// than what is seen, it is not in the list
180+
if feature < m.OSFeatures[j] {
181+
return false
182+
}
183+
}
184+
// if we hit the end, then feature was not found
185+
if j == len(m.OSFeatures) {
186+
return false
187+
}
188+
}
189+
return true
190+
}
191+
}
192+
return false
157193
}
158194

159195
func (m *matcher) String() string {
@@ -311,6 +347,9 @@ func FormatAll(platform specs.Platform) string {
311347
func Normalize(platform specs.Platform) specs.Platform {
312348
platform.OS = normalizeOS(platform.OS)
313349
platform.Architecture, platform.Variant = normalizeArch(platform.Architecture, platform.Variant)
350+
if len(platform.OSFeatures) > 0 {
351+
sort.Strings(platform.OSFeatures)
352+
}
314353

315354
return platform
316355
}

0 commit comments

Comments
 (0)