Skip to content

Commit

Permalink
raise up platform mismatch error
Browse files Browse the repository at this point in the history
Signed-off-by: Alex Goodman <[email protected]>
  • Loading branch information
wagoodman committed Jan 8, 2025
1 parent 9c7e279 commit c16a904
Show file tree
Hide file tree
Showing 8 changed files with 53 additions and 27 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ bin/
/snapshot
/.tool
/.task
.mise.toml

# changelog generation
CHANGELOG.md
Expand Down
15 changes: 8 additions & 7 deletions pkg/image/containerd/daemon_provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -354,27 +354,28 @@ func validatePlatform(expected *image.Platform, given *platforms.Platform) error
}

if given == nil {
return newErrFetchingImage(fmt.Errorf("image has no platform information (might be a manifest list)"))
return newErrPlatformMismatch(expected, fmt.Errorf("image has no platform information (might be a manifest list)"))
}

if given.OS != expected.OS {
return newErrFetchingImage(fmt.Errorf("image has unexpected OS %q, which differs from the user specified PS %q", given.OS, expected.OS))
return newErrPlatformMismatch(expected, fmt.Errorf("image has unexpected OS %q, which differs from the user specified PS %q", given.OS, expected.OS))
}

if given.Architecture != expected.Architecture {
return newErrFetchingImage(fmt.Errorf("image has unexpected architecture %q, which differs from the user specified architecture %q", given.Architecture, expected.Architecture))
return newErrPlatformMismatch(expected, fmt.Errorf("image has unexpected architecture %q, which differs from the user specified architecture %q", given.Architecture, expected.Architecture))
}

if given.Variant != expected.Variant {
return newErrFetchingImage(fmt.Errorf("image has unexpected architecture %q, which differs from the user specified architecture %q", given.Variant, expected.Variant))
return newErrPlatformMismatch(expected, fmt.Errorf("image has unexpected architecture %q, which differs from the user specified architecture %q", given.Variant, expected.Variant))
}

return nil
}

