Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add AMD64 micro architecture level support #7

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 28 additions & 3 deletions cpuinfo.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,11 @@ import (
"sync"

"github.com/containerd/log"
amd64variant "github.com/tonistiigi/go-archvariant"
)

// Present the ARM instruction set architecture, eg: v7, v8
// Present the instruction set architecture, eg: v7, v8 for ARM CPU,
// v3, v4 for AMD64 CPU.
// Don't use this value directly; call cpuVariant() instead.
var cpuVariantValue string

Expand All @@ -33,11 +35,34 @@ func cpuVariant() string {
cpuVariantOnce.Do(func() {
if isArmArch(runtime.GOARCH) {
var err error
cpuVariantValue, err = getCPUVariant()
cpuVariantValue, err = getArmCPUVariant()
if err != nil {
log.L.Errorf("Error getCPUVariant for OS %s: %v", runtime.GOOS, err)
log.L.Errorf("Error getArmCPUVariant for OS %s: %v", runtime.GOOS, err)
}
}
})
return cpuVariantValue
}

func cpuVariantMaximum() string {
cpuVariantOnce.Do(func() {
if isArmArch(runtime.GOARCH) {
var err error
cpuVariantValue, err = getArmCPUVariant()
if err != nil {
log.L.Errorf("Error getArmCPUVariant for OS %s: %v", runtime.GOOS, err)
}
} else if isAmd64Arch(runtime.GOARCH) {
var err error
cpuVariantValue, err = getAmd64MicroArchLevel()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should not change the value of DefaultSpec(). Default variant for amd64 is still v1 and v8 for arm64 even if chip is v9 compatible. Eg. image created on amd64 system will always be linux/amd64/v1 (normalized to linux/amd64) if user didn't specify that they want to create specific variant (or multiple images with different variants). This does not change depending on whether the building system happens to have AVX support or not.

I'd recommend creating a new function like MaximumSpec similar to https://github.com/moby/moby/blob/v26.1.4/distribution/pull_v2.go#L1089-L1096 that can be used in the cases where comparison to the maximum compatible version is needed (eg. when pulling multi-arch image manifest).

if err != nil {
log.L.Errorf("Error getAmd64MicroArchLevel for OS %s: %v", runtime.GOOS, err)
}
}
})
return cpuVariantValue
}

func getAmd64MicroArchLevel() (string, error) {
return amd64variant.AMD64Variant(), nil
}
4 changes: 2 additions & 2 deletions cpuinfo_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -106,12 +106,12 @@ func getCPUVariantFromArch(arch string) (string, error) {
return variant, nil
}

