diff --git a/internal/image/fixtures/schemazstdselection.json b/internal/image/fixtures/schemazstdselection.json new file mode 100644 index 0000000000..e91848047d --- /dev/null +++ b/internal/image/fixtures/schemazstdselection.json @@ -0,0 +1,27 @@ +{ + "schemaVersion": 2, + "mediaType": "application/vnd.oci.image.index.v1+json", + "manifests": [ + { + "mediaType": "application/vnd.oci.image.manifest.v1+json", + "digest": "sha256:ac9bbf71dacfad2bfbe48f7d46ca279b1b3b351a6c814db760030015167405b8", + "size": 772, + "annotations": { + "io.github.containers.compression.zstd": "true" + }, + "platform": { + "architecture": "amd64", + "os": "linux" + } + }, + { + "mediaType": "application/vnd.oci.image.manifest.v1+json", + "digest": "sha256:88cb27f194cce3b06994b4af2dd8a5642f4f294b71bda5b74a3fbb74a9522510", + "size": 758, + "platform": { + "architecture": "amd64", + "os": "linux" + } + } + ] +} diff --git a/manifest/list_test.go b/manifest/list_test.go index 540836d2a3..f0a0fd9c51 100644 --- a/manifest/list_test.go +++ b/manifest/list_test.go @@ -87,6 +87,15 @@ func TestChooseInstance(t *testing.T) { "unmatched", }, }, + { + listFile: "schemazstdselection.json", + matchedInstances: []expectedMatch{ + {"amd64", "", "sha256:ac9bbf71dacfad2bfbe48f7d46ca279b1b3b351a6c814db760030015167405b8"}, + }, + unmatchedInstances: []string{ + "unmatched", + }, + }, { // Focus on ARM variant field testing listFile: "schema2list-variants.json", matchedInstances: []expectedMatch{ diff --git a/manifest/oci_index.go b/manifest/oci_index.go index 726207b9d4..ffe65bd3b9 100644 --- a/manifest/oci_index.go +++ b/manifest/oci_index.go @@ -12,6 +12,13 @@ import ( imgspecv1 "github.com/opencontainers/image-spec/specs-go/v1" ) +const ( + // Following annotation is used while performing instance selection. + // If more than one image exists for the same platform then the image + // compressed with `zstd` will be given priority. + ZSTDInstanceAnnotation = "io.github.containers.compression.zstd" +) + // OCI1Index is just an alias for the OCI index type, but one which we can // provide methods for. type OCI1Index struct { @@ -76,6 +83,8 @@ func (index *OCI1Index) ChooseInstance(ctx *types.SystemContext) (digest.Digest, if err != nil { return "", fmt.Errorf("getting platform information %#v: %w", ctx, err) } + var matchedDigest digest.Digest + matchedDigest = "" for _, wantedPlatform := range wantedPlatforms { for _, d := range index.Manifests { if d.Platform == nil { @@ -89,11 +98,20 @@ func (index *OCI1Index) ChooseInstance(ctx *types.SystemContext) (digest.Digest, Variant: d.Platform.Variant, } if platform.MatchesPlatform(imagePlatform, wantedPlatform) { - return d.Digest, nil + matchedDigest = d.Digest + // Always give priority to zstd if more than one image + // exists for the same platform. + if _, ok := d.Annotations[ZSTDInstanceAnnotation]; ok { + return matchedDigest, nil + } } } } + if matchedDigest != "" { + return matchedDigest, nil + } + for _, d := range index.Manifests { if d.Platform == nil { return d.Digest, nil