Merge pull request #1575 from Weichen81/arm-multi-arch

Fix pull-multi-arch images for Arm
This commit is contained in:
Phil Estes 2018-01-03 09:54:24 -05:00 committed by GitHub
commit 1d896a82c2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 130 additions and 1 deletions

85
platforms/cpuinfo.go Normal file
View File

@ -0,0 +1,85 @@
package platforms
import (
"bufio"
"os"
"runtime"
"strings"
"github.com/containerd/containerd/errdefs"
"github.com/containerd/containerd/log"
"github.com/pkg/errors"
)
// Present the ARM instruction set architecture, eg: v7, v8
var cpuVariant string
func init() {
if isArmArch(runtime.GOARCH) {
cpuVariant = getCPUVariant()
} else {
cpuVariant = ""
}
}
// 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) {
if !isLinuxOS(runtime.GOOS) {
return "", errors.Wrapf(errdefs.ErrNotImplemented, "getCPUInfo for OS %s", runtime.GOOS)
}
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 "", errors.Wrapf(errdefs.ErrNotFound, "getCPUInfo for pattern: %s", pattern)
}
func getCPUVariant() string {
variant, err := getCPUInfo("Cpu architecture")
if err != nil {
log.L.WithError(err).Error("failure getting variant")
return ""
}
switch variant {
case "8":
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
}

24
platforms/cpuinfo_test.go Normal file
View File

@ -0,0 +1,24 @@
package platforms
import (
"runtime"
"testing"
)
func TestCPUVariant(t *testing.T) {
if !isArmArch(runtime.GOARCH) || !isLinuxOS(runtime.GOOS) {
t.Skip("only relevant on linux/arm")
}
variants := []string{"v8", "v7", "v6", "v5", "v4", "v3"}
p := getCPUVariant()
for _, variant := range variants {
if p == variant {
t.Logf("got valid variant as expected: %#v = %#v\n", p, variant)
return
}
}
t.Fatalf("could not get valid variant as expected: %v\n", variants)
}

View File

@ -5,6 +5,13 @@ import (
"strings"
)
// isLinuxOS returns true if the operating system is Linux.
//
// The OS value should be normalized before calling this function.
func isLinuxOS(os string) bool {
return os == "linux"
}
// These function are generated from from https://golang.org/src/go/build/syslist.go.
//
// We use switch statements because they are slightly faster than map lookups
@ -21,6 +28,17 @@ func isKnownOS(os string) bool {
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.

View File

@ -16,6 +16,7 @@ func DefaultSpec() specs.Platform {
return specs.Platform{
OS: runtime.GOOS,
Architecture: runtime.GOARCH,
// TODO(stevvooe): Need to resolve GOARM for arm hosts.
// The Variant field will be empty if arch != ARM.
Variant: cpuVariant,
}
}

View File

@ -12,6 +12,7 @@ func TestDefault(t *testing.T) {
expected := specs.Platform{
OS: runtime.GOOS,
Architecture: runtime.GOARCH,
Variant: cpuVariant,
}
p := DefaultSpec()
if !reflect.DeepEqual(p, expected) {