// getCPUVariant returns cpu variant for ARM
// getArmCPUVariant 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) {
func getArmCPUVariant() (string, error) {
variant, err := getCPUInfo("Cpu architecture")
if err != nil {
if errors.Is(err, errNotFound) {
Expand Down
2 changes: 1 addition & 1 deletion cpuinfo_linux_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ func TestCPUVariant(t *testing.T) {

variants := []string{"v8", "v7", "v6", "v5", "v4", "v3"}

p, err := getCPUVariant()
p, err := getArmCPUVariant()
if err != nil {
t.Fatalf("Error getting CPU variant: %v", err)
return
Expand Down
4 changes: 2 additions & 2 deletions cpuinfo_other.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ import (
"runtime"
)

func getCPUVariant() (string, error) {
func getArmCPUVariant() (string, error) {

var variant string

Expand All @@ -48,7 +48,7 @@ func getCPUVariant() (string, error) {
variant = "unknown"
}
} else {
return "", fmt.Errorf("getCPUVariant for OS %s: %v", runtime.GOOS, errNotImplemented)
return "", fmt.Errorf("getArmCPUVariant for OS %s: %v", runtime.GOOS, errNotImplemented)
}

return variant, nil
Expand Down
7 changes: 7 additions & 0 deletions database.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,13 @@ func isArmArch(arch string) bool {
return false
}

// isAmd64Arch returns true if the architecture is AMD64.
//
// The arch value should be normalized before being passed to this function.
func isAmd64Arch(arch string) bool {
return arch == "amd64"
}

// isKnownArch returns true if we know about the architecture.
//
// The arch value should be normalized before being passed to this function.
Expand Down
10 changes: 10 additions & 0 deletions defaults.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,13 @@ func DefaultString() string {
func DefaultStrict() MatchComparer {
return OnlyStrict(DefaultSpec())
}

// MaximumString returns the maximum string specifier for the platform.
func MaximumString() string {
return FormatAll(MaximumSpec())
}

// MaximumStrict returns strict form of Maximum.
func MaximumStrict() MatchComparer {
return OnlyStrict(MaximumSpec())
}
19 changes: 19 additions & 0 deletions defaults_darwin.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,3 +42,22 @@ func Default() MatchComparer {
Architecture: runtime.GOARCH,
})
}

// MaximumSpec returns the current platform's maximum platform specification.
func MaximumSpec() specs.Platform {
return specs.Platform{
OS: runtime.GOOS,
Architecture: runtime.GOARCH,
// The Variant field will be empty if arch != ARM and AMD64.
Variant: cpuVariantMaximum(),
}
}

// Maximum returns the maximum matcher for the platform.
func Maximum() MatchComparer {
return Ordered(MaximumSpec(), specs.Platform{
// darwin runtime also supports Linux binary via runu/LKL
OS: "linux",
Architecture: runtime.GOARCH,
})
}
20 changes: 20 additions & 0 deletions defaults_freebsd.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,3 +41,23 @@ func Default() MatchComparer {
Variant: cpuVariant(),
})
}

// MaximumSpec returns the current platform's maximum platform specification.
func MaximumSpec() specs.Platform {
return specs.Platform{
OS: runtime.GOOS,
Architecture: runtime.GOARCH,
// The Variant field will be empty if arch != ARM and AMD64.
Variant: cpuVariantMaximum(),
}
}

// Maximum returns the maximum matcher for the platform.
func Maximum() MatchComparer {
return Ordered(MaximumSpec(), specs.Platform{
OS: "linux",
Architecture: runtime.GOARCH,
// The Variant field will be empty if arch != ARM and AMD64.
Variant: cpuVariantMaximum(),
})
}
15 changes: 15 additions & 0 deletions defaults_unix.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,3 +38,18 @@ func DefaultSpec() specs.Platform {
func Default() MatchComparer {
return Only(DefaultSpec())
}

// MaximumSpec returns the current platform's maximum platform specification.
func MaximumSpec() specs.Platform {
return specs.Platform{
OS: runtime.GOOS,
Architecture: runtime.GOARCH,
// The Variant field will be empty if arch != ARM and AMD64.
Variant: cpuVariantMaximum(),
}
}

// Maximum returns the maximum matcher for the platform.
func Maximum() MatchComparer {
return Only(MaximumSpec())
}
17 changes: 17 additions & 0 deletions defaults_unix_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,3 +42,20 @@ func TestDefault(t *testing.T) {
t.Fatalf("default specifier should match formatted default spec: %v != %v", s, p)
}
}

func TestMaximum(t *testing.T) {
expected := specs.Platform{
OS: runtime.GOOS,
Architecture: runtime.GOARCH,
Variant: cpuVariantMaximum(),
}
p := MaximumSpec()
if !reflect.DeepEqual(p, expected) {
t.Fatalf("maximum platform not as expected: %#v != %#v", p, expected)
}

s := MaximumString()
if s != FormatAll(p) {
t.Fatalf("maximum specifier should match formatted maximum spec: %v != %v", s, p)
}
}
17 changes: 17 additions & 0 deletions defaults_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -116,3 +116,20 @@ func prefix(v string) string {
func Default() MatchComparer {
return Only(DefaultSpec())
}

// MaximumSpec returns the current platform's maximum platform specification.
func MaximumSpec() 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: cpuVariantMaximum(),
}
}

// Maximum returns the current platform's maximum platform specification.
func Maximum() MatchComparer {
return Only(MaximumSpec())
}
19 changes: 19 additions & 0 deletions defaults_windows_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,25 @@ func TestDefault(t *testing.T) {
}
}

func TestMaximum(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: cpuVariantMaximum(),
}
p := MaximumSpec()
if !reflect.DeepEqual(p, expected) {
t.Fatalf("maximum platform not as expected: %#v != %#v", p, expected)
}

s := MaximumString()
if s != FormatAll(p) {
t.Fatalf("maximum specifier should match formatted maximum spec: %v != %v", s, p)
}
}

func TestDefaultMatchComparer(t *testing.T) {
defaultMatcher := Default()

Expand Down
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ require (
github.com/containerd/log v0.1.0
github.com/opencontainers/image-spec v1.1.0
github.com/stretchr/testify v1.8.4
github.com/tonistiigi/go-archvariant v1.0.0
golang.org/x/sys v0.26.0
)

Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/tonistiigi/go-archvariant v1.0.0 h1:5LC1eDWiBNflnTF1prCiX09yfNHIxDC/aukdhCdTyb0=
github.com/tonistiigi/go-archvariant v1.0.0/go.mod h1:TxFmO5VS6vMq2kvs3ht04iPXtu2rUT/erOnGFYfk5Ho=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo=
golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
Expand Down
9 changes: 9 additions & 0 deletions platforms_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -263,6 +263,15 @@ func TestParseSelector(t *testing.T) {
formatted: "linux/amd64",
useV2Format: false,
},
{
input: "Linux/x86_64/v2",
expected: specs.Platform{
OS: "linux",
Architecture: "amd64",
Variant: "v2",
},
formatted: "linux/amd64/v2",
},
{
input: "i386",
expected: specs.Platform{
Expand Down
Loading