Use github.com/containerd/platforms package
Signed-off-by: Derek McGowan <derek@mcg.dev>
This commit is contained in:
@@ -17,72 +17,11 @@
|
||||
package platforms
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/containerd/platforms"
|
||||
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 "amd64":
|
||||
if amd64Version, err := strconv.Atoi(strings.TrimPrefix(platform.Variant, "v")); err == nil && amd64Version > 1 {
|
||||
for amd64Version--; amd64Version >= 1; amd64Version-- {
|
||||
vector = append(vector, specs.Platform{
|
||||
Architecture: platform.Architecture,
|
||||
OS: platform.OS,
|
||||
OSVersion: platform.OSVersion,
|
||||
OSFeatures: platform.OSFeatures,
|
||||
Variant: "v" + strconv.Itoa(amd64Version),
|
||||
})
|
||||
}
|
||||
}
|
||||
vector = append(vector, specs.Platform{
|
||||
Architecture: "386",
|
||||
OS: platform.OS,
|
||||
OSVersion: platform.OSVersion,
|
||||
OSFeatures: platform.OSFeatures,
|
||||
})
|
||||
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),
|
||||
})
|
||||
}
|
||||
}
|
||||
case "arm64":
|
||||
variant := platform.Variant
|
||||
if variant == "" {
|
||||
variant = "v8"
|
||||
}
|
||||
vector = append(vector, platformVector(specs.Platform{
|
||||
Architecture: "arm",
|
||||
OS: platform.OS,
|
||||
OSVersion: platform.OSVersion,
|
||||
OSFeatures: platform.OSFeatures,
|
||||
Variant: variant,
|
||||
})...)
|
||||
}
|
||||
|
||||
return vector
|
||||
}
|
||||
type MatchComparer = platforms.MatchComparer
|
||||
|
||||
// Only returns a match comparer for a single platform
|
||||
// using default resolution logic for the platform.
|
||||
@@ -92,7 +31,7 @@ func platformVector(platform specs.Platform) []specs.Platform {
|
||||
// For arm/v6, will also match arm/v5
|
||||
// For amd64, will also match 386
|
||||
func Only(platform specs.Platform) MatchComparer {
|
||||
return Ordered(platformVector(Normalize(platform))...)
|
||||
return platforms.Only(platform)
|
||||
}
|
||||
|
||||
// OnlyStrict returns a match comparer for a single platform.
|
||||
@@ -104,100 +43,21 @@ func Only(platform specs.Platform) MatchComparer {
|
||||
// OnlyStrict matches non-canonical forms.
|
||||
// So, "arm64" matches "arm/64/v8".
|
||||
func OnlyStrict(platform specs.Platform) MatchComparer {
|
||||
return Ordered(Normalize(platform))
|
||||
return platforms.OnlyStrict(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,
|
||||
}
|
||||
func Ordered(ps ...specs.Platform) MatchComparer {
|
||||
return platforms.Ordered(ps...)
|
||||
}
|
||||
|
||||
// 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,
|
||||
}
|
||||
func Any(ps ...specs.Platform) MatchComparer {
|
||||
return platforms.Any(ps...)
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
var All = platforms.All
|
||||
|
||||
@@ -1,415 +0,0 @@
|
||||
/*
|
||||
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 (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestOnly(t *testing.T) {
|
||||
for _, tc := range []struct {
|
||||
platform string
|
||||
matches map[bool][]string
|
||||
}{
|
||||
{
|
||||
platform: "linux/amd64",
|
||||
matches: map[bool][]string{
|
||||
true: {
|
||||
"linux/amd64",
|
||||
"linux/386",
|
||||
},
|
||||
false: {
|
||||
"linux/amd64/v2",
|
||||
"linux/arm/v7",
|
||||
"linux/arm64",
|
||||
"windows/amd64",
|
||||
"windows/arm",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
platform: "linux/amd64/v2",
|
||||
matches: map[bool][]string{
|
||||
true: {
|
||||
"linux/amd64",
|
||||
"linux/amd64/v1",
|
||||
"linux/amd64/v2",
|
||||
"linux/386",
|
||||
},
|
||||
false: {
|
||||
"linux/amd64/v3",
|
||||
"linux/amd64/v4",
|
||||
"linux/arm/v7",
|
||||
"linux/arm64",
|
||||
"windows/amd64",
|
||||
"windows/arm",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
platform: "linux/386",
|
||||
matches: map[bool][]string{
|
||||
true: {
|
||||
"linux/386",
|
||||
},
|
||||
false: {
|
||||
"linux/amd64",
|
||||
"linux/arm/v7",
|
||||
"linux/arm64",
|
||||
"windows/amd64",
|
||||
"windows/arm",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
platform: "windows/amd64",
|
||||
matches: map[bool][]string{
|
||||
true: {"windows/amd64"},
|
||||
false: {
|
||||
"linux/amd64",
|
||||
"linux/arm/v7",
|
||||
"linux/arm64",
|
||||
"windows/arm",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
platform: "linux/arm/v8",
|
||||
matches: map[bool][]string{
|
||||
true: {
|
||||
"linux/arm",
|
||||
"linux/arm/v5",
|
||||
"linux/arm/v6",
|
||||
"linux/arm/v7",
|
||||
"linux/arm/v8",
|
||||
},
|
||||
false: {
|
||||
"linux/amd64",
|
||||
"linux/arm/v4",
|
||||
"linux/arm64",
|
||||
"windows/amd64",
|
||||
"windows/arm",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
platform: "linux/arm/v7",
|
||||
matches: map[bool][]string{
|
||||
true: {
|
||||
"linux/arm",
|
||||
"linux/arm/v5",
|
||||
"linux/arm/v6",
|
||||
"linux/arm/v7",
|
||||
},
|
||||
false: {
|
||||
"linux/amd64",
|
||||
"linux/arm/v4",
|
||||
"linux/arm/v8",
|
||||
"linux/arm64",
|
||||
"windows/amd64",
|
||||
"windows/arm",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
platform: "linux/arm/v6",
|
||||
matches: map[bool][]string{
|
||||
true: {
|
||||
"linux/arm/v5",
|
||||
"linux/arm/v6",
|
||||
},
|
||||
false: {
|
||||
"linux/amd64",
|
||||
"linux/arm",
|
||||
"linux/arm/v4",
|
||||
"linux/arm/v7",
|
||||
"linux/arm/v8",
|
||||
"linux/arm64",
|
||||
"windows/amd64",
|
||||
"windows/arm",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
platform: "linux/arm/v5",
|
||||
matches: map[bool][]string{
|
||||
true: {
|
||||
"linux/arm/v5",
|
||||
},
|
||||
false: {
|
||||
"linux/amd64",
|
||||
"linux/arm",
|
||||
"linux/arm/v4",
|
||||
"linux/arm/v6",
|
||||
"linux/arm/v7",
|
||||
"linux/arm/v8",
|
||||
"linux/arm64",
|
||||
"windows/amd64",
|
||||
"windows/arm",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
platform: "linux/arm/v4",
|
||||
matches: map[bool][]string{
|
||||
true: {
|
||||
"linux/arm/v4",
|
||||
},
|
||||
false: {
|
||||
"linux/amd64",
|
||||
"linux/arm",
|
||||
"linux/arm/v5",
|
||||
"linux/arm/v6",
|
||||
"linux/arm/v7",
|
||||
"linux/arm/v8",
|
||||
"linux/arm64",
|
||||
"windows/amd64",
|
||||
"windows/arm",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
platform: "linux/arm64",
|
||||
matches: map[bool][]string{
|
||||
true: {
|
||||
"linux/arm",
|
||||
"linux/arm/v5",
|
||||
"linux/arm/v6",
|
||||
"linux/arm/v7",
|
||||
"linux/arm/v8",
|
||||
"linux/arm64",
|
||||
"linux/arm64/v8",
|
||||
},
|
||||
false: {
|
||||
"linux/amd64",
|
||||
"linux/arm/v4",
|
||||
"linux/arm/v9",
|
||||
"linux/arm64/v9",
|
||||
"windows/amd64",
|
||||
"windows/arm",
|
||||
},
|
||||
},
|
||||
},
|
||||
} {
|
||||
testcase := tc
|
||||
t.Run(testcase.platform, func(t *testing.T) {
|
||||
p, err := Parse(testcase.platform)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
m := Only(p)
|
||||
for shouldMatch, platforms := range testcase.matches {
|
||||
for _, matchPlatform := range platforms {
|
||||
mp, err := Parse(matchPlatform)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if match := m.Match(mp); shouldMatch != match {
|
||||
t.Errorf("Only(%q).Match(%q) should return %v, but returns %v", testcase.platform, matchPlatform, shouldMatch, match)
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestOnlyStrict(t *testing.T) {
|
||||
for _, tc := range []struct {
|
||||
platform string
|
||||
matches map[bool][]string
|
||||
}{
|
||||
{
|
||||
platform: "linux/amd64",
|
||||
matches: map[bool][]string{
|
||||
true: {
|
||||
"linux/amd64",
|
||||
},
|
||||
false: {
|
||||
"linux/386",
|
||||
"linux/arm/v7",
|
||||
"linux/arm64",
|
||||
"windows/amd64",
|
||||
"windows/arm",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
platform: "linux/386",
|
||||
matches: map[bool][]string{
|
||||
true: {
|
||||
"linux/386",
|
||||
},
|
||||
false: {
|
||||
"linux/amd64",
|
||||
"linux/arm/v7",
|
||||
"linux/arm64",
|
||||
"windows/amd64",
|
||||
"windows/arm",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
platform: "windows/amd64",
|
||||
matches: map[bool][]string{
|
||||
true: {"windows/amd64"},
|
||||
false: {
|
||||
"linux/amd64",
|
||||
"linux/arm/v7",
|
||||
"linux/arm64",
|
||||
"windows/arm",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
platform: "linux/arm/v8",
|
||||
matches: map[bool][]string{
|
||||
true: {
|
||||
"linux/arm/v8",
|
||||
},
|
||||
false: {
|
||||
"linux/arm",
|
||||
"linux/arm/v5",
|
||||
"linux/arm/v6",
|
||||
"linux/arm/v7",
|
||||
"linux/amd64",
|
||||
"linux/arm/v4",
|
||||
"linux/arm64",
|
||||
"windows/amd64",
|
||||
"windows/arm",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
platform: "linux/arm/v7",
|
||||
matches: map[bool][]string{
|
||||
true: {
|
||||
"linux/arm",
|
||||
"linux/arm/v7",
|
||||
},
|
||||
false: {
|
||||
"linux/arm/v5",
|
||||
"linux/arm/v6",
|
||||
"linux/amd64",
|
||||
"linux/arm/v4",
|
||||
"linux/arm/v8",
|
||||
"linux/arm64",
|
||||
"windows/amd64",
|
||||
"windows/arm",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
platform: "linux/arm/v6",
|
||||
matches: map[bool][]string{
|
||||
true: {
|
||||
"linux/arm/v6",
|
||||
},
|
||||
false: {
|
||||
"linux/arm/v5",
|
||||
"linux/amd64",
|
||||
"linux/arm",
|
||||
"linux/arm/v4",
|
||||
"linux/arm/v7",
|
||||
"linux/arm/v8",
|
||||
"linux/arm64",
|
||||
"windows/amd64",
|
||||
"windows/arm",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
platform: "linux/arm/v5",
|
||||
matches: map[bool][]string{
|
||||
true: {
|
||||
"linux/arm/v5",
|
||||
},
|
||||
false: {
|
||||
"linux/amd64",
|
||||
"linux/arm",
|
||||
"linux/arm/v4",
|
||||
"linux/arm/v6",
|
||||
"linux/arm/v7",
|
||||
"linux/arm/v8",
|
||||
"linux/arm64",
|
||||
"windows/amd64",
|
||||
"windows/arm",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
platform: "linux/arm/v4",
|
||||
matches: map[bool][]string{
|
||||
true: {
|
||||
"linux/arm/v4",
|
||||
},
|
||||
false: {
|
||||
"linux/amd64",
|
||||
"linux/arm",
|
||||
"linux/arm/v5",
|
||||
"linux/arm/v6",
|
||||
"linux/arm/v7",
|
||||
"linux/arm/v8",
|
||||
"linux/arm64",
|
||||
"windows/amd64",
|
||||
"windows/arm",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
platform: "linux/arm64",
|
||||
matches: map[bool][]string{
|
||||
true: {
|
||||
"linux/arm64",
|
||||
"linux/arm64/v8",
|
||||
},
|
||||
false: {
|
||||
"linux/arm",
|
||||
"linux/arm/v5",
|
||||
"linux/arm/v6",
|
||||
"linux/arm/v7",
|
||||
"linux/arm/v8",
|
||||
"linux/amd64",
|
||||
"linux/arm/v4",
|
||||
"linux/arm/v9",
|
||||
"linux/arm64/v9",
|
||||
"windows/amd64",
|
||||
"windows/arm",
|
||||
},
|
||||
},
|
||||
},
|
||||
} {
|
||||
testcase := tc
|
||||
t.Run(testcase.platform, func(t *testing.T) {
|
||||
p, err := Parse(testcase.platform)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
m := OnlyStrict(p)
|
||||
for shouldMatch, platforms := range testcase.matches {
|
||||
for _, matchPlatform := range platforms {
|
||||
mp, err := Parse(matchPlatform)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if match := m.Match(mp); shouldMatch != match {
|
||||
t.Errorf("OnlyStrict(%q).Match(%q) should return %v, but returns %v", testcase.platform, matchPlatform, shouldMatch, match)
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
/*
|
||||
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 (
|
||||
"runtime"
|
||||
"sync"
|
||||
|
||||
"github.com/containerd/log"
|
||||
)
|
||||
|
||||
// Present the ARM instruction set architecture, eg: v7, v8
|
||||
// Don't use this value directly; call cpuVariant() instead.
|
||||
var cpuVariantValue string
|
||||
|
||||
var cpuVariantOnce sync.Once
|
||||
|
||||
func cpuVariant() string {
|
||||
cpuVariantOnce.Do(func() {
|
||||
if isArmArch(runtime.GOARCH) {
|
||||
var err error
|
||||
cpuVariantValue, err = getCPUVariant()
|
||||
if err != nil {
|
||||
log.L.Errorf("Error getCPUVariant for OS %s: %v", runtime.GOOS, err)
|
||||
}
|
||||
}
|
||||
})
|
||||
return cpuVariantValue
|
||||
}
|
||||
@@ -1,160 +0,0 @@
|
||||
/*
|
||||
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 (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"runtime"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
// getMachineArch retrieves the machine architecture through system call
|
||||
func getMachineArch() (string, error) {
|
||||
var uname unix.Utsname
|
||||
err := unix.Uname(&uname)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
arch := string(uname.Machine[:bytes.IndexByte(uname.Machine[:], 0)])
|
||||
|
||||
return arch, nil
|
||||
}
|
||||
|
||||
// For Linux, the kernel has already detected the ABI, ISA and Features.
|
||||
// So we don't need to access the ARM registers to detect platform information
|
||||
// by ourselves. We can just parse these information from /proc/cpuinfo
|
||||
func getCPUInfo(pattern string) (info string, err error) {
|
||||
|
||||
cpuinfo, err := os.Open("/proc/cpuinfo")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer cpuinfo.Close()
|
||||
|
||||
// Start to Parse the Cpuinfo line by line. For SMP SoC, we parse
|
||||
// the first core is enough.
|
||||
scanner := bufio.NewScanner(cpuinfo)
|
||||
for scanner.Scan() {
|
||||
newline := scanner.Text()
|
||||
list := strings.Split(newline, ":")
|
||||
|
||||
if len(list) > 1 && strings.EqualFold(strings.TrimSpace(list[0]), pattern) {
|
||||
return strings.TrimSpace(list[1]), nil
|
||||
}
|
||||
}
|
||||
|
||||
// Check whether the scanner encountered errors
|
||||
err = scanner.Err()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return "", fmt.Errorf("getCPUInfo for pattern %s: %w", pattern, errNotFound)
|
||||
}
|
||||
|
||||
// getCPUVariantFromArch get CPU variant from arch through a system call
|
||||
func getCPUVariantFromArch(arch string) (string, error) {
|
||||
|
||||
var variant string
|
||||
|
||||
arch = strings.ToLower(arch)
|
||||
|
||||
if arch == "aarch64" {
|
||||
variant = "8"
|
||||
} else if arch[0:4] == "armv" && len(arch) >= 5 {
|
||||
// Valid arch format is in form of armvXx
|
||||
switch arch[3:5] {
|
||||
case "v8":
|
||||
variant = "8"
|
||||
case "v7":
|
||||
variant = "7"
|
||||
case "v6":
|
||||
variant = "6"
|
||||
case "v5":
|
||||
variant = "5"
|
||||
case "v4":
|
||||
variant = "4"
|
||||
case "v3":
|
||||
variant = "3"
|
||||
default:
|
||||
variant = "unknown"
|
||||
}
|
||||
} else {
|
||||
return "", fmt.Errorf("getCPUVariantFromArch invalid arch: %s, %w", arch, errInvalidArgument)
|
||||
}
|
||||
return variant, nil
|
||||
}
|
||||
|
||||
// getCPUVariant returns cpu variant for ARM
|
||||
// We first try reading "Cpu architecture" field from /proc/cpuinfo
|
||||
// If we can't find it, then fall back using a system call
|
||||
// This is to cover running ARM in emulated environment on x86 host as this field in /proc/cpuinfo
|
||||
// was not present.
|
||||
func getCPUVariant() (string, error) {
|
||||
variant, err := getCPUInfo("Cpu architecture")
|
||||
if err != nil {
|
||||
if errors.Is(err, errNotFound) {
|
||||
// Let's try getting CPU variant from machine architecture
|
||||
arch, err := getMachineArch()
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failure getting machine architecture: %v", err)
|
||||
}
|
||||
|
||||
variant, err = getCPUVariantFromArch(arch)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failure getting CPU variant from machine architecture: %v", err)
|
||||
}
|
||||
} else {
|
||||
return "", fmt.Errorf("failure getting CPU variant: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// handle edge case for Raspberry Pi ARMv6 devices (which due to a kernel quirk, report "CPU architecture: 7")
|
||||
// https://www.raspberrypi.org/forums/viewtopic.php?t=12614
|
||||
if runtime.GOARCH == "arm" && variant == "7" {
|
||||
model, err := getCPUInfo("model name")
|
||||
if err == nil && strings.HasPrefix(strings.ToLower(model), "armv6-compatible") {
|
||||
variant = "6"
|
||||
}
|
||||
}
|
||||
|
||||
switch strings.ToLower(variant) {
|
||||
case "8", "aarch64":
|
||||
variant = "v8"
|
||||
case "7", "7m", "?(12)", "?(13)", "?(14)", "?(15)", "?(16)", "?(17)":
|
||||
variant = "v7"
|
||||
case "6", "6tej":
|
||||
variant = "v6"
|
||||
case "5", "5t", "5te", "5tej":
|
||||
variant = "v5"
|
||||
case "4", "4t":
|
||||
variant = "v4"
|
||||
case "3":
|
||||
variant = "v3"
|
||||
default:
|
||||
variant = "unknown"
|
||||
}
|
||||
|
||||
return variant, nil
|
||||
}
|
||||
@@ -1,139 +0,0 @@
|
||||
/*
|
||||
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 (
|
||||
"errors"
|
||||
"runtime"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestCPUVariant(t *testing.T) {
|
||||
if !isArmArch(runtime.GOARCH) {
|
||||
t.Skip("only relevant on linux/arm")
|
||||
}
|
||||
|
||||
variants := []string{"v8", "v7", "v6", "v5", "v4", "v3"}
|
||||
|
||||
p, err := getCPUVariant()
|
||||
if err != nil {
|
||||
t.Fatalf("Error getting CPU variant: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
for _, variant := range variants {
|
||||
if p == variant {
|
||||
t.Logf("got valid variant as expected: %#v = %#v", p, variant)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
t.Fatalf("could not get valid variant as expected: %v", variants)
|
||||
}
|
||||
|
||||
func TestGetCPUVariantFromArch(t *testing.T) {
|
||||
|
||||
for _, testcase := range []struct {
|
||||
name string
|
||||
input string
|
||||
output string
|
||||
expectedErr error
|
||||
}{
|
||||
{
|
||||
name: "Test aarch64",
|
||||
input: "aarch64",
|
||||
output: "8",
|
||||
expectedErr: nil,
|
||||
},
|
||||
{
|
||||
name: "Test Armv8 with capital",
|
||||
input: "Armv8",
|
||||
output: "8",
|
||||
expectedErr: nil,
|
||||
},
|
||||
{
|
||||
name: "Test armv7",
|
||||
input: "armv7",
|
||||
output: "7",
|
||||
expectedErr: nil,
|
||||
},
|
||||
{
|
||||
name: "Test armv6",
|
||||
input: "armv6",
|
||||
output: "6",
|
||||
expectedErr: nil,
|
||||
},
|
||||
{
|
||||
name: "Test armv5",
|
||||
input: "armv5",
|
||||
output: "5",
|
||||
expectedErr: nil,
|
||||
},
|
||||
{
|
||||
name: "Test armv4",
|
||||
input: "armv4",
|
||||
output: "4",
|
||||
expectedErr: nil,
|
||||
},
|
||||
{
|
||||
name: "Test armv3",
|
||||
input: "armv3",
|
||||
output: "3",
|
||||
expectedErr: nil,
|
||||
},
|
||||
{
|
||||
name: "Test unknown input",
|
||||
input: "armv9",
|
||||
output: "unknown",
|
||||
expectedErr: nil,
|
||||
},
|
||||
{
|
||||
name: "Test invalid input which doesn't start with armv",
|
||||
input: "armxxxx",
|
||||
output: "",
|
||||
expectedErr: errInvalidArgument,
|
||||
},
|
||||
{
|
||||
name: "Test invalid input whose length is less than 5",
|
||||
input: "armv",
|
||||
output: "",
|
||||
expectedErr: errInvalidArgument,
|
||||
},
|
||||
} {
|
||||
t.Run(testcase.name, func(t *testing.T) {
|
||||
t.Logf("input: %v", testcase.input)
|
||||
|
||||
variant, err := getCPUVariantFromArch(testcase.input)
|
||||
|
||||
if err == nil {
|
||||
if testcase.expectedErr != nil {
|
||||
t.Fatalf("Expect to get error: %v, however no error got", testcase.expectedErr)
|
||||
} else {
|
||||
if variant != testcase.output {
|
||||
t.Fatalf("Expect to get variant: %v, however %v returned", testcase.output, variant)
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
if !errors.Is(err, testcase.expectedErr) {
|
||||
t.Fatalf("Expect to get error: %v, however error %v returned", testcase.expectedErr, err)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,55 +0,0 @@
|
||||
//go:build !linux
|
||||
|
||||
/*
|
||||
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 (
|
||||
"fmt"
|
||||
"runtime"
|
||||
)
|
||||
|
||||
func getCPUVariant() (string, error) {
|
||||
|
||||
var variant string
|
||||
|
||||
if runtime.GOOS == "windows" || runtime.GOOS == "darwin" {
|
||||
// Windows/Darwin only supports v7 for ARM32 and v8 for ARM64 and so we can use
|
||||
// runtime.GOARCH to determine the variants
|
||||
switch runtime.GOARCH {
|
||||
case "arm64":
|
||||
variant = "v8"
|
||||
case "arm":
|
||||
variant = "v7"
|
||||
default:
|
||||
variant = "unknown"
|
||||
}
|
||||
} else if runtime.GOOS == "freebsd" {
|
||||
// FreeBSD supports ARMv6 and ARMv7 as well as ARMv4 and ARMv5 (though deprecated)
|
||||
// detecting those variants is currently unimplemented
|
||||
switch runtime.GOARCH {
|
||||
case "arm64":
|
||||
variant = "v8"
|
||||
default:
|
||||
variant = "unknown"
|
||||
}
|
||||
} else {
|
||||
return "", fmt.Errorf("getCPUVariant for OS %s: %v", runtime.GOOS, errNotImplemented)
|
||||
}
|
||||
|
||||
return variant, nil
|
||||
}
|
||||
@@ -1,109 +0,0 @@
|
||||
/*
|
||||
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 (
|
||||
"runtime"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// These function are generated from https://golang.org/src/go/build/syslist.go.
|
||||
//
|
||||
// We use switch statements because they are slightly faster than map lookups
|
||||
// and use a little less memory.
|
||||
|
||||
// isKnownOS returns true if we know about the operating system.
|
||||
//
|
||||
// The OS value should be normalized before calling this function.
|
||||
func isKnownOS(os string) bool {
|
||||
switch os {
|
||||
case "aix", "android", "darwin", "dragonfly", "freebsd", "hurd", "illumos", "ios", "js", "linux", "nacl", "netbsd", "openbsd", "plan9", "solaris", "windows", "zos":
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// isArmArch returns true if the architecture is ARM.
|
||||
//
|
||||
// The arch value should be normalized before being passed to this function.
|
||||
func isArmArch(arch string) bool {
|
||||
switch arch {
|
||||
case "arm", "arm64":
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// isKnownArch returns true if we know about the architecture.
|
||||
//
|
||||
// The arch value should be normalized before being passed to this function.
|
||||
func isKnownArch(arch string) bool {
|
||||
switch arch {
|
||||
case "386", "amd64", "amd64p32", "arm", "armbe", "arm64", "arm64be", "ppc64", "ppc64le", "loong64", "mips", "mipsle", "mips64", "mips64le", "mips64p32", "mips64p32le", "ppc", "riscv", "riscv64", "s390", "s390x", "sparc", "sparc64", "wasm":
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func normalizeOS(os string) string {
|
||||
if os == "" {
|
||||
return runtime.GOOS
|
||||
}
|
||||
os = strings.ToLower(os)
|
||||
|
||||
switch os {
|
||||
case "macos":
|
||||
os = "darwin"
|
||||
}
|
||||
return os
|
||||
}
|
||||
|
||||
// normalizeArch normalizes the architecture.
|
||||
func normalizeArch(arch, variant string) (string, string) {
|
||||
arch, variant = strings.ToLower(arch), strings.ToLower(variant)
|
||||
switch arch {
|
||||
case "i386":
|
||||
arch = "386"
|
||||
variant = ""
|
||||
case "x86_64", "x86-64", "amd64":
|
||||
arch = "amd64"
|
||||
if variant == "v1" {
|
||||
variant = ""
|
||||
}
|
||||
case "aarch64", "arm64":
|
||||
arch = "arm64"
|
||||
switch variant {
|
||||
case "8", "v8":
|
||||
variant = ""
|
||||
}
|
||||
case "armhf":
|
||||
arch = "arm"
|
||||
variant = "v7"
|
||||
case "armel":
|
||||
arch = "arm"
|
||||
variant = "v6"
|
||||
case "arm":
|
||||
switch variant {
|
||||
case "", "7":
|
||||
variant = "v7"
|
||||
case "5", "6", "8":
|
||||
variant = "v" + variant
|
||||
}
|
||||
}
|
||||
|
||||
return arch, variant
|
||||
}
|
||||
@@ -16,12 +16,27 @@
|
||||
|
||||
package platforms
|
||||
|
||||
import (
|
||||
"github.com/containerd/platforms"
|
||||
specs "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
)
|
||||
|
||||
// DefaultString returns the default string specifier for the platform.
|
||||
func DefaultString() string {
|
||||
return Format(DefaultSpec())
|
||||
return platforms.DefaultString()
|
||||
}
|
||||
|
||||
// DefaultStrict returns strict form of Default.
|
||||
func DefaultStrict() MatchComparer {
|
||||
return OnlyStrict(DefaultSpec())
|
||||
return platforms.DefaultStrict()
|
||||
}
|
||||
|
||||
// DefaultSpec returns the current platform's default platform specification.
|
||||
func DefaultSpec() specs.Platform {
|
||||
return platforms.DefaultSpec()
|
||||
}
|
||||
|
||||
// Default returns the default matcher for the platform.
|
||||
func Default() MatchComparer {
|
||||
return platforms.Default()
|
||||
}
|
||||
|
||||
@@ -1,44 +0,0 @@
|
||||
//go:build darwin
|
||||
|
||||
/*
|
||||
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 (
|
||||
"runtime"
|
||||
|
||||
specs "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
)
|
||||
|
||||
// DefaultSpec returns the current platform's default platform specification.
|
||||
func DefaultSpec() specs.Platform {
|
||||
return specs.Platform{
|
||||
OS: runtime.GOOS,
|
||||
Architecture: runtime.GOARCH,
|
||||
// The Variant field will be empty if arch != ARM.
|
||||
Variant: cpuVariant(),
|
||||
}
|
||||
}
|
||||
|
||||
// Default returns the default matcher for the platform.
|
||||
func Default() MatchComparer {
|
||||
return Ordered(DefaultSpec(), specs.Platform{
|
||||
// darwin runtime also supports Linux binary via runu/LKL
|
||||
OS: "linux",
|
||||
Architecture: runtime.GOARCH,
|
||||
})
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
/*
|
||||
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 (
|
||||
"runtime"
|
||||
|
||||
specs "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
)
|
||||
|
||||
// DefaultSpec returns the current platform's default platform specification.
|
||||
func DefaultSpec() specs.Platform {
|
||||
return specs.Platform{
|
||||
OS: runtime.GOOS,
|
||||
Architecture: runtime.GOARCH,
|
||||
// The Variant field will be empty if arch != ARM.
|
||||
Variant: cpuVariant(),
|
||||
}
|
||||
}
|
||||
|
||||
// Default returns the default matcher for the platform.
|
||||
func Default() MatchComparer {
|
||||
return Ordered(DefaultSpec(), specs.Platform{
|
||||
OS: "linux",
|
||||
Architecture: runtime.GOARCH,
|
||||
// The Variant field will be empty if arch != ARM.
|
||||
Variant: cpuVariant(),
|
||||
})
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
//go:build !windows && !darwin && !freebsd
|
||||
|
||||
/*
|
||||
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 (
|
||||
"runtime"
|
||||
|
||||
specs "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
)
|
||||
|
||||
// DefaultSpec returns the current platform's default platform specification.
|
||||
func DefaultSpec() specs.Platform {
|
||||
return specs.Platform{
|
||||
OS: runtime.GOOS,
|
||||
Architecture: runtime.GOARCH,
|
||||
// The Variant field will be empty if arch != ARM.
|
||||
Variant: cpuVariant(),
|
||||
}
|
||||
}
|
||||
|
||||
// Default returns the default matcher for the platform.
|
||||
func Default() MatchComparer {
|
||||
return Only(DefaultSpec())
|
||||
}
|
||||
@@ -1,44 +0,0 @@
|
||||
//go:build !windows
|
||||
|
||||
/*
|
||||
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 (
|
||||
"reflect"
|
||||
"runtime"
|
||||
"testing"
|
||||
|
||||
specs "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
)
|
||||
|
||||
func TestDefault(t *testing.T) {
|
||||
expected := specs.Platform{
|
||||
OS: runtime.GOOS,
|
||||
Architecture: runtime.GOARCH,
|
||||
Variant: cpuVariant(),
|
||||
}
|
||||
p := DefaultSpec()
|
||||
if !reflect.DeepEqual(p, expected) {
|
||||
t.Fatalf("default platform not as expected: %#v != %#v", p, expected)
|
||||
}
|
||||
|
||||
s := DefaultString()
|
||||
if s != Format(p) {
|
||||
t.Fatalf("default specifier should match formatted default spec: %v != %v", s, p)
|
||||
}
|
||||
}
|
||||
@@ -1,119 +0,0 @@
|
||||
/*
|
||||
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 (
|
||||
"fmt"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/Microsoft/hcsshim/osversion"
|
||||
specs "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
// DefaultSpec returns the current platform's default platform specification.
|
||||
func DefaultSpec() specs.Platform {
|
||||
major, minor, build := windows.RtlGetNtVersionNumbers()
|
||||
return specs.Platform{
|
||||
OS: runtime.GOOS,
|
||||
Architecture: runtime.GOARCH,
|
||||
OSVersion: fmt.Sprintf("%d.%d.%d", major, minor, build),
|
||||
// The Variant field will be empty if arch != ARM.
|
||||
Variant: cpuVariant(),
|
||||
}
|
||||
}
|
||||
|
||||
type windowsmatcher struct {
|
||||
specs.Platform
|
||||
osVersionPrefix string
|
||||
defaultMatcher Matcher
|
||||
}
|
||||
|
||||
// Match matches platform with the same windows major, minor
|
||||
// and build version.
|
||||
func (m windowsmatcher) Match(p specs.Platform) bool {
|
||||
match := m.defaultMatcher.Match(p)
|
||||
|
||||
if match && m.OS == "windows" {
|
||||
// HPC containers do not have OS version filled
|
||||
if p.OSVersion == "" {
|
||||
return true
|
||||
}
|
||||
|
||||
hostOsVersion := GetOsVersion(m.osVersionPrefix)
|
||||
ctrOsVersion := GetOsVersion(p.OSVersion)
|
||||
return osversion.CheckHostAndContainerCompat(hostOsVersion, ctrOsVersion)
|
||||
}
|
||||
|
||||
return match
|
||||
}
|
||||
|
||||
func GetOsVersion(osVersionPrefix string) osversion.OSVersion {
|
||||
parts := strings.Split(osVersionPrefix, ".")
|
||||
if len(parts) < 3 {
|
||||
return osversion.OSVersion{}
|
||||
}
|
||||
|
||||
majorVersion, _ := strconv.Atoi(parts[0])
|
||||
minorVersion, _ := strconv.Atoi(parts[1])
|
||||
buildNumber, _ := strconv.Atoi(parts[2])
|
||||
|
||||
return osversion.OSVersion{
|
||||
MajorVersion: uint8(majorVersion),
|
||||
MinorVersion: uint8(minorVersion),
|
||||
Build: uint16(buildNumber),
|
||||
}
|
||||
}
|
||||
|
||||
// Less sorts matched platforms in front of other platforms.
|
||||
// For matched platforms, it puts platforms with larger revision
|
||||
// number in front.
|
||||
func (m windowsmatcher) Less(p1, p2 specs.Platform) bool {
|
||||
m1, m2 := m.Match(p1), m.Match(p2)
|
||||
if m1 && m2 {
|
||||
r1, r2 := revision(p1.OSVersion), revision(p2.OSVersion)
|
||||
return r1 > r2
|
||||
}
|
||||
return m1 && !m2
|
||||
}
|
||||
|
||||
func revision(v string) int {
|
||||
parts := strings.Split(v, ".")
|
||||
if len(parts) < 4 {
|
||||
return 0
|
||||
}
|
||||
r, err := strconv.Atoi(parts[3])
|
||||
if err != nil {
|
||||
return 0
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
func prefix(v string) string {
|
||||
parts := strings.Split(v, ".")
|
||||
if len(parts) < 4 {
|
||||
return v
|
||||
}
|
||||
return strings.Join(parts[0:3], ".")
|
||||
}
|
||||
|
||||
// Default returns the current platform's default platform specification.
|
||||
func Default() MatchComparer {
|
||||
return Only(DefaultSpec())
|
||||
}
|
||||
@@ -1,374 +0,0 @@
|
||||
/*
|
||||
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 (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"runtime"
|
||||
"sort"
|
||||
"testing"
|
||||
|
||||
imagespec "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
func TestDefault(t *testing.T) {
|
||||
major, minor, build := windows.RtlGetNtVersionNumbers()
|
||||
expected := imagespec.Platform{
|
||||
OS: runtime.GOOS,
|
||||
Architecture: runtime.GOARCH,
|
||||
OSVersion: fmt.Sprintf("%d.%d.%d", major, minor, build),
|
||||
Variant: cpuVariant(),
|
||||
}
|
||||
p := DefaultSpec()
|
||||
if !reflect.DeepEqual(p, expected) {
|
||||
t.Fatalf("default platform not as expected: %#v != %#v", p, expected)
|
||||
}
|
||||
|
||||
s := DefaultString()
|
||||
if s != Format(p) {
|
||||
t.Fatalf("default specifier should match formatted default spec: %v != %v", s, p)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDefaultMatchComparer(t *testing.T) {
|
||||
defaultMatcher := Default()
|
||||
|
||||
for _, test := range []struct {
|
||||
platform imagespec.Platform
|
||||
match bool
|
||||
}{
|
||||
{
|
||||
platform: DefaultSpec(),
|
||||
match: true,
|
||||
},
|
||||
{
|
||||
platform: imagespec.Platform{
|
||||
OS: "linux",
|
||||
Architecture: runtime.GOARCH,
|
||||
},
|
||||
match: false,
|
||||
},
|
||||
} {
|
||||
assert.Equal(t, test.match, defaultMatcher.Match(test.platform))
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestMatchComparerMatch_WCOW(t *testing.T) {
|
||||
major, minor, build := windows.RtlGetNtVersionNumbers()
|
||||
buildStr := fmt.Sprintf("%d.%d.%d", major, minor, build)
|
||||
m := windowsmatcher{
|
||||
Platform: DefaultSpec(),
|
||||
osVersionPrefix: buildStr,
|
||||
defaultMatcher: &matcher{
|
||||
Platform: Normalize(DefaultSpec()),
|
||||
},
|
||||
}
|
||||
for _, test := range []struct {
|
||||
platform imagespec.Platform
|
||||
match bool
|
||||
}{
|
||||
{
|
||||
platform: DefaultSpec(),
|
||||
match: true,
|
||||
},
|
||||
{
|
||||
platform: imagespec.Platform{
|
||||
Architecture: "amd64",
|
||||
OS: "windows",
|
||||
OSVersion: buildStr + ".1",
|
||||
},
|
||||
match: true,
|
||||
},
|
||||
{
|
||||
platform: imagespec.Platform{
|
||||
Architecture: "amd64",
|
||||
OS: "windows",
|
||||
OSVersion: buildStr + ".2",
|
||||
},
|
||||
match: true,
|
||||
},
|
||||
{
|
||||
platform: imagespec.Platform{
|
||||
Architecture: "amd64",
|
||||
OS: "windows",
|
||||
// Use an nonexistent Windows build so we don't get a match. Ws2019's build is 17763/
|
||||
OSVersion: "10.0.17762.1",
|
||||
},
|
||||
match: false,
|
||||
},
|
||||
{
|
||||
platform: imagespec.Platform{
|
||||
Architecture: "amd64",
|
||||
OS: "windows",
|
||||
// Use an nonexistent Windows build so we don't get a match. Ws2019's build is 17763/
|
||||
OSVersion: "10.0.17764.1",
|
||||
},
|
||||
match: false,
|
||||
},
|
||||
{
|
||||
platform: imagespec.Platform{
|
||||
Architecture: "amd64",
|
||||
OS: "windows",
|
||||
},
|
||||
match: true,
|
||||
},
|
||||
{
|
||||
platform: imagespec.Platform{
|
||||
Architecture: "amd64",
|
||||
OS: "linux",
|
||||
},
|
||||
match: false,
|
||||
},
|
||||
} {
|
||||
assert.Equal(t, test.match, m.Match(test.platform), "should match: %t, %s to %s", test.match, m.Platform, test.platform)
|
||||
}
|
||||
}
|
||||
|
||||
// TestMatchComparerMatch_ABICheckWCOW checks windows platform matcher
|
||||
// behavior for stable ABI and non-stable ABI compliant versions
|
||||
func TestMatchComparerMatch_ABICheckWCOW(t *testing.T) {
|
||||
platformWS2019 := imagespec.Platform{
|
||||
Architecture: "amd64",
|
||||
OS: "windows",
|
||||
OSVersion: "10.0.17763",
|
||||
}
|
||||
platformWS2022 := imagespec.Platform{
|
||||
Architecture: "amd64",
|
||||
OS: "windows",
|
||||
OSVersion: "10.0.20348",
|
||||
}
|
||||
platformWindows11 := imagespec.Platform{
|
||||
Architecture: "amd64",
|
||||
OS: "windows",
|
||||
OSVersion: "10.0.22621",
|
||||
}
|
||||
matcherWS2019 := windowsmatcher{
|
||||
Platform: platformWS2019,
|
||||
osVersionPrefix: platformWS2019.OSVersion,
|
||||
defaultMatcher: &matcher{
|
||||
Platform: Normalize(platformWS2019),
|
||||
},
|
||||
}
|
||||
matcherWS2022 := windowsmatcher{
|
||||
Platform: platformWS2022,
|
||||
osVersionPrefix: platformWS2022.OSVersion,
|
||||
defaultMatcher: &matcher{
|
||||
Platform: Normalize(platformWS2022),
|
||||
},
|
||||
}
|
||||
matcherWindows11 := windowsmatcher{
|
||||
Platform: platformWindows11,
|
||||
osVersionPrefix: platformWindows11.OSVersion,
|
||||
defaultMatcher: &matcher{
|
||||
Platform: Normalize(platformWindows11),
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range []struct {
|
||||
hostPlatformMatcher windowsmatcher
|
||||
testPlatform imagespec.Platform
|
||||
match bool
|
||||
}{
|
||||
{
|
||||
hostPlatformMatcher: matcherWS2019,
|
||||
testPlatform: imagespec.Platform{
|
||||
Architecture: "amd64",
|
||||
OS: "windows",
|
||||
OSVersion: "10.0.17763",
|
||||
},
|
||||
match: true,
|
||||
},
|
||||
{
|
||||
hostPlatformMatcher: matcherWS2019,
|
||||
testPlatform: imagespec.Platform{
|
||||
Architecture: "amd64",
|
||||
OS: "windows",
|
||||
OSVersion: "10.0.20348",
|
||||
},
|
||||
match: false,
|
||||
},
|
||||
{
|
||||
hostPlatformMatcher: matcherWS2022,
|
||||
testPlatform: imagespec.Platform{
|
||||
Architecture: "amd64",
|
||||
OS: "windows",
|
||||
OSVersion: "10.0.17763",
|
||||
},
|
||||
match: false,
|
||||
},
|
||||
{
|
||||
hostPlatformMatcher: matcherWS2022,
|
||||
testPlatform: imagespec.Platform{
|
||||
Architecture: "amd64",
|
||||
OS: "windows",
|
||||
OSVersion: "10.0.20348",
|
||||
},
|
||||
match: true,
|
||||
},
|
||||
{
|
||||
hostPlatformMatcher: matcherWindows11,
|
||||
testPlatform: imagespec.Platform{
|
||||
Architecture: "amd64",
|
||||
OS: "windows",
|
||||
OSVersion: "10.0.17763",
|
||||
},
|
||||
match: false,
|
||||
},
|
||||
{
|
||||
hostPlatformMatcher: matcherWindows11,
|
||||
testPlatform: imagespec.Platform{
|
||||
Architecture: "amd64",
|
||||
OS: "windows",
|
||||
OSVersion: "10.0.20348",
|
||||
},
|
||||
match: true,
|
||||
},
|
||||
} {
|
||||
assert.Equal(t, test.match, test.hostPlatformMatcher.Match(test.testPlatform), "should match: %t, %s to %s", test.match, test.hostPlatformMatcher.Platform, test.testPlatform)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMatchComparerMatch_LCOW(t *testing.T) {
|
||||
major, minor, build := windows.RtlGetNtVersionNumbers()
|
||||
buildStr := fmt.Sprintf("%d.%d.%d", major, minor, build)
|
||||
m := windowsmatcher{
|
||||
Platform: imagespec.Platform{
|
||||
OS: "linux",
|
||||
Architecture: "amd64",
|
||||
},
|
||||
osVersionPrefix: "",
|
||||
defaultMatcher: &matcher{
|
||||
Platform: Normalize(imagespec.Platform{
|
||||
OS: "linux",
|
||||
Architecture: "amd64",
|
||||
},
|
||||
),
|
||||
},
|
||||
}
|
||||
for _, test := range []struct {
|
||||
platform imagespec.Platform
|
||||
match bool
|
||||
}{
|
||||
{
|
||||
platform: DefaultSpec(),
|
||||
match: false,
|
||||
},
|
||||
{
|
||||
platform: imagespec.Platform{
|
||||
Architecture: "amd64",
|
||||
OS: "windows",
|
||||
},
|
||||
match: false,
|
||||
},
|
||||
{
|
||||
platform: imagespec.Platform{
|
||||
Architecture: "amd64",
|
||||
OS: "windows",
|
||||
OSVersion: buildStr + ".2",
|
||||
},
|
||||
match: false,
|
||||
},
|
||||
{
|
||||
platform: imagespec.Platform{
|
||||
Architecture: "amd64",
|
||||
OS: "windows",
|
||||
// Use an nonexistent Windows build so we don't get a match. Ws2019's build is 17763/
|
||||
OSVersion: "10.0.17762.1",
|
||||
},
|
||||
match: false,
|
||||
},
|
||||
{
|
||||
platform: imagespec.Platform{
|
||||
Architecture: "amd64",
|
||||
OS: "linux",
|
||||
},
|
||||
match: true,
|
||||
},
|
||||
} {
|
||||
assert.Equal(t, test.match, m.Match(test.platform), "should match %b, %s to %s", test.match, m.Platform, test.platform)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMatchComparerLess(t *testing.T) {
|
||||
m := windowsmatcher{
|
||||
Platform: DefaultSpec(),
|
||||
osVersionPrefix: "10.0.17763",
|
||||
defaultMatcher: &matcher{
|
||||
Platform: Normalize(DefaultSpec()),
|
||||
},
|
||||
}
|
||||
platforms := []imagespec.Platform{
|
||||
{
|
||||
Architecture: "amd64",
|
||||
OS: "windows",
|
||||
OSVersion: "10.0.17764.1",
|
||||
},
|
||||
{
|
||||
Architecture: "amd64",
|
||||
OS: "windows",
|
||||
},
|
||||
{
|
||||
Architecture: "amd64",
|
||||
OS: "windows",
|
||||
OSVersion: "10.0.17763.1",
|
||||
},
|
||||
{
|
||||
Architecture: "amd64",
|
||||
OS: "windows",
|
||||
OSVersion: "10.0.17763.2",
|
||||
},
|
||||
{
|
||||
Architecture: "amd64",
|
||||
OS: "windows",
|
||||
OSVersion: "10.0.17762.1",
|
||||
},
|
||||
}
|
||||
expected := []imagespec.Platform{
|
||||
{
|
||||
Architecture: "amd64",
|
||||
OS: "windows",
|
||||
OSVersion: "10.0.17763.2",
|
||||
},
|
||||
{
|
||||
Architecture: "amd64",
|
||||
OS: "windows",
|
||||
OSVersion: "10.0.17763.1",
|
||||
},
|
||||
{
|
||||
Architecture: "amd64",
|
||||
OS: "windows",
|
||||
},
|
||||
{
|
||||
Architecture: "amd64",
|
||||
OS: "windows",
|
||||
OSVersion: "10.0.17764.1",
|
||||
},
|
||||
{
|
||||
Architecture: "amd64",
|
||||
OS: "windows",
|
||||
OSVersion: "10.0.17762.1",
|
||||
},
|
||||
}
|
||||
sort.SliceStable(platforms, func(i, j int) bool {
|
||||
return m.Less(platforms[i], platforms[j])
|
||||
})
|
||||
assert.Equal(t, expected, platforms)
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
/*
|
||||
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 "errors"
|
||||
|
||||
// These errors mirror the errors defined in [github.com/containerd/containerd/errdefs],
|
||||
// however, they are not exported as they are not expected to be used as sentinel
|
||||
// errors by consumers of this package.
|
||||
//
|
||||
//nolint:unused // not all errors are used on all platforms.
|
||||
var (
|
||||
errNotFound = errors.New("not found")
|
||||
errInvalidArgument = errors.New("invalid argument")
|
||||
errNotImplemented = errors.New("not implemented")
|
||||
)
|
||||
@@ -110,27 +110,15 @@
|
||||
package platforms
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"path"
|
||||
"regexp"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/containerd/platforms"
|
||||
specs "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
)
|
||||
|
||||
var (
|
||||
specifierRe = regexp.MustCompile(`^[A-Za-z0-9_-]+$`)
|
||||
)
|
||||
|
||||
// Platform is a type alias for convenience, so there is no need to import image-spec package everywhere.
|
||||
type Platform = specs.Platform
|
||||
type Platform = platforms.Platform
|
||||
|
||||
// Matcher matches platforms specifications, provided by an image or runtime.
|
||||
type Matcher interface {
|
||||
Match(platform specs.Platform) bool
|
||||
}
|
||||
type Matcher = platforms.Matcher
|
||||
|
||||
// NewMatcher returns a simple matcher based on the provided platform
|
||||
// specification. The returned matcher only looks for equality based on os,
|
||||
@@ -141,35 +129,12 @@ type Matcher interface {
|
||||
//
|
||||
// Applications should opt to use `Match` over directly parsing specifiers.
|
||||
func NewMatcher(platform specs.Platform) Matcher {
|
||||
return newDefaultMatcher(platform)
|
||||
}
|
||||
|
||||
type matcher struct {
|
||||
specs.Platform
|
||||
}
|
||||
|
||||
func (m *matcher) Match(platform specs.Platform) bool {
|
||||
normalized := Normalize(platform)
|
||||
return m.OS == normalized.OS &&
|
||||
m.Architecture == normalized.Architecture &&
|
||||
m.Variant == normalized.Variant
|
||||
}
|
||||
|
||||
func (m *matcher) String() string {
|
||||
return Format(m.Platform)
|
||||
return platforms.NewMatcher(platform)
|
||||
}
|
||||
|
||||
// ParseAll parses a list of platform specifiers into a list of platform.
|
||||
func ParseAll(specifiers []string) ([]specs.Platform, error) {
|
||||
platforms := make([]specs.Platform, len(specifiers))
|
||||
for i, s := range specifiers {
|
||||
p, err := Parse(s)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid platform %s: %w", s, err)
|
||||
}
|
||||
platforms[i] = p
|
||||
}
|
||||
return platforms, nil
|
||||
return platforms.ParseAll(specifiers)
|
||||
}
|
||||
|
||||
// Parse parses the platform specifier syntax into a platform declaration.
|
||||
@@ -181,101 +146,18 @@ func ParseAll(specifiers []string) ([]specs.Platform, error) {
|
||||
// back to the known set of architectures. The missing component will be
|
||||
// inferred based on the local environment.
|
||||
func Parse(specifier string) (specs.Platform, error) {
|
||||
if strings.Contains(specifier, "*") {
|
||||
// TODO(stevvooe): need to work out exact wildcard handling
|
||||
return specs.Platform{}, fmt.Errorf("%q: wildcards not yet supported: %w", specifier, errInvalidArgument)
|
||||
}
|
||||
|
||||
parts := strings.Split(specifier, "/")
|
||||
|
||||
for _, part := range parts {
|
||||
if !specifierRe.MatchString(part) {
|
||||
return specs.Platform{}, fmt.Errorf("%q is an invalid component of %q: platform specifier component must match %q: %w", part, specifier, specifierRe.String(), errInvalidArgument)
|
||||
}
|
||||
}
|
||||
|
||||
var p specs.Platform
|
||||
switch len(parts) {
|
||||
case 1:
|
||||
// in this case, we will test that the value might be an OS, then look
|
||||
// it up. If it is not known, we'll treat it as an architecture. Since
|
||||
// we have very little information about the platform here, we are
|
||||
// going to be a little more strict if we don't know about the argument
|
||||
// value.
|
||||
p.OS = normalizeOS(parts[0])
|
||||
if isKnownOS(p.OS) {
|
||||
// picks a default architecture
|
||||
p.Architecture = runtime.GOARCH
|
||||
if p.Architecture == "arm" && cpuVariant() != "v7" {
|
||||
p.Variant = cpuVariant()
|
||||
}
|
||||
|
||||
if p.OS == "windows" {
|
||||
p.OSVersion = GetWindowsOsVersion()
|
||||
}
|
||||
|
||||
return p, nil
|
||||
}
|
||||
|
||||
p.Architecture, p.Variant = normalizeArch(parts[0], "")
|
||||
if p.Architecture == "arm" && p.Variant == "v7" {
|
||||
p.Variant = ""
|
||||
}
|
||||
if isKnownArch(p.Architecture) {
|
||||
p.OS = runtime.GOOS
|
||||
return p, nil
|
||||
}
|
||||
|
||||
return specs.Platform{}, fmt.Errorf("%q: unknown operating system or architecture: %w", specifier, errInvalidArgument)
|
||||
case 2:
|
||||
// In this case, we treat as a regular os/arch pair. We don't care
|
||||
// about whether or not we know of the platform.
|
||||
p.OS = normalizeOS(parts[0])
|
||||
p.Architecture, p.Variant = normalizeArch(parts[1], "")
|
||||
if p.Architecture == "arm" && p.Variant == "v7" {
|
||||
p.Variant = ""
|
||||
}
|
||||
|
||||
if p.OS == "windows" {
|
||||
p.OSVersion = GetWindowsOsVersion()
|
||||
}
|
||||
|
||||
return p, nil
|
||||
case 3:
|
||||
// we have a fully specified variant, this is rare
|
||||
p.OS = normalizeOS(parts[0])
|
||||
p.Architecture, p.Variant = normalizeArch(parts[1], parts[2])
|
||||
if p.Architecture == "arm64" && p.Variant == "" {
|
||||
p.Variant = "v8"
|
||||
}
|
||||
|
||||
if p.OS == "windows" {
|
||||
p.OSVersion = GetWindowsOsVersion()
|
||||
}
|
||||
|
||||
return p, nil
|
||||
}
|
||||
|
||||
return specs.Platform{}, fmt.Errorf("%q: cannot parse platform specifier: %w", specifier, errInvalidArgument)
|
||||
return platforms.Parse(specifier)
|
||||
}
|
||||
|
||||
// MustParse is like Parses but panics if the specifier cannot be parsed.
|
||||
// Simplifies initialization of global variables.
|
||||
func MustParse(specifier string) specs.Platform {
|
||||
p, err := Parse(specifier)
|
||||
if err != nil {
|
||||
panic("platform: Parse(" + strconv.Quote(specifier) + "): " + err.Error())
|
||||
}
|
||||
return p
|
||||
return platforms.MustParse(specifier)
|
||||
}
|
||||
|
||||
// Format returns a string specifier from the provided platform specification.
|
||||
func Format(platform specs.Platform) string {
|
||||
if platform.OS == "" {
|
||||
return "unknown"
|
||||
}
|
||||
|
||||
return path.Join(platform.OS, platform.Architecture, platform.Variant)
|
||||
return platforms.Format(platform)
|
||||
}
|
||||
|
||||
// Normalize validates and translate the platform to the canonical value.
|
||||
@@ -283,8 +165,9 @@ func Format(platform specs.Platform) string {
|
||||
// For example, if "Aarch64" is encountered, we change it to "arm64" or if
|
||||
// "x86_64" is encountered, it becomes "amd64".
|
||||
func Normalize(platform specs.Platform) specs.Platform {
|
||||
platform.OS = normalizeOS(platform.OS)
|
||||
platform.Architecture, platform.Variant = normalizeArch(platform.Architecture, platform.Variant)
|
||||
|
||||
return platform
|
||||
return platforms.Normalize(platform)
|
||||
}
|
||||
|
||||
func GetWindowsOsVersion() string {
|
||||
return platforms.GetWindowsOsVersion()
|
||||
}
|
||||
|
||||
@@ -1,34 +0,0 @@
|
||||
//go:build !windows
|
||||
|
||||
/*
|
||||
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 (
|
||||
specs "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
)
|
||||
|
||||
// NewMatcher returns the default Matcher for containerd
|
||||
func newDefaultMatcher(platform specs.Platform) Matcher {
|
||||
return &matcher{
|
||||
Platform: Normalize(platform),
|
||||
}
|
||||
}
|
||||
|
||||
func GetWindowsOsVersion() string {
|
||||
return ""
|
||||
}
|
||||
@@ -1,376 +0,0 @@
|
||||
/*
|
||||
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 (
|
||||
"path"
|
||||
"reflect"
|
||||
"runtime"
|
||||
"testing"
|
||||
|
||||
specs "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
)
|
||||
|
||||
func TestParseSelector(t *testing.T) {
|
||||
var (
|
||||
defaultOS = runtime.GOOS
|
||||
defaultArch = runtime.GOARCH
|
||||
defaultVariant = ""
|
||||
)
|
||||
|
||||
if defaultArch == "arm" && cpuVariant() != "v7" {
|
||||
defaultVariant = cpuVariant()
|
||||
}
|
||||
|
||||
for _, testcase := range []struct {
|
||||
skip bool
|
||||
input string
|
||||
expected specs.Platform
|
||||
matches []specs.Platform
|
||||
formatted string
|
||||
}{
|
||||
// While wildcards are a valid use case for platform selection,
|
||||
// addressing these cases is outside the initial scope for this
|
||||
// package. When we do add platform wildcards, we should add in these
|
||||
// testcases to ensure that they are correctly represented.
|
||||
{
|
||||
skip: true,
|
||||
input: "*",
|
||||
expected: specs.Platform{
|
||||
OS: "*",
|
||||
Architecture: "*",
|
||||
},
|
||||
formatted: "*/*",
|
||||
},
|
||||
{
|
||||
skip: true,
|
||||
input: "linux/*",
|
||||
expected: specs.Platform{
|
||||
OS: "linux",
|
||||
Architecture: "*",
|
||||
},
|
||||
formatted: "linux/*",
|
||||
},
|
||||
{
|
||||
skip: true,
|
||||
input: "*/arm64",
|
||||
expected: specs.Platform{
|
||||
OS: "*",
|
||||
Architecture: "arm64",
|
||||
},
|
||||
matches: []specs.Platform{
|
||||
{
|
||||
OS: "*",
|
||||
Architecture: "aarch64",
|
||||
},
|
||||
{
|
||||
OS: "*",
|
||||
Architecture: "aarch64",
|
||||
Variant: "v8",
|
||||
},
|
||||
{
|
||||
OS: "*",
|
||||
Architecture: "arm64",
|
||||
Variant: "v8",
|
||||
},
|
||||
},
|
||||
formatted: "*/arm64",
|
||||
},
|
||||
{
|
||||
input: "linux/arm64",
|
||||
expected: specs.Platform{
|
||||
OS: "linux",
|
||||
Architecture: "arm64",
|
||||
},
|
||||
matches: []specs.Platform{
|
||||
{
|
||||
OS: "linux",
|
||||
Architecture: "aarch64",
|
||||
},
|
||||
{
|
||||
OS: "linux",
|
||||
Architecture: "aarch64",
|
||||
Variant: "v8",
|
||||
},
|
||||
{
|
||||
OS: "linux",
|
||||
Architecture: "arm64",
|
||||
Variant: "v8",
|
||||
},
|
||||
},
|
||||
formatted: "linux/arm64",
|
||||
},
|
||||
{
|
||||
input: "linux/arm64/v8",
|
||||
expected: specs.Platform{
|
||||
OS: "linux",
|
||||
Architecture: "arm64",
|
||||
Variant: "v8",
|
||||
},
|
||||
matches: []specs.Platform{
|
||||
{
|
||||
OS: "linux",
|
||||
Architecture: "aarch64",
|
||||
},
|
||||
{
|
||||
OS: "linux",
|
||||
Architecture: "aarch64",
|
||||
Variant: "v8",
|
||||
},
|
||||
{
|
||||
OS: "linux",
|
||||
Architecture: "arm64",
|
||||
},
|
||||
},
|
||||
formatted: "linux/arm64/v8",
|
||||
},
|
||||
{
|
||||
// NOTE(stevvooe): In this case, the consumer can assume this is v7
|
||||
// but we leave the variant blank. This will represent the vast
|
||||
// majority of arm images.
|
||||
input: "linux/arm",
|
||||
expected: specs.Platform{
|
||||
OS: "linux",
|
||||
Architecture: "arm",
|
||||
},
|
||||
matches: []specs.Platform{
|
||||
{
|
||||
OS: "linux",
|
||||
Architecture: "arm",
|
||||
Variant: "v7",
|
||||
},
|
||||
{
|
||||
OS: "linux",
|
||||
Architecture: "armhf",
|
||||
},
|
||||
{
|
||||
OS: "linux",
|
||||
Architecture: "arm",
|
||||
Variant: "7",
|
||||
},
|
||||
},
|
||||
formatted: "linux/arm",
|
||||
},
|
||||
{
|
||||
input: "linux/arm/v6",
|
||||
expected: specs.Platform{
|
||||
OS: "linux",
|
||||
Architecture: "arm",
|
||||
Variant: "v6",
|
||||
},
|
||||
matches: []specs.Platform{
|
||||
{
|
||||
OS: "linux",
|
||||
Architecture: "armel",
|
||||
},
|
||||
},
|
||||
formatted: "linux/arm/v6",
|
||||
},
|
||||
{
|
||||
input: "linux/arm/v7",
|
||||
expected: specs.Platform{
|
||||
OS: "linux",
|
||||
Architecture: "arm",
|
||||
Variant: "v7",
|
||||
},
|
||||
matches: []specs.Platform{
|
||||
{
|
||||
OS: "linux",
|
||||
Architecture: "arm",
|
||||
},
|
||||
{
|
||||
OS: "linux",
|
||||
Architecture: "armhf",
|
||||
},
|
||||
},
|
||||
formatted: "linux/arm/v7",
|
||||
},
|
||||
{
|
||||
input: "arm",
|
||||
expected: specs.Platform{
|
||||
OS: defaultOS,
|
||||
Architecture: "arm",
|
||||
},
|
||||
formatted: path.Join(defaultOS, "arm"),
|
||||
},
|
||||
{
|
||||
input: "armel",
|
||||
expected: specs.Platform{
|
||||
OS: defaultOS,
|
||||
Architecture: "arm",
|
||||
Variant: "v6",
|
||||
},
|
||||
formatted: path.Join(defaultOS, "arm/v6"),
|
||||
},
|
||||
{
|
||||
input: "armhf",
|
||||
expected: specs.Platform{
|
||||
OS: defaultOS,
|
||||
Architecture: "arm",
|
||||
},
|
||||
formatted: path.Join(defaultOS, "arm"),
|
||||
},
|
||||
{
|
||||
input: "Aarch64",
|
||||
expected: specs.Platform{
|
||||
OS: defaultOS,
|
||||
Architecture: "arm64",
|
||||
},
|
||||
formatted: path.Join(defaultOS, "arm64"),
|
||||
},
|
||||
{
|
||||
input: "x86_64",
|
||||
expected: specs.Platform{
|
||||
OS: defaultOS,
|
||||
Architecture: "amd64",
|
||||
},
|
||||
formatted: path.Join(defaultOS, "amd64"),
|
||||
},
|
||||
{
|
||||
input: "Linux/x86_64",
|
||||
expected: specs.Platform{
|
||||
OS: "linux",
|
||||
Architecture: "amd64",
|
||||
},
|
||||
formatted: "linux/amd64",
|
||||
},
|
||||
{
|
||||
input: "i386",
|
||||
expected: specs.Platform{
|
||||
OS: defaultOS,
|
||||
Architecture: "386",
|
||||
},
|
||||
formatted: path.Join(defaultOS, "386"),
|
||||
},
|
||||
{
|
||||
input: "linux",
|
||||
expected: specs.Platform{
|
||||
OS: "linux",
|
||||
Architecture: defaultArch,
|
||||
Variant: defaultVariant,
|
||||
},
|
||||
formatted: path.Join("linux", defaultArch, defaultVariant),
|
||||
},
|
||||
{
|
||||
input: "s390x",
|
||||
expected: specs.Platform{
|
||||
OS: defaultOS,
|
||||
Architecture: "s390x",
|
||||
},
|
||||
formatted: path.Join(defaultOS, "s390x"),
|
||||
},
|
||||
{
|
||||
input: "linux/s390x",
|
||||
expected: specs.Platform{
|
||||
OS: "linux",
|
||||
Architecture: "s390x",
|
||||
},
|
||||
formatted: "linux/s390x",
|
||||
},
|
||||
{
|
||||
input: "macOS",
|
||||
expected: specs.Platform{
|
||||
OS: "darwin",
|
||||
Architecture: defaultArch,
|
||||
Variant: defaultVariant,
|
||||
},
|
||||
formatted: path.Join("darwin", defaultArch, defaultVariant),
|
||||
},
|
||||
} {
|
||||
t.Run(testcase.input, func(t *testing.T) {
|
||||
if testcase.skip {
|
||||
t.Skip("this case is not yet supported")
|
||||
}
|
||||
p, err := Parse(testcase.input)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(p, testcase.expected) {
|
||||
t.Fatalf("platform did not match expected: %#v != %#v", p, testcase.expected)
|
||||
}
|
||||
|
||||
m := NewMatcher(p)
|
||||
|
||||
// ensure that match works on the input to the output.
|
||||
if ok := m.Match(testcase.expected); !ok {
|
||||
t.Fatalf("expected specifier %q matches %#v", testcase.input, testcase.expected)
|
||||
}
|
||||
for _, mc := range testcase.matches {
|
||||
if ok := m.Match(mc); !ok {
|
||||
t.Fatalf("expected specifier %q matches %#v", testcase.input, mc)
|
||||
}
|
||||
}
|
||||
|
||||
formatted := Format(p)
|
||||
if formatted != testcase.formatted {
|
||||
t.Fatalf("unexpected format: %q != %q", formatted, testcase.formatted)
|
||||
}
|
||||
|
||||
// re-parse the formatted output and ensure we are stable
|
||||
reparsed, err := Parse(formatted)
|
||||
if err != nil {
|
||||
t.Fatalf("error parsing formatted output: %v", err)
|
||||
}
|
||||
|
||||
if Format(reparsed) != formatted {
|
||||
t.Fatalf("normalized output did not survive the round trip: %v != %v", Format(reparsed), formatted)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseSelectorInvalid(t *testing.T) {
|
||||
for _, testcase := range []struct {
|
||||
input string
|
||||
}{
|
||||
{
|
||||
input: "", // empty
|
||||
},
|
||||
{
|
||||
input: "/linux/arm", // leading slash
|
||||
},
|
||||
{
|
||||
input: "linux/arm/", // trailing slash
|
||||
},
|
||||
{
|
||||
input: "linux /arm", // spaces
|
||||
},
|
||||
{
|
||||
input: "linux/&arm", // invalid character
|
||||
},
|
||||
{
|
||||
input: "linux/arm/foo/bar", // too many components
|
||||
},
|
||||
} {
|
||||
t.Run(testcase.input, func(t *testing.T) {
|
||||
if _, err := Parse(testcase.input); err == nil {
|
||||
t.Fatalf("should have received an error")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func FuzzPlatformsParse(f *testing.F) {
|
||||
f.Add("linux/amd64")
|
||||
f.Fuzz(func(t *testing.T, s string) {
|
||||
pf, err := Parse(s)
|
||||
if err != nil && (pf.OS != "" || pf.Architecture != "") {
|
||||
t.Errorf("either %+v or %+v must be nil", err, pf)
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
/*
|
||||
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 (
|
||||
"fmt"
|
||||
|
||||
specs "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
// NewMatcher returns a Windows matcher that will match on osVersionPrefix if
|
||||
// the platform is Windows otherwise use the default matcher
|
||||
func newDefaultMatcher(platform specs.Platform) Matcher {
|
||||
prefix := prefix(platform.OSVersion)
|
||||
return windowsmatcher{
|
||||
Platform: platform,
|
||||
osVersionPrefix: prefix,
|
||||
defaultMatcher: &matcher{
|
||||
Platform: Normalize(platform),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func GetWindowsOsVersion() string {
|
||||
major, minor, build := windows.RtlGetNtVersionNumbers()
|
||||
return fmt.Sprintf("%d.%d.%d", major, minor, build)
|
||||
}
|
||||
@@ -1,41 +0,0 @@
|
||||
/*
|
||||
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 (
|
||||
"testing"
|
||||
|
||||
specs "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestNormalize(t *testing.T) {
|
||||
require.Equal(t, DefaultSpec(), Normalize(DefaultSpec()))
|
||||
}
|
||||
|
||||
func TestFallbackOnOSVersion(t *testing.T) {
|
||||
p := specs.Platform{
|
||||
OS: "windows",
|
||||
Architecture: "amd64",
|
||||
OSVersion: "99.99.99.99",
|
||||
}
|
||||
|
||||
other := specs.Platform{OS: p.OS, Architecture: p.Architecture}
|
||||
|
||||
m := NewMatcher(p)
|
||||
require.True(t, m.Match(other))
|
||||
}
|
||||
Reference in New Issue
Block a user