
This improves the hard-coded list of ARM fallbacks in the `platform.Only` implementation (by doing a descending loop over variant numbers instead, which is all the hard-coded list was doing).
Making this a separate function can then more easily be recursive later for handling an `arm64`->`arm` fallback (or similar), but I think it makes the code a lot more clear too (so we're calculating a vector of platforms separately from building a matcher object).
This also makes a minor adjustment in `TestImagePullWithDistSourceLabel` which had an implicit assumption that `platforms.Only` would only ever result in a single suitable manifest, which isn't strictly true (and is likely failing as-is when run on any 32bit `arm` system that's `v6` or higher, which this fixes 😅).
Signed-off-by: Tianon Gravi <admwiggin@gmail.com>
161 lines
4.0 KiB
Go
161 lines
4.0 KiB
Go
/*
|
|
Copyright The containerd Authors.
|
|
|
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
you may not use this file except in compliance with the License.
|
|
You may obtain a copy of the License at
|
|
|
|
http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
Unless required by applicable law or agreed to in writing, software
|
|
distributed under the License is distributed on an "AS IS" BASIS,
|
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
See the License for the specific language governing permissions and
|
|
limitations under the License.
|
|
*/
|
|
|
|
package platforms
|
|
|
|
import (
|
|
"strconv"
|
|
"strings"
|
|
|
|
specs "github.com/opencontainers/image-spec/specs-go/v1"
|
|
)
|
|
|
|
// MatchComparer is able to match and compare platforms to
|
|
// filter and sort platforms.
|
|
type MatchComparer interface {
|
|
Matcher
|
|
|
|
Less(specs.Platform, specs.Platform) bool
|
|
}
|
|
|
|
// platformVector returns an (ordered) vector of appropriate specs.Platform
|
|
// objects to try matching for the given platform object (see platforms.Only).
|
|
func platformVector(platform specs.Platform) []specs.Platform {
|
|
vector := []specs.Platform{platform}
|
|
|
|
switch platform.Architecture {
|
|
case "arm":
|
|
if armVersion, err := strconv.Atoi(strings.TrimPrefix(platform.Variant, "v")); err == nil && armVersion > 5 {
|
|
for armVersion--; armVersion >= 5; armVersion-- {
|
|
vector = append(vector, specs.Platform{
|
|
Architecture: platform.Architecture,
|
|
OS: platform.OS,
|
|
OSVersion: platform.OSVersion,
|
|
OSFeatures: platform.OSFeatures,
|
|
Variant: "v" + strconv.Itoa(armVersion),
|
|
})
|
|
}
|
|
}
|
|
}
|
|
|
|
return vector
|
|
}
|
|
|
|
// Only returns a match comparer for a single platform
|
|
// using default resolution logic for the platform.
|
|
//
|
|
// For arm/v8, will also match arm/v7, arm/v6 and arm/v5
|
|
// For arm/v7, will also match arm/v6 and arm/v5
|
|
// For arm/v6, will also match arm/v5
|
|
func Only(platform specs.Platform) MatchComparer {
|
|
return Ordered(platformVector(Normalize(platform))...)
|
|
}
|
|
|
|
// Ordered returns a platform MatchComparer which matches any of the platforms
|
|
// but orders them in order they are provided.
|
|
func Ordered(platforms ...specs.Platform) MatchComparer {
|
|
matchers := make([]Matcher, len(platforms))
|
|
for i := range platforms {
|
|
matchers[i] = NewMatcher(platforms[i])
|
|
}
|
|
return orderedPlatformComparer{
|
|
matchers: matchers,
|
|
}
|
|
}
|
|
|
|
// Any returns a platform MatchComparer which matches any of the platforms
|
|
// with no preference for ordering.
|
|
func Any(platforms ...specs.Platform) MatchComparer {
|
|
matchers := make([]Matcher, len(platforms))
|
|
for i := range platforms {
|
|
matchers[i] = NewMatcher(platforms[i])
|
|
}
|
|
return anyPlatformComparer{
|
|
matchers: matchers,
|
|
}
|
|
}
|
|
|
|
// All is a platform MatchComparer which matches all platforms
|
|
// with preference for ordering.
|
|
var All MatchComparer = allPlatformComparer{}
|
|
|
|
type orderedPlatformComparer struct {
|
|
matchers []Matcher
|
|
}
|
|
|
|
func (c orderedPlatformComparer) Match(platform specs.Platform) bool {
|
|
for _, m := range c.matchers {
|
|
if m.Match(platform) {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
func (c orderedPlatformComparer) Less(p1 specs.Platform, p2 specs.Platform) bool {
|
|
for _, m := range c.matchers {
|
|
p1m := m.Match(p1)
|
|
p2m := m.Match(p2)
|
|
if p1m && !p2m {
|
|
return true
|
|
}
|
|
if p1m || p2m {
|
|
return false
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
type anyPlatformComparer struct {
|
|
matchers []Matcher
|
|
}
|
|
|
|
func (c anyPlatformComparer) Match(platform specs.Platform) bool {
|
|
for _, m := range c.matchers {
|
|
if m.Match(platform) {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
func (c anyPlatformComparer) Less(p1, p2 specs.Platform) bool {
|
|
var p1m, p2m bool
|
|
for _, m := range c.matchers {
|
|
if !p1m && m.Match(p1) {
|
|
p1m = true
|
|
}
|
|
if !p2m && m.Match(p2) {
|
|
p2m = true
|
|
}
|
|
if p1m && p2m {
|
|
return false
|
|
}
|
|
}
|
|
// If one matches, and the other does, sort match first
|
|
return p1m && !p2m
|
|
}
|
|
|
|
type allPlatformComparer struct{}
|
|
|
|
func (allPlatformComparer) Match(specs.Platform) bool {
|
|
return true
|
|
}
|
|
|
|
func (allPlatformComparer) Less(specs.Platform, specs.Platform) bool {
|
|
return false
|
|
}
|