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) }