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

Change platform selection logic #189

Merged
merged 8 commits into from
Jun 9, 2023
Merged
Show file tree
Hide file tree
Changes from 5 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
45 changes: 19 additions & 26 deletions client.go
Original file line number Diff line number Diff line change
Expand Up @@ -102,13 +102,13 @@ func GetImageFromSource(ctx context.Context, imgStr string, source image.Source,
func selectImageProvider(imgStr string, source image.Source, cfg config) (image.Provider, error) {
var provider image.Provider
tempDirGenerator := rootTempDirGenerator.NewGenerator()

if err := setPlatform(source, &cfg, runtime.GOARCH); err != nil {
return nil, err
}
platformSelectionUnsupported := fmt.Errorf("specified platform=%q however image source=%q does not support selecting platform", cfg.Platform.String(), source.String())

switch source {
case image.DockerTarballSource:
if cfg.Platform != nil {
spiffcs marked this conversation as resolved.
Show resolved Hide resolved
return nil, platformSelectionUnsupported
}
// note: the imgStr is the path on disk to the tar file
provider = docker.NewProviderFromTarball(imgStr, tempDirGenerator)
case image.DockerDaemonSource:
Expand All @@ -130,43 +130,36 @@ func selectImageProvider(imgStr string, source image.Source, cfg config) (image.
return nil, err
}
case image.OciDirectorySource:
if cfg.Platform != nil {
return nil, platformSelectionUnsupported
}
provider = oci.NewProviderFromPath(imgStr, tempDirGenerator)
case image.OciTarballSource:
if cfg.Platform != nil {
return nil, platformSelectionUnsupported
}
provider = oci.NewProviderFromTarball(imgStr, tempDirGenerator)
case image.OciRegistrySource:
defaultPlatformIfNil(&cfg)
provider = oci.NewProviderFromRegistry(imgStr, tempDirGenerator, cfg.Registry, cfg.Platform)
case image.SingularitySource:
if cfg.Platform != nil {
return nil, platformSelectionUnsupported
}
provider = sif.NewProviderFromPath(imgStr, tempDirGenerator)
default:
return nil, fmt.Errorf("unable to determine image source")
}
return provider, nil
}

func setPlatform(source image.Source, cfg *config, defaultArch string) error {
// we should override the platform based on the host architecture if the user did not specify a platform
// see https://github.com/anchore/stereoscope/issues/149 for more details
defaultPlatform, err := image.NewPlatform(defaultArch)
if err != nil {
log.WithFields("error", err).Warnf("unable to set default platform to %q", runtime.GOARCH)
defaultPlatform = nil
}

switch source {
case image.DockerTarballSource, image.OciDirectorySource, image.OciTarballSource, image.SingularitySource:
if cfg.Platform != nil {
return fmt.Errorf("specified platform=%q however image source=%q does not support selecting platform", cfg.Platform.String(), source.String())
}

case image.DockerDaemonSource, image.PodmanDaemonSource, image.OciRegistrySource:
if cfg.Platform == nil {
cfg.Platform = defaultPlatform
func defaultPlatformIfNil(cfg *config) {
spiffcs marked this conversation as resolved.
Show resolved Hide resolved
if cfg.Platform == nil {
p, err := image.NewPlatform(fmt.Sprintf("linux/%s", runtime.GOARCH))
if err == nil {
cfg.Platform = p
}

default:
return fmt.Errorf("unable to determine image source to select platform")
}
return nil
}

// GetImage parses the user provided image string and provides an image object;
Expand Down
193 changes: 0 additions & 193 deletions client_test.go

This file was deleted.

62 changes: 60 additions & 2 deletions test/integration/platform_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@ package integration

import (
"context"
"encoding/json"
"fmt"
"runtime"
"strings"
"testing"

Expand All @@ -28,6 +30,12 @@ func TestPlatformSelection(t *testing.T) {
os: "linux",
expectedDigest: "sha256:1ee006886991ad4689838d3a288e0dd3fd29b70e276622f16b67a8922831a853", // direct from repo manifest
},
{
spiffcs marked this conversation as resolved.
Show resolved Hide resolved
source: image.OciRegistrySource,
architecture: "s390x",
os: "linux",
expectedDigest: "sha256:91c15b1ba6f408a648be60f8c047ef79058f26fa640025f374281f31c8704387", // from generated manifest
},
{
source: image.OciRegistrySource,
architecture: "amd64",
Expand All @@ -46,6 +54,12 @@ func TestPlatformSelection(t *testing.T) {
os: "linux",
expectedDigest: "sha256:79d3cb76a5a8ba402af164ace87bd0f3e0759979f94caf7247745126359711da", // from generated manifest
},
{
source: image.DockerDaemonSource,
architecture: "s390x",
os: "linux",
expectedDigest: "sha256:91c15b1ba6f408a648be60f8c047ef79058f26fa640025f374281f31c8704387", // from generated manifest
},
{
source: image.PodmanDaemonSource,
architecture: "arm64",
Expand All @@ -71,8 +85,7 @@ func TestPlatformSelection(t *testing.T) {
tt.expectedErr(t, err)
require.NotNil(t, img)

assert.Equal(t, tt.os, img.Metadata.OS)
assert.Equal(t, tt.architecture, img.Metadata.Architecture)
assertArchAndOs(t, img, tt.os, tt.architecture)
found := false
if img.Metadata.ManifestDigest == tt.expectedDigest {
found = true
Expand All @@ -89,3 +102,48 @@ func TestPlatformSelection(t *testing.T) {
})
}
}

func TestDigestThatNarrowsToOnePlatform(t *testing.T) {
spiffcs marked this conversation as resolved.
Show resolved Hide resolved
// This digest is busybox:1.31 on linux/s390x
// Test assumes that the host running these tests _isn't_ linux/s390x, but the behavior
// should be the same regardless.
imageStrWithDigest := "busybox:1.31@sha256:91c15b1ba6f408a648be60f8c047ef79058f26fa640025f374281f31c8704387"
tests := []struct {
name string
source image.Source
}{
{
name: "docker",
source: image.DockerDaemonSource,
},
{
name: "registry",
source: image.OciRegistrySource,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
img, err := stereoscope.GetImageFromSource(context.TODO(), imageStrWithDigest, tt.source)
assert.NoError(t, err)
assertArchAndOs(t, img, "linux", "s390x")
})
}
}

func TestDefaultPlatformWithOciRegistry(t *testing.T) {
img, err := stereoscope.GetImageFromSource(context.TODO(), "busybox:1.31", image.OciRegistrySource)
require.NoError(t, err)
assertArchAndOs(t, img, "linux", runtime.GOARCH)
}

func assertArchAndOs(t *testing.T, img *image.Image, os string, architecture string) {
type platform struct {
Architecture string `json:"architecture"`
Os string `json:"os"`
}
var got platform
err := json.Unmarshal(img.Metadata.RawConfig, &got)
require.NoError(t, err)
assert.Equal(t, os, got.Os)
assert.Equal(t, architecture, got.Architecture)
}