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 23, 2021
1 parent 06ea6e4 commit b2d805e
Show file tree
Hide file tree
Showing 5 changed files with 108 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
38 changes: 38 additions & 0 deletions libimage/normalize.go
Original file line number Diff line number Diff line change
@@ -1,13 +1,51 @@
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. If left empty, the individual item will not be normalized.
func NormalizePlatform(rawOS, rawArch, rawVariant string) (os, arch, variant string) {
os, arch, variant = rawOS, rawArch, rawVariant
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 rawOS, rawArch, rawVariant
}
logrus.Debugf("Normalized platform %s to %s", rawPlatform, normalizedPlatform)
os = rawOS
if rawOS != "" {
os = normalizedPlatform.OS
}
arch = rawArch
if rawArch != "" {
arch = normalizedPlatform.Architecture
}
variant = rawVariant
if rawVariant != "" {
variant = normalizedPlatform.Variant
}
return os, arch, 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
55 changes: 55 additions & 0 deletions libimage/normalize_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,61 @@ import (
"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{"", "", ""},
},
{
platform{"foo", "", "garbage"},
platform{"foo", "", "garbage"},
},
{
platform{"&", "invalid", "os"},
platform{"&", "invalid", "os"},
},
{
platform{"linux", "", ""},
platform{"linux", "", ""},
},
{
platform{"LINUX", "", ""},
platform{"linux", "", ""},
},
{
platform{"", "aarch64", ""},
platform{"", "arm64", ""},
},
{
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 b2d805e

Please sign in to comment.