Skip to content

Commit

Permalink
libimage: normalize platform
Browse files Browse the repository at this point in the history
Buildah, containers and probably other container engines are normalizing
the platform parameters to support common values.  For instance, "x86_64"
is normalized to the OCI conformant "amd64".

Use the same normalization when copying images and looking up local
images.  Also add some debug logs to facilitate future debugging.

Fixes: containers/podman/issues/12680
Signed-off-by: Valentin Rothberg <[email protected]>
  • Loading branch information
vrothberg committed Dec 22, 2021
1 parent cf127e9 commit 393646b
Show file tree
Hide file tree
Showing 5 changed files with 92 additions and 13 deletions.
10 changes: 1 addition & 9 deletions libimage/copier.go
Original file line number Diff line number Diff line change
Expand Up @@ -218,15 +218,7 @@ func (r *Runtime) newCopier(options *CopyOptions) (*copier, error) {

c.systemContext.DockerArchiveAdditionalTags = options.dockerArchiveAdditionalTags

if options.Architecture != "" {
c.systemContext.ArchitectureChoice = options.Architecture
}
if options.OS != "" {
c.systemContext.OSChoice = options.OS
}
if options.Variant != "" {
c.systemContext.VariantChoice = options.Variant
}
c.systemContext.OSChoice, c.systemContext.ArchitectureChoice, c.systemContext.VariantChoice = NormalizePlatform(options.OS, options.Architecture, options.Variant)

if options.SignaturePolicyPath != "" {
c.systemContext.SignaturePolicyPath = options.SignaturePolicyPath
Expand Down
25 changes: 25 additions & 0 deletions libimage/normalize.go
Original file line number Diff line number Diff line change
@@ -1,13 +1,38 @@
package libimage

import (
"runtime"
"strings"

"github.com/containerd/containerd/platforms"
"github.com/containers/image/v5/docker/reference"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
)

// NormalizePlatform normalizes (according to the OCI spec) the specified os,
// arch and variant, all of which can be empty.
func NormalizePlatform(os, arch, variant string) (_, _, _ string) {
if os == "" {
os = runtime.GOOS
}
if arch == "" {
arch = runtime.GOARCH
}
rawPlatform := os + "/" + arch
if variant != "" {
rawPlatform += "/" + variant
}

normalizedPlatform, err := platforms.Parse(rawPlatform)
if err != nil {
logrus.Debugf("Error normalizing platform: %v", err)
return os, arch, variant
}
logrus.Debugf("Normalized platform %s to %s", rawPlatform, normalizedPlatform)
return normalizedPlatform.OS, normalizedPlatform.Architecture, normalizedPlatform.Variant
}

// NormalizeName normalizes the provided name according to the conventions by
// Podman and Buildah. If tag and digest are missing, the "latest" tag will be
// used. If it's a short name, it will be prefixed with "localhost/".
Expand Down
52 changes: 52 additions & 0 deletions libimage/normalize_test.go
Original file line number Diff line number Diff line change
@@ -1,12 +1,64 @@
package libimage

import (
"runtime"
"testing"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

func TestNormalizePlatform(t *testing.T) {
type platform struct {
os, arch, variant string
}
for _, test := range []struct {
input, expected platform
}{
{
platform{"", "", ""},
platform{runtime.GOOS, runtime.GOARCH, ""},
},
{
platform{"foo", "", "garbage"},
platform{"foo", runtime.GOARCH, "garbage"},
},
{
platform{"&", "invalid", "os"},
platform{"&", "invalid", "os"},
},
{
platform{"linux", "", ""},
platform{"linux", runtime.GOARCH, ""},
},
{
platform{"LINUX", "", ""},
platform{"linux", runtime.GOARCH, ""},
},
{
platform{"macos", "x86_64", ""},
platform{"darwin", "amd64", ""},
},
{
platform{"linux", "amd64", ""},
platform{"linux", "amd64", ""},
},
{
platform{"linux", "arm64", "v8"},
platform{"linux", "arm64", "v8"},
},
{
platform{"linux", "aarch64", ""},
platform{"linux", "arm64", ""},
},
} {
os, arch, variant := NormalizePlatform(test.input.os, test.input.arch, test.input.variant)
assert.Equal(t, test.expected.os, os, test.input)
assert.Equal(t, test.expected.arch, arch, test.input)
assert.Equal(t, test.expected.variant, variant, test.input)
}
}

func TestNormalizeName(t *testing.T) {
const digestSuffix = "@sha256:0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"

Expand Down
7 changes: 6 additions & 1 deletion libimage/pull_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,12 @@ func TestPullPlatforms(t *testing.T) {

image, _, err = runtime.LookupImage(withTag, &LookupImageOptions{Architecture: "arm"})
require.NoError(t, err, "lookup busybox - by arm")
require.NotNil(t, image, "lookup busybox - by local arch")
require.NotNil(t, image, "lookup busybox - by arm")

pullOptions.Architecture = "aarch64"
pulledImages, err = runtime.Pull(ctx, withTag, config.PullPolicyAlways, pullOptions)
require.NoError(t, err, "pull busybox - aarch64")
require.Len(t, pulledImages, 1)
}

func TestPullPolicy(t *testing.T) {
Expand Down
11 changes: 8 additions & 3 deletions libimage/runtime.go
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,8 @@ func (r *Runtime) LookupImage(name string, options *LookupImageOptions) (*Image,
if options.Variant == "" {
options.Variant = r.systemContext.VariantChoice
}
// Normalize platform to be OCI compatible (e.g., "aarch64" -> "arm64").
options.OS, options.Architecture, options.Variant = NormalizePlatform(options.OS, options.Architecture, options.Variant)

// First, check if we have an exact match in the storage. Maybe an ID
// or a fully-qualified image name.
Expand Down Expand Up @@ -489,13 +491,16 @@ func (r *Runtime) imageReferenceMatchesContext(ref types.ImageReference, options
}

if options.Architecture != "" && options.Architecture != data.Architecture {
return false, err
logrus.Debugf("architecture %q does not match architecture %q of image %s", options.Architecture, data.Architecture, ref)
return false, nil
}
if options.OS != "" && options.OS != data.Os {
return false, err
logrus.Debugf("OS %q does not match OS %q of image %s", options.OS, data.Os, ref)
return false, nil
}
if options.Variant != "" && options.Variant != data.Variant {
return false, err
logrus.Debugf("variant %q does not match variant %q of image %s", options.Variant, data.Variant, ref)
return false, nil
}

return true, nil
Expand Down

0 comments on commit 393646b

Please sign in to comment.