func newErrFetchingImage(err error) *image.ErrFetchingImage {
return &image.ErrFetchingImage{
Reason: err.Error(),
func newErrPlatformMismatch(expected *image.Platform, err error) *image.ErrPlatformMismatch {
return &image.ErrPlatformMismatch{
ExpectedPlatform: expected.String(),
Err: err,
}
}

Expand Down
2 changes: 1 addition & 1 deletion pkg/image/containerd/daemon_provider_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ func Test_exportPlatformComparer(t *testing.T) {

func TestValidatePlatform(t *testing.T) {
isFetchError := func(t require.TestingT, err error, args ...interface{}) {
var pErr *image.ErrFetchingImage
var pErr *image.ErrPlatformMismatch
require.ErrorAs(t, err, &pErr)
}

Expand Down
7 changes: 6 additions & 1 deletion pkg/image/docker/daemon_provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,12 @@ type emitter interface {

func handlePullEvent(status emitter, event *pullEvent) error {
if event.Error != "" {
return &image.ErrFetchingImage{Reason: event.Error}
if strings.Contains(event.Error, "does not match the specified platform") {
return &image.ErrPlatformMismatch{
Err: errors.New(event.Error),
}
}
return errors.New(event.Error)
}

// check for the last two events indicating the pull is complete
Expand Down
15 changes: 14 additions & 1 deletion pkg/image/docker/daemon_provider_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,20 @@ func TestHandlePullEventWithMockEmitter(t *testing.T) {
},
expectOnEvent: false,
assertFunc: func(t require.TestingT, err error, args ...interface{}) {
var pErr *image.ErrFetchingImage
require.Error(t, err)
var pErr *image.ErrPlatformMismatch
require.NotErrorAs(t, err, &pErr)
},
},
{
name: "platform error in event",
event: &pullEvent{
Error: "image with reference anchore/test_images:golang was found but its platform (linux/amd64) does not match the specified platform (linux/arm64)",
},
expectOnEvent: false,
assertFunc: func(t require.TestingT, err error, args ...interface{}) {
require.Error(t, err)
var pErr *image.ErrPlatformMismatch
require.ErrorAs(t, err, &pErr)
},
},
Expand Down
15 changes: 8 additions & 7 deletions pkg/image/oci/registry_provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -130,25 +130,26 @@ func validatePlatform(platform *image.Platform, givenOs, givenArch string) error
return nil
}
if givenArch == "" || givenOs == "" {
return newErrFetchingImage(fmt.Errorf("missing architecture or OS from image config when user specified platform=%q", platform.String()))
return newErrPlatformMismatch(platform, fmt.Errorf("missing architecture or OS from image config when user specified platform=%q", platform.String()))
}
platformStr := fmt.Sprintf("%s/%s", givenOs, givenArch)
actualPlatform, err := containerregistryV1.ParsePlatform(platformStr)
if err != nil {
return newErrFetchingImage(fmt.Errorf("failed to parse platform from image config: %w", err))
return newErrPlatformMismatch(platform, fmt.Errorf("failed to parse platform from image config: %w", err))
}
if actualPlatform == nil {
return newErrFetchingImage(fmt.Errorf("not platform from image config (from %q)", platformStr))
return newErrPlatformMismatch(platform, fmt.Errorf("not platform from image config (from %q)", platformStr))
}
if !matchesPlatform(*actualPlatform, *toContainerRegistryPlatform(platform)) {
return newErrFetchingImage(fmt.Errorf("image platform=%q does not match user specified platform=%q", actualPlatform.String(), platform.String()))
return newErrPlatformMismatch(platform, fmt.Errorf("image platform=%q does not match user specified platform=%q", actualPlatform.String(), platform.String()))
}
return nil
}

func newErrFetchingImage(err error) *image.ErrFetchingImage {
return &image.ErrFetchingImage{
Reason: err.Error(),
func newErrPlatformMismatch(platform *image.Platform, err error) *image.ErrPlatformMismatch {
return &image.ErrPlatformMismatch{
ExpectedPlatform: platform.String(),
Err: err,
}
}

Expand Down
2 changes: 1 addition & 1 deletion pkg/image/oci/registry_provider_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ import (

func TestValidatePlatform(t *testing.T) {
isFetchError := func(t require.TestingT, err error, args ...interface{}) {
var pErr *image.ErrFetchingImage
var pErr *image.ErrPlatformMismatch
require.ErrorAs(t, err, &pErr)
}
tests := []struct {
Expand Down
23 changes: 14 additions & 9 deletions pkg/image/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,22 @@ import (
"fmt"
)

// ErrFetchingImage is meant to be used when a provider has positively resolved the image but, while fetching the
// image, an error occurred. The goal is to differentiate between a provider that cannot resolve an image (thus
// if the caller has a set of providers, it can try another provider) and a provider that can resolve an image but
// there is an unresolvable problem (e.g. network error, mismatched architecture, etc... thus the caller should
// not try any further providers).
type ErrFetchingImage struct {
Reason string
// ErrPlatformMismatch is meant to be used when a provider has positively resolved the image but the image OS or
// architecture does not match with what was requested.
type ErrPlatformMismatch struct {
ExpectedPlatform string
Err error
}

func (e *ErrFetchingImage) Error() string {
return fmt.Sprintf("error fetching image: %s", e.Reason)
func (e *ErrPlatformMismatch) Error() string {
if e.ExpectedPlatform == "" {
return fmt.Sprintf("mismatched platform: %v", e.Err)
}
return fmt.Sprintf("mismatched platform (expected %v): %v", e.ExpectedPlatform, e.Err)
}

func (e *ErrPlatformMismatch) Unwrap() error {
return e.Err
}

// Provider is an abstraction for any object that provides image objects (e.g. the docker daemon API, a tar file of
Expand Down

0 comments on commit c16a904

Please sign in to comment.