mirror of https://github.com/containers/image.git
370 lines
21 KiB
Go
370 lines
21 KiB
Go
package manifest
|
||
|
||
import (
|
||
"encoding/json"
|
||
"os"
|
||
"path/filepath"
|
||
"testing"
|
||
"time"
|
||
|
||
"github.com/containers/image/v5/pkg/compression"
|
||
"github.com/containers/image/v5/types"
|
||
"github.com/opencontainers/go-digest"
|
||
"github.com/stretchr/testify/assert"
|
||
"github.com/stretchr/testify/require"
|
||
)
|
||
|
||
// DiffID values corresponding to layers of schema2-to-schema1-by-docker.json
|
||
var schema1FixtureLayerDiffIDs = []digest.Digest{
|
||
"sha256:142a601d97936307e75220c35dde0348971a9584c21e7cb42e1f7004005432ab",
|
||
"sha256:90fcc66ad3be9f1757f954b750deb37032f208428aa12599fcb02182b9065a9c",
|
||
"sha256:5a8624bb7e76d1e6829f9c64c43185e02bc07f97a2189eb048609a8914e72c56",
|
||
"sha256:d349ff6b3afc6a2800054768c82bfbf4289c9aa5da55c1290f802943dcd4d1e9",
|
||
"sha256:8c064bb1f60e84fa8cc6079b6d2e76e0423389fd6aeb7e497dfdae5e05b2b25b",
|
||
}
|
||
|
||
// assertJSONEqualsFixture tests that jsonBytes is structurally equal to fixture,
|
||
// possibly ignoring ignoreFields
|
||
func assertJSONEqualsFixture(t *testing.T, jsonBytes []byte, fixture string, ignoreFields ...string) {
|
||
var contents map[string]any
|
||
err := json.Unmarshal(jsonBytes, &contents)
|
||
require.NoError(t, err)
|
||
|
||
fixtureBytes, err := os.ReadFile(filepath.Join("fixtures", fixture))
|
||
require.NoError(t, err)
|
||
var fixtureContents map[string]any
|
||
|
||
err = json.Unmarshal(fixtureBytes, &fixtureContents)
|
||
require.NoError(t, err)
|
||
for _, f := range ignoreFields {
|
||
delete(contents, f)
|
||
delete(fixtureContents, f)
|
||
}
|
||
assert.Equal(t, fixtureContents, contents)
|
||
}
|
||
|
||
func manifestSchema1FromFixture(t *testing.T, fixture string) *Schema1 {
|
||
manifest, err := os.ReadFile(filepath.Join("fixtures", fixture))
|
||
require.NoError(t, err)
|
||
|
||
m, err := Schema1FromManifest(manifest)
|
||
require.NoError(t, err)
|
||
return m
|
||
}
|
||
|
||
func TestSchema1FromManifest(t *testing.T) {
|
||
validManifest, err := os.ReadFile(filepath.Join("fixtures", "schema2-to-schema1-by-docker.json"))
|
||
require.NoError(t, err)
|
||
|
||
// Invalid manifest version is rejected
|
||
m, err := Schema1FromManifest(validManifest)
|
||
require.NoError(t, err)
|
||
m.SchemaVersion = 2
|
||
manifest, err := m.Serialize()
|
||
require.NoError(t, err)
|
||
_, err = Schema1FromManifest(manifest)
|
||
assert.Error(t, err)
|
||
|
||
parser := func(m []byte) error {
|
||
_, err := Schema1FromManifest(m)
|
||
return err
|
||
}
|
||
// Schema mismatch is rejected
|
||
testManifestFixturesAreRejected(t, parser, []string{
|
||
"v2s2.manifest.json", "v2list.manifest.json",
|
||
"ociv1.manifest.json", "ociv1.image.index.json",
|
||
})
|
||
// Extra fields are rejected
|
||
testValidManifestWithExtraFieldsIsRejected(t, parser, validManifest, []string{"config", "layers", "manifests"})
|
||
}
|
||
|
||
func TestSchema1Clone(t *testing.T) {
|
||
// This fixture should be kept updated to have all known fields set to non-empty values
|
||
m := manifestSchema1FromFixture(t, "v2s1.everything.json")
|
||
clone := Schema1Clone(m)
|
||
assert.Equal(t, m, clone)
|
||
}
|
||
|
||
func TestSchema1Initialize(t *testing.T) {
|
||
// Test this indirectly via Schema1FromComponents; otherwise we would have to break the API and create an instance manually.
|
||
|
||
// FIXME: this should eventually share a fixture with the other parsing tests.
|
||
fsLayers := []Schema1FSLayers{
|
||
{BlobSum: "sha256:e623934bca8d1a74f51014256445937714481e49343a31bda2bc5f534748184d"},
|
||
{BlobSum: "sha256:62e48e39dc5b30b75a97f05bccc66efbae6058b860ee20a5c9a184b9d5e25788"},
|
||
{BlobSum: "sha256:9e92df2aea7dc0baf5f1f8d509678d6a6306de27ad06513f8e218371938c07a6"},
|
||
{BlobSum: "sha256:f576d102e09b9eef0e305aaef705d2d43a11bebc3fd5810a761624bd5e11997e"},
|
||
{BlobSum: "sha256:4aa565ad8b7a87248163ce7dba1dd3894821aac97e846b932ff6b8ef9a8a508a"},
|
||
{BlobSum: "sha256:9cadd93b16ff2a0c51ac967ea2abfadfac50cfa3af8b5bf983d89b8f8647f3e4"},
|
||
}
|
||
history := []Schema1History{
|
||
{V1Compatibility: "{\"architecture\":\"amd64\",\"config\":{\"Hostname\":\"9428cdea83ba\",\"Domainname\":\"\",\"User\":\"nova\",\"AttachStdin\":false,\"AttachStdout\":false,\"AttachStderr\":false,\"Tty\":false,\"OpenStdin\":false,\"StdinOnce\":false,\"Env\":[\"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin\",\"container=oci\",\"KOLLA_BASE_DISTRO=rhel\",\"KOLLA_INSTALL_TYPE=binary\",\"KOLLA_INSTALL_METATYPE=rhos\",\"PS1=$(tput bold)($(printenv KOLLA_SERVICE_NAME))$(tput sgr0)[$(id -un)@$(hostname -s) $(pwd)]$ \"],\"Cmd\":[\"kolla_start\"],\"Healthcheck\":{\"Test\":[\"CMD-SHELL\",\"/openstack/healthcheck\"]},\"ArgsEscaped\":true,\"Image\":\"3bf9afe371220b1eb1c57bec39b5a99ba976c36c92d964a1c014584f95f51e33\",\"Volumes\":null,\"WorkingDir\":\"\",\"Entrypoint\":null,\"OnBuild\":[],\"Labels\":{\"Kolla-SHA\":\"5.0.0-39-g6f1b947b\",\"architecture\":\"x86_64\",\"authoritative-source-url\":\"registry.access.redhat.com\",\"build-date\":\"2018-01-25T00:32:27.807261\",\"com.redhat.build-host\":\"ip-10-29-120-186.ec2.internal\",\"com.redhat.component\":\"openstack-nova-api-docker\",\"description\":\"Red Hat OpenStack Platform 12.0 nova-api\",\"distribution-scope\":\"public\",\"io.k8s.description\":\"Red Hat OpenStack Platform 12.0 nova-api\",\"io.k8s.display-name\":\"Red Hat OpenStack Platform 12.0 nova-api\",\"io.openshift.tags\":\"rhosp osp openstack osp-12.0\",\"kolla_version\":\"stable/pike\",\"name\":\"rhosp12/openstack-nova-api\",\"release\":\"20180124.1\",\"summary\":\"Red Hat OpenStack Platform 12.0 nova-api\",\"tripleo-common_version\":\"7.6.3-23-g4891cfe\",\"url\":\"https://access.redhat.com/containers/#/registry.access.redhat.com/rhosp12/openstack-nova-api/images/12.0-20180124.1\",\"vcs-ref\":\"9b31243b7b448eb2fc3b6e2c96935b948f806e98\",\"vcs-type\":\"git\",\"vendor\":\"Red Hat, Inc.\",\"version\":\"12.0\",\"version-release\":\"12.0-20180124.1\"}},\"container_config\":{\"Hostname\":\"9428cdea83ba\",\"Domainname\":\"\",\"User\":\"nova\",\"AttachStdin\":false,\"AttachStdout\":false,\"AttachStderr\":false,\"Tty\":false,\"OpenStdin\":false,\"StdinOnce\":false,\"Env\":[\"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin\",\"container=oci\",\"KOLLA_BASE_DISTRO=rhel\",\"KOLLA_INSTALL_TYPE=binary\",\"KOLLA_INSTALL_METATYPE=rhos\",\"PS1=$(tput bold)($(printenv KOLLA_SERVICE_NAME))$(tput sgr0)[$(id -un)@$(hostname -s) $(pwd)]$ \"],\"Cmd\":[\"/bin/sh\",\"-c\",\"#(nop) \",\"USER [nova]\"],\"Healthcheck\":{\"Test\":[\"CMD-SHELL\",\"/openstack/healthcheck\"]},\"ArgsEscaped\":true,\"Image\":\"sha256:274ce4dcbeb09fa173a5d50203ae5cec28f456d1b8b59477b47a42bd74d068bf\",\"Volumes\":null,\"WorkingDir\":\"\",\"Entrypoint\":null,\"OnBuild\":[],\"Labels\":{\"Kolla-SHA\":\"5.0.0-39-g6f1b947b\",\"architecture\":\"x86_64\",\"authoritative-source-url\":\"registry.access.redhat.com\",\"build-date\":\"2018-01-25T00:32:27.807261\",\"com.redhat.build-host\":\"ip-10-29-120-186.ec2.internal\",\"com.redhat.component\":\"openstack-nova-api-docker\",\"description\":\"Red Hat OpenStack Platform 12.0 nova-api\",\"distribution-scope\":\"public\",\"io.k8s.description\":\"Red Hat OpenStack Platform 12.0 nova-api\",\"io.k8s.display-name\":\"Red Hat OpenStack Platform 12.0 nova-api\",\"io.openshift.tags\":\"rhosp osp openstack osp-12.0\",\"kolla_version\":\"stable/pike\",\"name\":\"rhosp12/openstack-nova-api\",\"release\":\"20180124.1\",\"summary\":\"Red Hat OpenStack Platform 12.0 nova-api\",\"tripleo-common_version\":\"7.6.3-23-g4891cfe\",\"url\":\"https://access.redhat.com/containers/#/registry.access.redhat.com/rhosp12/openstack-nova-api/images/12.0-20180124.1\",\"vcs-ref\":\"9b31243b7b448eb2fc3b6e2c96935b948f806e98\",\"vcs-type\":\"git\",\"vendor\":\"Red Hat, Inc.\",\"version\":\"12.0\",\"version-release\":\"12.0-20180124.1\"}},\"created\":\"2018-01-25T00:37:48.268558Z\",\"docker_version\":\"1.12.6\",\"id\":\"486cbbaf6c6f7d890f9368c86eda3f4ebe3ae982b75098037eb3c3cc6f0e0cdf\",\"os\":\"linux\",\"parent\":\"20d0c9c79f9fee83c4094993335b9b321112f13eef60ed9ec1599c7593dccf20\"}"},
|
||
{V1Compatibility: "{\"id\":\"20d0c9c79f9fee83c4094993335b9b321112f13eef60ed9ec1599c7593dccf20\",\"parent\":\"47a1014db2116c312736e11adcc236fb77d0ad32457f959cbaec0c3fc9ab1caa\",\"created\":\"2018-01-24T23:08:25.300741Z\",\"container_config\":{\"Cmd\":[\"/bin/sh -c rm -f '/etc/yum.repos.d/rhel-7.4.repo' '/etc/yum.repos.d/rhos-optools-12.0.repo' '/etc/yum.repos.d/rhos-12.0-container-yum-need_images.repo'\"]}}"},
|
||
{V1Compatibility: "{\"id\":\"47a1014db2116c312736e11adcc236fb77d0ad32457f959cbaec0c3fc9ab1caa\",\"parent\":\"cec66cab6c92a5f7b50ef407b80b83840a0d089b9896257609fd01de3a595824\",\"created\":\"2018-01-24T22:00:57.807862Z\",\"container_config\":{\"Cmd\":[\"/bin/sh -c rm -f '/etc/yum.repos.d/rhel-7.4.repo' '/etc/yum.repos.d/rhos-optools-12.0.repo' '/etc/yum.repos.d/rhos-12.0-container-yum-need_images.repo'\"]}}"},
|
||
{V1Compatibility: "{\"id\":\"cec66cab6c92a5f7b50ef407b80b83840a0d089b9896257609fd01de3a595824\",\"parent\":\"0e7730eccb3d014b33147b745d771bc0e38a967fd932133a6f5325a3c84282e2\",\"created\":\"2018-01-24T21:40:32.494686Z\",\"container_config\":{\"Cmd\":[\"/bin/sh -c rm -f '/etc/yum.repos.d/rhel-7.4.repo' '/etc/yum.repos.d/rhos-optools-12.0.repo' '/etc/yum.repos.d/rhos-12.0-container-yum-need_images.repo'\"]}}"},
|
||
{V1Compatibility: "{\"id\":\"0e7730eccb3d014b33147b745d771bc0e38a967fd932133a6f5325a3c84282e2\",\"parent\":\"3e49094c0233214ab73f8e5c204af8a14cfc6f0403384553c17fbac2e9d38345\",\"created\":\"2017-11-21T16:49:37.292899Z\",\"container_config\":{\"Cmd\":[\"/bin/sh -c rm -f '/etc/yum.repos.d/compose-rpms-1.repo'\"]},\"author\":\"Red Hat, Inc.\"}"},
|
||
{V1Compatibility: "{\"id\":\"3e49094c0233214ab73f8e5c204af8a14cfc6f0403384553c17fbac2e9d38345\",\"comment\":\"Imported from -\",\"created\":\"2017-11-21T16:47:27.755341705Z\",\"container_config\":{\"Cmd\":[\"\"]}}"},
|
||
}
|
||
|
||
// Valid input
|
||
m, err := Schema1FromComponents(nil, fsLayers, history, "amd64")
|
||
assert.NoError(t, err)
|
||
assert.Equal(t, []Schema1V1Compatibility{
|
||
{
|
||
ID: "486cbbaf6c6f7d890f9368c86eda3f4ebe3ae982b75098037eb3c3cc6f0e0cdf",
|
||
Parent: "20d0c9c79f9fee83c4094993335b9b321112f13eef60ed9ec1599c7593dccf20",
|
||
Created: time.Date(2018, 1, 25, 0, 37, 48, 268558000, time.UTC),
|
||
ContainerConfig: schema1V1CompatibilityContainerConfig{
|
||
Cmd: []string{"/bin/sh", "-c", "#(nop) ", "USER [nova]"},
|
||
},
|
||
ThrowAway: false,
|
||
},
|
||
{
|
||
ID: "20d0c9c79f9fee83c4094993335b9b321112f13eef60ed9ec1599c7593dccf20",
|
||
Parent: "47a1014db2116c312736e11adcc236fb77d0ad32457f959cbaec0c3fc9ab1caa",
|
||
Created: time.Date(2018, 1, 24, 23, 8, 25, 300741000, time.UTC),
|
||
ContainerConfig: schema1V1CompatibilityContainerConfig{
|
||
Cmd: []string{"/bin/sh -c rm -f '/etc/yum.repos.d/rhel-7.4.repo' '/etc/yum.repos.d/rhos-optools-12.0.repo' '/etc/yum.repos.d/rhos-12.0-container-yum-need_images.repo'"},
|
||
},
|
||
ThrowAway: false,
|
||
},
|
||
{
|
||
ID: "47a1014db2116c312736e11adcc236fb77d0ad32457f959cbaec0c3fc9ab1caa",
|
||
Parent: "cec66cab6c92a5f7b50ef407b80b83840a0d089b9896257609fd01de3a595824",
|
||
Created: time.Date(2018, 1, 24, 22, 0, 57, 807862000, time.UTC),
|
||
ContainerConfig: schema1V1CompatibilityContainerConfig{
|
||
Cmd: []string{"/bin/sh -c rm -f '/etc/yum.repos.d/rhel-7.4.repo' '/etc/yum.repos.d/rhos-optools-12.0.repo' '/etc/yum.repos.d/rhos-12.0-container-yum-need_images.repo'"},
|
||
},
|
||
ThrowAway: false,
|
||
},
|
||
{
|
||
ID: "cec66cab6c92a5f7b50ef407b80b83840a0d089b9896257609fd01de3a595824",
|
||
Parent: "0e7730eccb3d014b33147b745d771bc0e38a967fd932133a6f5325a3c84282e2",
|
||
Created: time.Date(2018, 1, 24, 21, 40, 32, 494686000, time.UTC),
|
||
ContainerConfig: schema1V1CompatibilityContainerConfig{
|
||
Cmd: []string{"/bin/sh -c rm -f '/etc/yum.repos.d/rhel-7.4.repo' '/etc/yum.repos.d/rhos-optools-12.0.repo' '/etc/yum.repos.d/rhos-12.0-container-yum-need_images.repo'"},
|
||
},
|
||
ThrowAway: false,
|
||
},
|
||
{
|
||
ID: "0e7730eccb3d014b33147b745d771bc0e38a967fd932133a6f5325a3c84282e2",
|
||
Parent: "3e49094c0233214ab73f8e5c204af8a14cfc6f0403384553c17fbac2e9d38345",
|
||
Created: time.Date(2017, 11, 21, 16, 49, 37, 292899000, time.UTC),
|
||
ContainerConfig: schema1V1CompatibilityContainerConfig{
|
||
Cmd: []string{"/bin/sh -c rm -f '/etc/yum.repos.d/compose-rpms-1.repo'"},
|
||
},
|
||
Author: "Red Hat, Inc.",
|
||
ThrowAway: false,
|
||
},
|
||
{
|
||
ID: "3e49094c0233214ab73f8e5c204af8a14cfc6f0403384553c17fbac2e9d38345",
|
||
Comment: "Imported from -",
|
||
Created: time.Date(2017, 11, 21, 16, 47, 27, 755341705, time.UTC),
|
||
ContainerConfig: schema1V1CompatibilityContainerConfig{
|
||
Cmd: []string{""},
|
||
},
|
||
ThrowAway: false,
|
||
},
|
||
}, m.ExtractedV1Compatibility)
|
||
|
||
// Layer and history length mismatch
|
||
_, err = Schema1FromComponents(nil, fsLayers, history[1:], "amd64")
|
||
assert.Error(t, err)
|
||
|
||
// No layers/history
|
||
_, err = Schema1FromComponents(nil, []Schema1FSLayers{}, []Schema1History{}, "amd64")
|
||
assert.Error(t, err)
|
||
|
||
// Invalid history JSON
|
||
_, err = Schema1FromComponents(nil,
|
||
[]Schema1FSLayers{{BlobSum: "sha256:e623934bca8d1a74f51014256445937714481e49343a31bda2bc5f534748184d"}},
|
||
[]Schema1History{{V1Compatibility: "-"}},
|
||
"amd64")
|
||
assert.Error(t, err)
|
||
}
|
||
|
||
func TestSchema1LayerInfos(t *testing.T) {
|
||
// We use this instead of original schema1 manifests, because those, surprisingly,
|
||
// seem not to set the "throwaway" flag.
|
||
m := manifestSchema1FromFixture(t, "schema2-to-schema1-by-docker.json") // FIXME: Test also Schema1FromComponents
|
||
assert.Equal(t, []LayerInfo{
|
||
{BlobInfo: types.BlobInfo{Digest: "sha256:6a5a5368e0c2d3e5909184fa28ddfd56072e7ff3ee9a945876f7eee5896ef5bb", Size: -1}, EmptyLayer: false},
|
||
{BlobInfo: types.BlobInfo{Digest: "sha256:a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4", Size: -1}, EmptyLayer: true},
|
||
{BlobInfo: types.BlobInfo{Digest: "sha256:a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4", Size: -1}, EmptyLayer: true},
|
||
{BlobInfo: types.BlobInfo{Digest: "sha256:a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4", Size: -1}, EmptyLayer: true},
|
||
{BlobInfo: types.BlobInfo{Digest: "sha256:1bbf5d58d24c47512e234a5623474acf65ae00d4d1414272a893204f44cc680c", Size: -1}, EmptyLayer: false},
|
||
{BlobInfo: types.BlobInfo{Digest: "sha256:a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4", Size: -1}, EmptyLayer: true},
|
||
{BlobInfo: types.BlobInfo{Digest: "sha256:8f5dc8a4b12c307ac84de90cdd9a7f3915d1be04c9388868ca118831099c67a9", Size: -1}, EmptyLayer: false},
|
||
{BlobInfo: types.BlobInfo{Digest: "sha256:a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4", Size: -1}, EmptyLayer: true},
|
||
{BlobInfo: types.BlobInfo{Digest: "sha256:a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4", Size: -1}, EmptyLayer: true},
|
||
{BlobInfo: types.BlobInfo{Digest: "sha256:a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4", Size: -1}, EmptyLayer: true},
|
||
{BlobInfo: types.BlobInfo{Digest: "sha256:a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4", Size: -1}, EmptyLayer: true},
|
||
{BlobInfo: types.BlobInfo{Digest: "sha256:bbd6b22eb11afce63cc76f6bc41042d99f10d6024c96b655dafba930b8d25909", Size: -1}, EmptyLayer: false},
|
||
{BlobInfo: types.BlobInfo{Digest: "sha256:960e52ecf8200cbd84e70eb2ad8678f4367e50d14357021872c10fa3fc5935fa", Size: -1}, EmptyLayer: false},
|
||
{BlobInfo: types.BlobInfo{Digest: "sha256:a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4", Size: -1}, EmptyLayer: true},
|
||
{BlobInfo: types.BlobInfo{Digest: "sha256:a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4", Size: -1}, EmptyLayer: true},
|
||
}, m.LayerInfos())
|
||
}
|
||
|
||
func TestSchema1UpdateLayerInfos(t *testing.T) {
|
||
for _, c := range []struct {
|
||
name string
|
||
sourceFixture string
|
||
updates []types.BlobInfo
|
||
expectedFixture string // or "" to indicate an expected failure
|
||
}{
|
||
{
|
||
name: "gzip → uncompressed",
|
||
sourceFixture: "v2s1.manifest.json",
|
||
updates: []types.BlobInfo{
|
||
{
|
||
Digest: "sha256:5f70bf18a086007016e948b04aed3b82103a36bea41755b6cddfaf10ace3c6ef",
|
||
Size: 32654,
|
||
CompressionOperation: types.Decompress,
|
||
},
|
||
{
|
||
Digest: "sha256:5f70bf18a086007016e948b04aed3b82103a36bea41755b6cddfaf10ace3c6ef",
|
||
Size: 16724,
|
||
CompressionOperation: types.Decompress,
|
||
},
|
||
{
|
||
Digest: "sha256:5f70bf18a086007016e948b04aed3b82103a36bea41755b6cddfaf10ace3c6ef",
|
||
Size: 73109,
|
||
CompressionOperation: types.Decompress,
|
||
},
|
||
},
|
||
expectedFixture: "v2s1.manifest.json", // MIME type is not stored, and we didn’t change the digests in this test, so we should not see any changes.
|
||
},
|
||
{
|
||
name: "uncompressed → gzip",
|
||
sourceFixture: "v2s1.manifest.json",
|
||
updates: []types.BlobInfo{
|
||
{
|
||
Digest: "sha256:5f70bf18a086007016e948b04aed3b82103a36bea41755b6cddfaf10ace3c6ef",
|
||
Size: 32654,
|
||
CompressionOperation: types.Compress,
|
||
CompressionAlgorithm: &compression.Gzip,
|
||
},
|
||
{
|
||
Digest: "sha256:5f70bf18a086007016e948b04aed3b82103a36bea41755b6cddfaf10ace3c6ef",
|
||
Size: 16724,
|
||
CompressionOperation: types.Compress,
|
||
CompressionAlgorithm: &compression.Gzip,
|
||
},
|
||
{
|
||
Digest: "sha256:5f70bf18a086007016e948b04aed3b82103a36bea41755b6cddfaf10ace3c6ef",
|
||
Size: 73109,
|
||
CompressionOperation: types.Compress,
|
||
CompressionAlgorithm: &compression.Gzip,
|
||
},
|
||
},
|
||
expectedFixture: "v2s1.manifest.json", // MIME type is not stored, and we didn’t change the digests in this test, so we should not see any changes.
|
||
},
|
||
{
|
||
name: "gzip → zstd",
|
||
sourceFixture: "v2s1.manifest.json",
|
||
updates: []types.BlobInfo{
|
||
{
|
||
Digest: "sha256:e692418e4cbaf90ca69d05a66403747baa33ee08806650b51fab815ad7fc331f",
|
||
Size: 32654,
|
||
CompressionOperation: types.Compress,
|
||
CompressionAlgorithm: &compression.Zstd,
|
||
},
|
||
{
|
||
Digest: "sha256:3c3a4604a545cdc127456d94e421cd355bca5b528f4a9c1905b15da2eb4a4c6b",
|
||
Size: 16724,
|
||
CompressionOperation: types.Compress,
|
||
CompressionAlgorithm: &compression.Zstd,
|
||
},
|
||
{
|
||
Digest: "sha256:ec4b8955958665577945c89419d1af06b5f7636b4ac3da7f12184802ad867736",
|
||
Size: 73109,
|
||
CompressionOperation: types.Compress,
|
||
CompressionAlgorithm: &compression.Zstd,
|
||
},
|
||
},
|
||
expectedFixture: "", // zstd is not supported for docker images
|
||
},
|
||
{
|
||
name: "uncompressed → gzip encrypted",
|
||
sourceFixture: "v2s1.manifest.json",
|
||
updates: []types.BlobInfo{
|
||
{
|
||
Digest: "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
|
||
Size: 32654,
|
||
Annotations: map[string]string{"org.opencontainers.image.enc.…": "layer1"},
|
||
CompressionOperation: types.Compress,
|
||
CompressionAlgorithm: &compression.Gzip,
|
||
CryptoOperation: types.Encrypt,
|
||
},
|
||
{
|
||
Digest: "sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
|
||
Size: 16724,
|
||
Annotations: map[string]string{"org.opencontainers.image.enc.…": "layer2"},
|
||
CompressionOperation: types.Compress,
|
||
CompressionAlgorithm: &compression.Gzip,
|
||
CryptoOperation: types.Encrypt,
|
||
},
|
||
{
|
||
Digest: "sha256:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc",
|
||
Size: 73109,
|
||
Annotations: map[string]string{"org.opencontainers.image.enc.…": "layer2"},
|
||
CompressionOperation: types.Compress,
|
||
CompressionAlgorithm: &compression.Gzip,
|
||
CryptoOperation: types.Encrypt,
|
||
},
|
||
},
|
||
expectedFixture: "", // Encryption is not supported
|
||
},
|
||
{
|
||
name: "gzip → uncompressed decrypted", // We can’t represent encrypted images anyway, but verify that we reject decryption attempts.
|
||
sourceFixture: "v2s1.manifest.json",
|
||
updates: []types.BlobInfo{
|
||
{
|
||
Digest: "sha256:e692418e4cbaf90ca69d05a66403747baa33ee08806650b51fab815ad7fc331f",
|
||
Size: 32654,
|
||
CompressionOperation: types.Decompress,
|
||
CryptoOperation: types.Decrypt,
|
||
},
|
||
{
|
||
Digest: "sha256:3c3a4604a545cdc127456d94e421cd355bca5b528f4a9c1905b15da2eb4a4c6b",
|
||
Size: 16724,
|
||
CompressionOperation: types.Decompress,
|
||
CryptoOperation: types.Decrypt,
|
||
},
|
||
{
|
||
Digest: "sha256:ec4b8955958665577945c89419d1af06b5f7636b4ac3da7f12184802ad867736",
|
||
Size: 73109,
|
||
CompressionOperation: types.Decompress,
|
||
CryptoOperation: types.Decrypt,
|
||
},
|
||
},
|
||
expectedFixture: "", // Decryption is not supported
|
||
},
|
||
} {
|
||
manifest := manifestSchema1FromFixture(t, c.sourceFixture)
|
||
|
||
err := manifest.UpdateLayerInfos(c.updates)
|
||
if c.expectedFixture == "" {
|
||
assert.Error(t, err, c.name)
|
||
} else {
|
||
require.NoError(t, err, c.name)
|
||
|
||
updatedManifestBytes, err := manifest.Serialize()
|
||
require.NoError(t, err, c.name)
|
||
|
||
// Drop "signatures" which is generated by AddDummyV2S1Signature
|
||
assertJSONEqualsFixture(t, updatedManifestBytes, c.expectedFixture, "signatures")
|
||
}
|
||
}
|
||
}
|
||
|
||
func TestSchema1ImageID(t *testing.T) {
|
||
m := manifestSchema1FromFixture(t, "schema2-to-schema1-by-docker.json")
|
||
id, err := m.ImageID(schema1FixtureLayerDiffIDs)
|
||
require.NoError(t, err)
|
||
// NOTE: This value is dependent on the Schema1.ToSchema2Config implementation, and not necessarily stable over time.
|
||
// This is mostly a smoke-test; it’s fine to just update this value if that implementation changes.
|
||
assert.Equal(t, "9ca4bda0a6b3727a6ffcc43e981cad0f24e2ec79d338f6ba325b4dfd0756fb8f", id)
|
||
}
|