diff --git a/libimage/copier.go b/libimage/copier.go index fd10dd72d..c6b23d12e 100644 --- a/libimage/copier.go +++ b/libimage/copier.go @@ -11,6 +11,7 @@ import ( "time" "github.com/containers/common/libimage/manifests" + "github.com/containers/common/libimage/platform" "github.com/containers/common/pkg/config" "github.com/containers/common/pkg/retry" "github.com/containers/image/v5/copy" @@ -239,7 +240,7 @@ func (r *Runtime) newCopier(options *CopyOptions) (*copier, error) { c.systemContext.DockerArchiveAdditionalTags = options.dockerArchiveAdditionalTags - c.systemContext.OSChoice, c.systemContext.ArchitectureChoice, c.systemContext.VariantChoice = NormalizePlatform(options.OS, options.Architecture, options.Variant) + c.systemContext.OSChoice, c.systemContext.ArchitectureChoice, c.systemContext.VariantChoice = platform.NormalizePlatform(options.OS, options.Architecture, options.Variant) if options.SignaturePolicyPath != "" { c.systemContext.SignaturePolicyPath = options.SignaturePolicyPath diff --git a/libimage/image.go b/libimage/image.go index e4198a792..277170940 100644 --- a/libimage/image.go +++ b/libimage/image.go @@ -9,6 +9,8 @@ import ( "strings" "time" + "github.com/containerd/containerd/platforms" + "github.com/containers/common/libimage/platform" "github.com/containers/image/v5/docker/reference" "github.com/containers/image/v5/manifest" storageTransport "github.com/containers/image/v5/storage" @@ -1021,3 +1023,35 @@ func getImageID(ctx context.Context, src types.ImageReference, sys *types.System } return "@" + imageDigest.Encoded(), nil } + +// Checks whether the image matches the specified platform. +// Returns +// - 1) a matching error that can be used for logging (or returning) what does not match +// - 2) a bool indicating whether architecture, os or variant were set (some callers need that to decide whether they need to throw an error) +// - 3) a fatal error that occurred prior to check for matches (e.g., storage errors etc.) +func (i *Image) matchesPlatform(ctx context.Context, os, arch, variant string) (error, bool, error) { + if err := i.isCorrupted(""); err != nil { + return err, false, nil + } + inspectInfo, err := i.inspectInfo(ctx) + if err != nil { + return nil, false, fmt.Errorf("inspecting image: %w", err) + } + + customPlatform := len(os)+len(arch)+len(variant) != 0 + + expected, err := platforms.Parse(platform.ToPlatformString(os, arch, variant)) + if err != nil { + return nil, false, fmt.Errorf("parsing host platform: %v", err) + } + fromImage, err := platforms.Parse(platform.ToPlatformString(inspectInfo.Os, inspectInfo.Architecture, inspectInfo.Variant)) + if err != nil { + return nil, false, fmt.Errorf("parsing image platform: %v", err) + } + + if platforms.NewMatcher(expected).Match(fromImage) { + return nil, customPlatform, nil + } + + return fmt.Errorf("image platform (%s) does not match the expected platform (%s)", platforms.Format(fromImage), platforms.Format(expected)), customPlatform, nil +} diff --git a/libimage/normalize_test.go b/libimage/normalize_test.go index 9b9dfb464..a3a6e12dc 100644 --- a/libimage/normalize_test.go +++ b/libimage/normalize_test.go @@ -3,6 +3,7 @@ package libimage import ( "testing" + lplatform "github.com/containers/common/libimage/platform" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -83,7 +84,7 @@ func TestNormalizePlatform(t *testing.T) { platform{"linux", "arm", "v6"}, }, } { - os, arch, variant := NormalizePlatform(test.input.os, test.input.arch, test.input.variant) + os, arch, variant := lplatform.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) diff --git a/libimage/platform.go b/libimage/platform/platform.go similarity index 53% rename from libimage/platform.go rename to libimage/platform/platform.go index 06c15ee64..19bcc0a38 100644 --- a/libimage/platform.go +++ b/libimage/platform/platform.go @@ -1,7 +1,6 @@ -package libimage +package platform import ( - "context" "fmt" "runtime" @@ -32,7 +31,7 @@ func NormalizePlatform(rawOS, rawArch, rawVariant string) (os, arch, variant str if normalizedSpec.Variant == "" && rawVariant != "" { normalizedSpec.Variant = rawVariant } - rawPlatform := toPlatformString(normalizedSpec.OS, normalizedSpec.Architecture, normalizedSpec.Variant) + rawPlatform := ToPlatformString(normalizedSpec.OS, normalizedSpec.Architecture, normalizedSpec.Variant) normalizedPlatform, err := platforms.Parse(rawPlatform) if err != nil { logrus.Debugf("Error normalizing platform: %v", err) @@ -54,7 +53,7 @@ func NormalizePlatform(rawOS, rawArch, rawVariant string) (os, arch, variant str return os, arch, variant } -func toPlatformString(os, arch, variant string) string { +func ToPlatformString(os, arch, variant string) string { if os == "" { os = runtime.GOOS } @@ -66,35 +65,3 @@ func toPlatformString(os, arch, variant string) string { } return fmt.Sprintf("%s/%s/%s", os, arch, variant) } - -// Checks whether the image matches the specified platform. -// Returns -// - 1) a matching error that can be used for logging (or returning) what does not match -// - 2) a bool indicating whether architecture, os or variant were set (some callers need that to decide whether they need to throw an error) -// - 3) a fatal error that occurred prior to check for matches (e.g., storage errors etc.) -func (i *Image) matchesPlatform(ctx context.Context, os, arch, variant string) (error, bool, error) { - if err := i.isCorrupted(""); err != nil { - return err, false, nil - } - inspectInfo, err := i.inspectInfo(ctx) - if err != nil { - return nil, false, fmt.Errorf("inspecting image: %w", err) - } - - customPlatform := len(os)+len(arch)+len(variant) != 0 - - expected, err := platforms.Parse(toPlatformString(os, arch, variant)) - if err != nil { - return nil, false, fmt.Errorf("parsing host platform: %v", err) - } - fromImage, err := platforms.Parse(toPlatformString(inspectInfo.Os, inspectInfo.Architecture, inspectInfo.Variant)) - if err != nil { - return nil, false, fmt.Errorf("parsing image platform: %v", err) - } - - if platforms.NewMatcher(expected).Match(fromImage) { - return nil, customPlatform, nil - } - - return fmt.Errorf("image platform (%s) does not match the expected platform (%s)", platforms.Format(fromImage), platforms.Format(expected)), customPlatform, nil -} diff --git a/libimage/platform_test.go b/libimage/platform/platform_test.go similarity index 87% rename from libimage/platform_test.go rename to libimage/platform/platform_test.go index 36104107b..fbaea18c5 100644 --- a/libimage/platform_test.go +++ b/libimage/platform/platform_test.go @@ -1,4 +1,4 @@ -package libimage +package platform import ( "fmt" @@ -19,7 +19,7 @@ func TestToPlatformString(t *testing.T) { {"", "", "", fmt.Sprintf("%s/%s", runtime.GOOS, runtime.GOARCH)}, {"", "", "c", fmt.Sprintf("%s/%s/c", runtime.GOOS, runtime.GOARCH)}, } { - platform := toPlatformString(test.os, test.arch, test.variant) + platform := ToPlatformString(test.os, test.arch, test.variant) require.Equal(t, test.expected, platform) } } diff --git a/libimage/runtime.go b/libimage/runtime.go index 6d90272c3..45a24b593 100644 --- a/libimage/runtime.go +++ b/libimage/runtime.go @@ -7,6 +7,7 @@ import ( "os" "strings" + "github.com/containers/common/libimage/platform" "github.com/containers/common/pkg/config" "github.com/containers/image/v5/docker/reference" "github.com/containers/image/v5/pkg/shortnames" @@ -184,7 +185,7 @@ type LookupImageOptions struct { Variant string // Controls the behavior when checking the platform of an image. - PlatformPolicy PlatformPolicy + PlatformPolicy platform.PlatformPolicy // If set, do not look for items/instances in the manifest list that // match the current platform but return the manifest list as is. @@ -283,7 +284,7 @@ func (r *Runtime) LookupImage(name string, options *LookupImageOptions) (*Image, 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) + options.OS, options.Architecture, options.Variant = platform.NormalizePlatform(options.OS, options.Architecture, options.Variant) // Second, try out the candidates as resolved by shortnames. This takes // "localhost/" prefixed images into account as well. @@ -435,9 +436,9 @@ func (r *Runtime) lookupImageInLocalStorage(name, candidate string, namedCandida return nil, nil } switch options.PlatformPolicy { - case PlatformPolicyDefault: + case platform.PlatformPolicyDefault: logrus.Debugf("%v", matchError) - case PlatformPolicyWarn: + case platform.PlatformPolicyWarn: logrus.Warnf("%v", matchError) } }