Codebase list golang-github-containers-image / 69aa1e8
media type checks When copying an image, record the compression in the BlobInfo and use the information when updating the manifest's layer infos to set the layers' media types correctly. Also check for supported media types when parsing a v2s2/OCI1 manifest. Note that consumers of the containers/image library need to update opencontainers/image-spec to commit 775207bd45b6cb8153ce218cc59351799217451f. Fixes: github.com/containers/libpod/issues/2013 Fixes: github.com/containers/buildah/issues/1589 Signed-off-by: Valentin Rothberg <rothberg@redhat.com> Valentin Rothberg 4 years ago
32 changed file(s) with 1391 addition(s) and 75 deletion(s). Raw diff Collapse all Expand all
910910 return types.BlobInfo{}, errors.Wrap(err, "Error writing blob")
911911 }
912912
913 uploadedInfo.CompressionOperation = compressionOperation
914 // If we can modify the layer's blob, set the desired algorithm for it to be set in the manifest.
915 if canModifyBlob && !isConfig {
916 uploadedInfo.CompressionAlgorithm = &desiredCompressionFormat
917 }
918
913919 // This is fairly horrible: the writer from getOriginalLayerCopyWriter wants to consumer
914920 // all of the input (to compute DiffIDs), even if dest.PutBlob does not need it.
915921 // So, read everything from originalLayerReader, which will cause the rest to be
2626 github.com/mattn/go-isatty v0.0.4 // indirect
2727 github.com/mtrmac/gpgme v0.0.0-20170102180018-b2432428689c
2828 github.com/opencontainers/go-digest v1.0.0-rc1
29 github.com/opencontainers/image-spec v1.0.0
29 github.com/opencontainers/image-spec v1.0.2-0.20190823105129-775207bd45b6
3030 github.com/opencontainers/selinux v1.2.2
3131 github.com/ostreedev/ostree-go v0.0.0-20190702140239-759a8c1ac913
3232 github.com/pkg/errors v0.8.1
7474 github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=
7575 github.com/opencontainers/image-spec v1.0.0 h1:jcw3cCH887bLKETGYpv8afogdYchbShR0eH6oD9d5PQ=
7676 github.com/opencontainers/image-spec v1.0.0/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0=
77 github.com/opencontainers/image-spec v1.0.1 h1:JMemWkRwHx4Zj+fVxWoMCFm/8sYGGrUVojFA6h/TRcI=
78 github.com/opencontainers/image-spec v1.0.2-0.20190823105129-775207bd45b6 h1:yN8BPXVwMBAm3Cuvh1L5XE8XpvYRMdsVLd82ILprhUU=
79 github.com/opencontainers/image-spec v1.0.2-0.20190823105129-775207bd45b6/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0=
7780 github.com/opencontainers/runc v1.0.0-rc8 h1:dDCFes8Hj1r/i5qnypONo5jdOme/8HWZC/aNDyhECt0=
7881 github.com/opencontainers/runc v1.0.0-rc8/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U=
7982 github.com/opencontainers/selinux v1.2.2 h1:Kx9J6eDG5/24A6DtUquGSpJQ+m2MUTahn4FtGEe8bFg=
97100 github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
98101 github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
99102 github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
103 github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
100104 github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
101105 github.com/syndtr/gocapability v0.0.0-20180916011248-d98352740cb2 h1:b6uOv7YOFK0TYG7HtkIgExQo+2RdLuwRft63jn2HWj8=
102106 github.com/syndtr/gocapability v0.0.0-20180916011248-d98352740cb2/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww=
141145 gopkg.in/yaml.v1 v1.0.0-20140924161607-9f9df34309c0/go.mod h1:WDnlLJ4WF5VGsH/HVa3CI79GS0ol3YnhVnKP89i0kNg=
142146 gopkg.in/yaml.v2 v2.0.0-20170208141851-a3f3340b5840 h1:BftvRMCaj0KX6UeD7gnNJv0W8b4HAYTEWes978CoWlY=
143147 gopkg.in/yaml.v2 v2.0.0-20170208141851-a3f3340b5840/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
148 gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
144149 gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
145150 gotest.tools v0.0.0-20190624233834-05ebafbffc79 h1:C+K4iPg1rIvmCf4JjelkbWv2jeWevEwp05Lz8XfTYgE=
146151 gotest.tools v0.0.0-20190624233834-05ebafbffc79/go.mod h1:R//lfYlUuTOTfblYI3lGoAAAebUdzjvbmQsuB7Ykd90=
55 "crypto/sha256"
66 "encoding/hex"
77 "encoding/json"
8 "fmt"
89 "io/ioutil"
910 "strings"
1011
206207 layers := make([]imgspecv1.Descriptor, len(m.m.LayersDescriptors))
207208 for idx := range layers {
208209 layers[idx] = oci1DescriptorFromSchema2Descriptor(m.m.LayersDescriptors[idx])
209 if m.m.LayersDescriptors[idx].MediaType == manifest.DockerV2Schema2ForeignLayerMediaType {
210 switch m.m.LayersDescriptors[idx].MediaType {
211 case manifest.DockerV2Schema2ForeignLayerMediaType:
210212 layers[idx].MediaType = imgspecv1.MediaTypeImageLayerNonDistributable
211 } else {
212 // we assume layers are gzip'ed because docker v2s2 only deals with
213 // gzip'ed layers. However, OCI has non-gzip'ed layers as well.
213 case manifest.DockerV2Schema2ForeignLayerMediaTypeGzip:
214 layers[idx].MediaType = imgspecv1.MediaTypeImageLayerNonDistributableGzip
215 case manifest.DockerV2SchemaLayerMediaTypeUncompressed:
216 layers[idx].MediaType = imgspecv1.MediaTypeImageLayer
217 case manifest.DockerV2Schema2LayerMediaType:
214218 layers[idx].MediaType = imgspecv1.MediaTypeImageLayerGzip
219 default:
220 return nil, fmt.Errorf("Unknown media type during manifest conversion: %q", m.m.LayersDescriptors[idx].MediaType)
215221 }
216222 }
217223
4444 panic("Unexpected call to a mock function")
4545 }
4646
47 func manifestSchema2FromFixture(t *testing.T, src types.ImageSource, fixture string) genericManifest {
47 func manifestSchema2FromFixture(t *testing.T, src types.ImageSource, fixture string, mustFail bool) genericManifest {
4848 manifest, err := ioutil.ReadFile(filepath.Join("fixtures", fixture))
4949 require.NoError(t, err)
5050
5151 m, err := manifestSchema2FromManifest(src, manifest)
52 require.NoError(t, err)
52 if mustFail {
53 require.Error(t, err)
54 } else {
55 require.NoError(t, err)
56 }
5357 return m
5458 }
5559
9094 func TestManifestSchema2FromManifest(t *testing.T) {
9195 // This just tests that the JSON can be loaded; we test that the parsed
9296 // values are correctly returned in tests for the individual getter methods.
93 _ = manifestSchema2FromFixture(t, unusedImageSource{}, "schema2.json")
97 _ = manifestSchema2FromFixture(t, unusedImageSource{}, "schema2.json", false)
9498
9599 _, err := manifestSchema2FromManifest(nil, []byte{})
96100 assert.Error(t, err)
104108
105109 func TestManifestSchema2Serialize(t *testing.T) {
106110 for _, m := range []genericManifest{
107 manifestSchema2FromFixture(t, unusedImageSource{}, "schema2.json"),
111 manifestSchema2FromFixture(t, unusedImageSource{}, "schema2.json", false),
108112 manifestSchema2FromComponentsLikeFixture(nil),
109113 } {
110114 serialized, err := m.serialize()
128132
129133 func TestManifestSchema2ManifestMIMEType(t *testing.T) {
130134 for _, m := range []genericManifest{
131 manifestSchema2FromFixture(t, unusedImageSource{}, "schema2.json"),
135 manifestSchema2FromFixture(t, unusedImageSource{}, "schema2.json", false),
132136 manifestSchema2FromComponentsLikeFixture(nil),
133137 } {
134138 assert.Equal(t, manifest.DockerV2Schema2MediaType, m.manifestMIMEType())
137141
138142 func TestManifestSchema2ConfigInfo(t *testing.T) {
139143 for _, m := range []genericManifest{
140 manifestSchema2FromFixture(t, unusedImageSource{}, "schema2.json"),
144 manifestSchema2FromFixture(t, unusedImageSource{}, "schema2.json", false),
141145 manifestSchema2FromComponentsLikeFixture(nil),
142146 } {
143147 assert.Equal(t, types.BlobInfo{
194198 } else {
195199 src = nil
196200 }
197 m := manifestSchema2FromFixture(t, src, "schema2.json")
201 m := manifestSchema2FromFixture(t, src, "schema2.json", false)
198202 blob, err := m.ConfigBlob(context.Background())
199203 if c.blob != nil {
200204 assert.NoError(t, err)
218222
219223 func TestManifestSchema2LayerInfo(t *testing.T) {
220224 for _, m := range []genericManifest{
221 manifestSchema2FromFixture(t, unusedImageSource{}, "schema2.json"),
225 manifestSchema2FromFixture(t, unusedImageSource{}, "schema2.json", false),
222226 manifestSchema2FromComponentsLikeFixture(nil),
223227 } {
224228 assert.Equal(t, []types.BlobInfo{
253257
254258 func TestManifestSchema2EmbeddedDockerReferenceConflicts(t *testing.T) {
255259 for _, m := range []genericManifest{
256 manifestSchema2FromFixture(t, unusedImageSource{}, "schema2.json"),
260 manifestSchema2FromFixture(t, unusedImageSource{}, "schema2.json", false),
257261 manifestSchema2FromComponentsLikeFixture(nil),
258262 } {
259263 for _, name := range []string{"busybox", "example.com:5555/ns/repo:tag"} {
309313
310314 func TestManifestSchema2UpdatedImageNeedsLayerDiffIDs(t *testing.T) {
311315 for _, m := range []genericManifest{
312 manifestSchema2FromFixture(t, unusedImageSource{}, "schema2.json"),
316 manifestSchema2FromFixture(t, unusedImageSource{}, "schema2.json", false),
313317 manifestSchema2FromComponentsLikeFixture(nil),
314318 } {
315319 assert.False(t, m.UpdatedImageNeedsLayerDiffIDs(types.ManifestUpdateOptions{
437441
438442 func TestManifestSchema2UpdatedImage(t *testing.T) {
439443 originalSrc := newSchema2ImageSource(t, "httpd:latest")
440 original := manifestSchema2FromFixture(t, originalSrc, "schema2.json")
444 original := manifestSchema2FromFixture(t, originalSrc, "schema2.json", false)
441445
442446 // LayerInfos:
443447 layerInfos := append(original.LayerInfos()[1:], original.LayerInfos()[0])
489493 }
490494
491495 // m hasn’t been changed:
492 m2 := manifestSchema2FromFixture(t, originalSrc, "schema2.json")
496 m2 := manifestSchema2FromFixture(t, originalSrc, "schema2.json", false)
493497 typedOriginal, ok := original.(*manifestSchema2)
494498 require.True(t, ok)
495499 typedM2, ok := m2.(*manifestSchema2)
499503
500504 func TestConvertToManifestOCI(t *testing.T) {
501505 originalSrc := newSchema2ImageSource(t, "httpd-copy:latest")
502 original := manifestSchema2FromFixture(t, originalSrc, "schema2.json")
506 original := manifestSchema2FromFixture(t, originalSrc, "schema2.json", false)
503507 res, err := original.UpdatedImage(context.Background(), types.ManifestUpdateOptions{
504508 ManifestMIMEType: imgspecv1.MediaTypeImageManifest,
505509 })
519523 assert.Equal(t, byHand, converted)
520524 }
521525
526 func TestConvertToManifestOCIAllMediaTypes(t *testing.T) {
527 originalSrc := newSchema2ImageSource(t, "httpd-copy:latest")
528 original := manifestSchema2FromFixture(t, originalSrc, "schema2-all-media-types.json", false)
529 res, err := original.UpdatedImage(context.Background(), types.ManifestUpdateOptions{
530 ManifestMIMEType: imgspecv1.MediaTypeImageManifest,
531 })
532 require.NoError(t, err)
533 convertedJSON, mt, err := res.Manifest(context.Background())
534 require.NoError(t, err)
535 assert.Equal(t, imgspecv1.MediaTypeImageManifest, mt)
536
537 byHandJSON, err := ioutil.ReadFile("fixtures/schema2-all-media-types-to-oci1.json")
538 require.NoError(t, err)
539 var converted, byHand map[string]interface{}
540 err = json.Unmarshal(byHandJSON, &byHand)
541 require.NoError(t, err)
542 err = json.Unmarshal(convertedJSON, &converted)
543 require.NoError(t, err)
544 assert.Equal(t, byHand, converted)
545 }
546
547 func TestConvertToOCIWithInvalidMIMEType(t *testing.T) {
548 originalSrc := newSchema2ImageSource(t, "httpd-copy:latest")
549 manifestSchema2FromFixture(t, originalSrc, "schema2-invalid-media-type.json", true)
550 }
551
522552 func TestConvertToManifestSchema1(t *testing.T) {
523553 originalSrc := newSchema2ImageSource(t, "httpd-copy:latest")
524 original := manifestSchema2FromFixture(t, originalSrc, "schema2.json")
554 original := manifestSchema2FromFixture(t, originalSrc, "schema2.json", false)
525555 memoryDest := &memoryImageDest{ref: originalSrc.ref}
526556 res, err := original.UpdatedImage(context.Background(), types.ManifestUpdateOptions{
527557 ManifestMIMEType: manifest.DockerV2Schema1SignedMediaType,
0 {
1 "schemaVersion": 2,
2 "mediaType": "application/vnd.docker.distribution.manifest.v2+json",
3 "config": {
4 "mediaType": "application/vnd.docker.container.image.v1+json",
5 "size": 4651,
6 "digest": "sha256:a13a0762ab7bed51a1b49adec0a702b1cd99294fd460a025b465bcfb7b152745"
7 },
8 "layers": [
9 {
10 "mediaType": "application/vnd.docker.image.rootfs.diff.tar",
11 "size": 51354364,
12 "digest": "sha256:6a5a5368e0c2d3e5909184fa28ddfd56072e7ff3ee9a945876f7eee5896ef5bb"
13 },
14 {
15 "mediaType": "application/vnd.docker.image.rootfs.diff.tar.zstd",
16 "size": 150,
17 "digest": "sha256:1bbf5d58d24c47512e234a5623474acf65ae00d4d1414272a893204f44cc680c"
18 },
19 {
20 "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
21 "size": 152,
22 "digest": "sha256:2bbf5d58d24c47512e234a5623474acf65ae00d4d1414272a893204f44cc680c"
23 },
24 {
25 "mediaType": "application/vnd.docker.image.rootfs.foreign.diff.tar",
26 "size": 11739507,
27 "digest": "sha256:8f5dc8a4b12c307ac84de90cdd9a7f3915d1be04c9388868ca118831099c67a9"
28 },
29 {
30 "mediaType": "application/vnd.docker.image.rootfs.foreign.diff.tar.gzip",
31 "size": 8841833,
32 "digest": "sha256:bbd6b22eb11afce63cc76f6bc41042d99f10d6024c96b655dafba930b8d25909"
33 },
34 {
35 "mediaType": "application/vnd.docker.image.rootfs.foreign.diff.tar.gzip",
36 "size": 291,
37 "digest": "sha256:960e52ecf8200cbd84e70eb2ad8678f4367e50d14357021872c10fa3fc5935fa"
38 }
39 ]
40 }
0 {
1 "schemaVersion": 2,
2 "config": {
3 "mediaType": "application/vnd.oci.image.config.v1+json",
4 "size": 4651,
5 "digest": "sha256:a13a0762ab7bed51a1b49adec0a702b1cd99294fd460a025b465bcfb7b152745"
6 },
7 "layers": [
8 {
9 "mediaType": "application/vnd.oci.image.layer.v1.tar",
10 "size": 51354364,
11 "digest": "sha256:6a5a5368e0c2d3e5909184fa28ddfd56072e7ff3ee9a945876f7eee5896ef5bb"
12 },
13 {
14 "mediaType": "application/vnd.oci.image.layer.v1.tar+zstd",
15 "size": 150,
16 "digest": "sha256:1bbf5d58d24c47512e234a5623474acf65ae00d4d1414272a893204f44cc680c"
17 },
18 {
19 "mediaType": "application/vnd.oci.image.layer.v1.tar+gzip",
20 "size": 152,
21 "digest": "sha256:2bbf5d58d24c47512e234a5623474acf65ae00d4d1414272a893204f44cc680c"
22 },
23 {
24 "mediaType": "application/vnd.oci.image.layer.nondistributable.v1.tar",
25 "size": 11739507,
26 "digest": "sha256:8f5dc8a4b12c307ac84de90cdd9a7f3915d1be04c9388868ca118831099c67a9"
27 },
28 {
29 "mediaType": "application/vnd.oci.image.layer.nondistributable.v1.tar+gzip",
30 "size": 8841833,
31 "digest": "sha256:bbd6b22eb11afce63cc76f6bc41042d99f10d6024c96b655dafba930b8d25909"
32 },
33 {
34 "mediaType": "application/vnd.oci.image.layer.nondistributable.v1.tar+gzip",
35 "size": 291,
36 "digest": "sha256:960e52ecf8200cbd84e70eb2ad8678f4367e50d14357021872c10fa3fc5935fa"
37 }
38 ]
39 }
0 {
1 "schemaVersion": 2,
2 "config": {
3 "mediaType": "application/vnd.oci.image.config.v1+json",
4 "size": 5940,
5 "digest": "sha256:9ca4bda0a6b3727a6ffcc43e981cad0f24e2ec79d338f6ba325b4dfd0756fb8f"
6 },
7 "layers": [
8 {
9 "mediaType": "application/vnd.oci.image.layer.v1.tar+invalid-suffix",
10 "size": 51354364,
11 "digest": "sha256:6a5a5368e0c2d3e5909184fa28ddfd56072e7ff3ee9a945876f7eee5896ef5bb"
12 }
13 ]
14 }
3333 "digest": "sha256:960e52ecf8200cbd84e70eb2ad8678f4367e50d14357021872c10fa3fc5935fa"
3434 }
3535 ]
36 }
36 }
0 {
1 "schemaVersion": 2,
2 "config": {
3 "mediaType": "application/vnd.oci.image.config.v1+json",
4 "size": 4651,
5 "digest": "sha256:a13a0762ab7bed51a1b49adec0a702b1cd99294fd460a025b465bcfb7b152745"
6 },
7 "layers": [
8 {
9 "mediaType": "application/vnd.oci.image.layer.v1.tar",
10 "size": 51354364,
11 "digest": "sha256:6a5a5368e0c2d3e5909184fa28ddfd56072e7ff3ee9a945876f7eee5896ef5bb"
12 },
13 {
14 "mediaType": "application/vnd.oci.image.layer.v1.tar+gzip",
15 "size": 152,
16 "digest": "sha256:2bbf5d58d24c47512e234a5623474acf65ae00d4d1414272a893204f44cc680c"
17 },
18 {
19 "mediaType": "application/vnd.oci.image.layer.nondistributable.v1.tar",
20 "size": 11739507,
21 "digest": "sha256:8f5dc8a4b12c307ac84de90cdd9a7f3915d1be04c9388868ca118831099c67a9"
22 },
23 {
24 "mediaType": "application/vnd.oci.image.layer.nondistributable.v1.tar+gzip",
25 "size": 8841833,
26 "digest": "sha256:bbd6b22eb11afce63cc76f6bc41042d99f10d6024c96b655dafba930b8d25909"
27 },
28 {
29 "mediaType": "application/vnd.oci.image.layer.nondistributable.v1.tar+gzip",
30 "size": 291,
31 "digest": "sha256:960e52ecf8200cbd84e70eb2ad8678f4367e50d14357021872c10fa3fc5935fa"
32 }
33 ]
34 }
0 {
1 "schemaVersion": 2,
2 "mediaType": "application/vnd.docker.distribution.manifest.v2+json",
3 "config": {
4 "mediaType": "application/vnd.docker.container.image.v1+json",
5 "size": 4651,
6 "digest": "sha256:9ca4bda0a6b3727a6ffcc43e981cad0f24e2ec79d338f6ba325b4dfd0756fb8f"
7 },
8 "layers": [
9 {
10 "mediaType": "application/vnd.docker.image.rootfs.diff.tar",
11 "size": 51354364,
12 "digest": "sha256:6a5a5368e0c2d3e5909184fa28ddfd56072e7ff3ee9a945876f7eee5896ef5bb"
13 },
14 {
15 "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
16 "size": 152,
17 "digest": "sha256:2bbf5d58d24c47512e234a5623474acf65ae00d4d1414272a893204f44cc680c"
18 },
19 {
20 "mediaType": "application/vnd.docker.image.rootfs.foreign.diff.tar",
21 "size": 11739507,
22 "digest": "sha256:8f5dc8a4b12c307ac84de90cdd9a7f3915d1be04c9388868ca118831099c67a9"
23 },
24 {
25 "mediaType": "application/vnd.docker.image.rootfs.foreign.diff.tar.gzip",
26 "size": 8841833,
27 "digest": "sha256:bbd6b22eb11afce63cc76f6bc41042d99f10d6024c96b655dafba930b8d25909"
28 },
29 {
30 "mediaType": "application/vnd.docker.image.rootfs.foreign.diff.tar.gzip",
31 "size": 291,
32 "digest": "sha256:960e52ecf8200cbd84e70eb2ad8678f4367e50d14357021872c10fa3fc5935fa"
33 }
34 ]
35 }
0 {
1 "schemaVersion": 2,
2 "mediaType": "application/vnd.docker.distribution.manifest.v2+json",
3 "config": {
4 "mediaType": "application/octet-stream",
5 "size": 5940,
6 "digest": "sha256:9ca4bda0a6b3727a6ffcc43e981cad0f24e2ec79d338f6ba325b4dfd0756fb8f"
7 },
8 "layers": [
9 {
10 "mediaType": "application/vnd.docker.image.rootfs.diff.tar.zstd",
11 "size": 51354364,
12 "digest": "sha256:6a5a5368e0c2d3e5909184fa28ddfd56072e7ff3ee9a945876f7eee5896ef5bb"
13 },
14 {
15 "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
16 "size": 150,
17 "digest": "sha256:1bbf5d58d24c47512e234a5623474acf65ae00d4d1414272a893204f44cc680c"
18 },
19 {
20 "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
21 "size": 11739507,
22 "digest": "sha256:8f5dc8a4b12c307ac84de90cdd9a7f3915d1be04c9388868ca118831099c67a9"
23 },
24 {
25 "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
26 "size": 8841833,
27 "digest": "sha256:bbd6b22eb11afce63cc76f6bc41042d99f10d6024c96b655dafba930b8d25909"
28 },
29 {
30 "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
31 "size": 291,
32 "digest": "sha256:960e52ecf8200cbd84e70eb2ad8678f4367e50d14357021872c10fa3fc5935fa"
33 }
34 ]
35 }
22 import (
33 "context"
44 "encoding/json"
5 "fmt"
56 "io/ioutil"
67
78 "github.com/containers/image/docker/reference"
186187 layers := make([]manifest.Schema2Descriptor, len(m.m.Layers))
187188 for idx := range layers {
188189 layers[idx] = schema2DescriptorFromOCI1Descriptor(m.m.Layers[idx])
189 layers[idx].MediaType = manifest.DockerV2Schema2LayerMediaType
190 switch layers[idx].MediaType {
191 case imgspecv1.MediaTypeImageLayerNonDistributable:
192 layers[idx].MediaType = manifest.DockerV2Schema2ForeignLayerMediaType
193 case imgspecv1.MediaTypeImageLayerNonDistributableGzip:
194 layers[idx].MediaType = manifest.DockerV2Schema2ForeignLayerMediaTypeGzip
195 case imgspecv1.MediaTypeImageLayerNonDistributableZstd:
196 return nil, fmt.Errorf("Error during manifest conversion: %q: zstd compression is not supported for docker images", layers[idx].MediaType)
197 case imgspecv1.MediaTypeImageLayer:
198 layers[idx].MediaType = manifest.DockerV2SchemaLayerMediaTypeUncompressed
199 case imgspecv1.MediaTypeImageLayerGzip:
200 layers[idx].MediaType = manifest.DockerV2Schema2LayerMediaType
201 case imgspecv1.MediaTypeImageLayerZstd:
202 return nil, fmt.Errorf("Error during manifest conversion: %q: zstd compression is not supported for docker images", layers[idx].MediaType)
203 default:
204 return nil, fmt.Errorf("Unknown media type during manifest conversion: %q", layers[idx].MediaType)
205 }
190206 }
191207
192208 // Rather than copying the ConfigBlob now, we just pass m.src to the
409409
410410 // FIXME? Test also the various failure cases, if only to see that we don't crash?
411411 }
412
413 func TestConvertToManifestSchema2AllMediaTypes(t *testing.T) {
414 originalSrc := newOCI1ImageSource(t, "httpd-copy:latest")
415 original := manifestOCI1FromFixture(t, originalSrc, "oci1-all-media-types.json")
416 _, err := original.UpdatedImage(context.Background(), types.ManifestUpdateOptions{
417 ManifestMIMEType: manifest.DockerV2Schema2MediaType,
418 })
419 require.Error(t, err) // zstd compression is not supported for docker images
420 }
421
422 func TestConvertToV2S2WithInvalidMIMEType(t *testing.T) {
423 originalSrc := newOCI1ImageSource(t, "httpd-copy:latest")
424 manifest, err := ioutil.ReadFile(filepath.Join("fixtures", "oci1-invalid-media-type.json"))
425 require.NoError(t, err)
426
427 _, err = manifestOCI1FromManifest(originalSrc, manifest)
428 require.Error(t, err)
429 }
44
55 import (
66 "context"
7
78 "github.com/containers/image/types"
89 )
910
11
22 import (
33 "encoding/json"
4 "fmt"
45 "time"
56
7 "github.com/containers/image/pkg/compression"
68 "github.com/containers/image/pkg/strslice"
79 "github.com/containers/image/types"
810 "github.com/opencontainers/go-digest"
911 "github.com/pkg/errors"
12 "github.com/sirupsen/logrus"
1013 )
1114
1215 // Schema2Descriptor is a “descriptor” in docker/distribution schema 2.
160163 if err := json.Unmarshal(manifest, &s2); err != nil {
161164 return nil, err
162165 }
166 // Check manifest's and layers' media types.
167 if err := SupportedSchema2MediaType(s2.MediaType); err != nil {
168 return nil, err
169 }
170 for _, layer := range s2.LayersDescriptors {
171 if err := SupportedSchema2MediaType(layer.MediaType); err != nil {
172 return nil, err
173 }
174 }
163175 return &s2, nil
164176 }
165177
206218 original := m.LayersDescriptors
207219 m.LayersDescriptors = make([]Schema2Descriptor, len(layerInfos))
208220 for i, info := range layerInfos {
209 m.LayersDescriptors[i].MediaType = original[i].MediaType
221 // First make sure we support the media type of the original layer.
222 if err := SupportedSchema2MediaType(original[i].MediaType); err != nil {
223 return fmt.Errorf("Error preparing updated manifest: unknown media type of original layer: %q", original[i].MediaType)
224 }
225
226 // Set the correct media types based on the specified compression
227 // operation, the desired compression algorithm AND the original media
228 // type.
229 switch info.CompressionOperation {
230 case types.PreserveOriginal:
231 // Keep the original media type.
232 m.LayersDescriptors[i].MediaType = original[i].MediaType
233
234 case types.Decompress:
235 // Decompress the original media type and check if it was
236 // non-distributable one or not.
237 switch original[i].MediaType {
238 case DockerV2Schema2ForeignLayerMediaTypeGzip:
239 m.LayersDescriptors[i].MediaType = DockerV2Schema2ForeignLayerMediaType
240 case DockerV2Schema2LayerMediaType:
241 m.LayersDescriptors[i].MediaType = DockerV2SchemaLayerMediaTypeUncompressed
242 default:
243 return fmt.Errorf("Error preparing updated manifest: unsupported media type for decompression: %q", original[i].MediaType)
244 }
245
246 case types.Compress:
247 if info.CompressionAlgorithm == nil {
248 logrus.Debugf("Preparing updated manifest: blob %q was compressed but does not specify by which algorithm: falling back to use the original blob", info.Digest)
249 m.LayersDescriptors[i].MediaType = original[i].MediaType
250 break
251 }
252 // Compress the original media type and set the new one based on
253 // that type (distributable or not) and the specified compression
254 // algorithm. Throw an error if the algorithm is not supported.
255 switch info.CompressionAlgorithm.Name() {
256 case compression.Gzip.Name():
257 switch original[i].MediaType {
258 case DockerV2Schema2ForeignLayerMediaType:
259 m.LayersDescriptors[i].MediaType = DockerV2Schema2ForeignLayerMediaTypeGzip
260 case DockerV2SchemaLayerMediaTypeUncompressed:
261 m.LayersDescriptors[i].MediaType = DockerV2Schema2LayerMediaType
262 default:
263 return fmt.Errorf("Error preparing updated manifest: unsupported media type for compression: %q", original[i].MediaType)
264 }
265 case compression.Zstd.Name():
266 return fmt.Errorf("Error preparing updated manifest: zstd compression is not supported for docker images")
267 default:
268 return fmt.Errorf("Error preparing updated manifest: unknown compression algorithm %q for layer %q", info.CompressionAlgorithm.Name(), info.Digest)
269 }
270
271 default:
272 return fmt.Errorf("Error preparing updated manifest: unknown compression operation (%d) for layer %q", info.CompressionOperation, info.Digest)
273 }
210274 m.LayersDescriptors[i].Digest = info.Digest
211275 m.LayersDescriptors[i].Size = info.Size
212276 m.LayersDescriptors[i].URLs = info.URLs
0 package manifest
1
2 import (
3 "io/ioutil"
4 "testing"
5
6 "github.com/containers/image/pkg/compression"
7 "github.com/containers/image/types"
8 "github.com/stretchr/testify/assert"
9 )
10
11 func TestSupportedSchema2MediaType(t *testing.T) {
12 type testData struct {
13 m string
14 mustFail bool
15 }
16 data := []testData{
17 {
18 DockerV2Schema2MediaType,
19 false,
20 },
21 {
22 DockerV2Schema2ConfigMediaType,
23 false,
24 },
25 {
26 DockerV2Schema2LayerMediaType,
27 false,
28 },
29 {
30 DockerV2SchemaLayerMediaTypeUncompressed,
31 false,
32 },
33 {
34 DockerV2ListMediaType,
35 false,
36 },
37 {
38 DockerV2Schema2ForeignLayerMediaType,
39 false,
40 },
41 {
42 DockerV2Schema2ForeignLayerMediaTypeGzip,
43 false,
44 },
45 {
46 "application/vnd.docker.image.rootfs.foreign.diff.unknown",
47 true,
48 },
49 }
50 for _, d := range data {
51 err := SupportedSchema2MediaType(d.m)
52 if d.mustFail {
53 assert.NotNil(t, err)
54 } else {
55 assert.Nil(t, err)
56 }
57 }
58 }
59
60 func TestUpdateLayerInfosV2S2GzipToZstd(t *testing.T) {
61 bytes, err := ioutil.ReadFile("fixtures/v2s2.manifest.json")
62 assert.Nil(t, err)
63
64 origManifest, err := Schema2FromManifest(bytes)
65 assert.Nil(t, err)
66
67 err = origManifest.UpdateLayerInfos([]types.BlobInfo{
68 {
69 Digest: "sha256:e692418e4cbaf90ca69d05a66403747baa33ee08806650b51fab815ad7fc331f",
70 Size: 32654,
71 MediaType: DockerV2Schema2LayerMediaType,
72 CompressionOperation: types.Compress,
73 CompressionAlgorithm: &compression.Zstd,
74 },
75 {
76 Digest: "sha256:3c3a4604a545cdc127456d94e421cd355bca5b528f4a9c1905b15da2eb4a4c6b",
77 Size: 16724,
78 MediaType: DockerV2Schema2LayerMediaType,
79 CompressionOperation: types.Compress,
80 CompressionAlgorithm: &compression.Zstd,
81 },
82 {
83 Digest: "sha256:ec4b8955958665577945c89419d1af06b5f7636b4ac3da7f12184802ad867736",
84 Size: 73109,
85 MediaType: DockerV2Schema2LayerMediaType,
86 CompressionOperation: types.Compress,
87 CompressionAlgorithm: &compression.Zstd,
88 },
89 })
90 assert.NotNil(t, err) // zstd is not supported for docker images
91 }
92
93 func TestUpdateLayerInfosV2S2InvalidCompressionOperation(t *testing.T) {
94 bytes, err := ioutil.ReadFile("fixtures/v2s2.manifest.json")
95 assert.Nil(t, err)
96
97 origManifest, err := Schema2FromManifest(bytes)
98 assert.Nil(t, err)
99
100 err = origManifest.UpdateLayerInfos([]types.BlobInfo{
101 {
102 Digest: "sha256:e692418e4cbaf90ca69d05a66403747baa33ee08806650b51fab815ad7fc331f",
103 Size: 32654,
104 MediaType: DockerV2Schema2LayerMediaType,
105 CompressionOperation: types.Decompress,
106 },
107 {
108 Digest: "sha256:3c3a4604a545cdc127456d94e421cd355bca5b528f4a9c1905b15da2eb4a4c6b",
109 Size: 16724,
110 MediaType: DockerV2Schema2LayerMediaType,
111 CompressionOperation: types.Decompress,
112 },
113 {
114 Digest: "sha256:ec4b8955958665577945c89419d1af06b5f7636b4ac3da7f12184802ad867736",
115 Size: 73109,
116 MediaType: DockerV2Schema2LayerMediaType,
117 CompressionOperation: 42, // MUST fail here
118 },
119 })
120 assert.NotNil(t, err)
121 }
122
123 func TestUpdateLayerInfosV2S2InvalidCompressionAlgorithm(t *testing.T) {
124 bytes, err := ioutil.ReadFile("fixtures/v2s2.manifest.json")
125 assert.Nil(t, err)
126
127 origManifest, err := Schema2FromManifest(bytes)
128 assert.Nil(t, err)
129
130 err = origManifest.UpdateLayerInfos([]types.BlobInfo{
131 {
132 Digest: "sha256:e692418e4cbaf90ca69d05a66403747baa33ee08806650b51fab815ad7fc331f",
133 Size: 32654,
134 MediaType: DockerV2Schema2LayerMediaType,
135 CompressionOperation: types.Compress,
136 CompressionAlgorithm: &compression.Gzip,
137 },
138 {
139 Digest: "sha256:3c3a4604a545cdc127456d94e421cd355bca5b528f4a9c1905b15da2eb4a4c6b",
140 Size: 16724,
141 MediaType: DockerV2Schema2LayerMediaType,
142 CompressionOperation: types.Compress,
143 CompressionAlgorithm: &compression.Gzip,
144 },
145 {
146 Digest: "sha256:ec4b8955958665577945c89419d1af06b5f7636b4ac3da7f12184802ad867736",
147 Size: 73109,
148 MediaType: DockerV2Schema2LayerMediaType,
149 CompressionOperation: types.Compress,
150 CompressionAlgorithm: &compression.Zstd, // MUST fail here
151 },
152 })
153 assert.NotNil(t, err)
154 }
155
156 func TestUpdateLayerInfosV2S2NondistributableToGzip(t *testing.T) {
157 bytes, err := ioutil.ReadFile("fixtures/v2s2.nondistributable.manifest.json")
158 assert.Nil(t, err)
159
160 origManifest, err := Schema2FromManifest(bytes)
161 assert.Nil(t, err)
162
163 err = origManifest.UpdateLayerInfos([]types.BlobInfo{
164 {
165 Digest: "sha256:e692418e4cbaf90ca69d05a66403747baa33ee08806650b51fab815ad7fc331f",
166 Size: 32654,
167 MediaType: DockerV2Schema2ForeignLayerMediaType,
168 CompressionOperation: types.Compress,
169 CompressionAlgorithm: &compression.Gzip,
170 },
171 })
172 assert.Nil(t, err)
173
174 updatedManifestBytes, err := origManifest.Serialize()
175 assert.Nil(t, err)
176
177 bytes, err = ioutil.ReadFile("fixtures/v2s2.nondistributable.gzip.manifest.json")
178 assert.Nil(t, err)
179
180 expectedManifest, err := Schema2FromManifest(bytes)
181 assert.Nil(t, err)
182
183 expectedManifestBytes, err := expectedManifest.Serialize()
184 assert.Nil(t, err)
185
186 assert.Equal(t, string(expectedManifestBytes), string(updatedManifestBytes))
187 }
188
189 func TestUpdateLayerInfosV2S2NondistributableGzipToUncompressed(t *testing.T) {
190 bytes, err := ioutil.ReadFile("fixtures/v2s2.nondistributable.gzip.manifest.json")
191 assert.Nil(t, err)
192
193 origManifest, err := Schema2FromManifest(bytes)
194 assert.Nil(t, err)
195
196 err = origManifest.UpdateLayerInfos([]types.BlobInfo{
197 {
198 Digest: "sha256:e692418e4cbaf90ca69d05a66403747baa33ee08806650b51fab815ad7fc331f",
199 Size: 32654,
200 MediaType: DockerV2Schema2ForeignLayerMediaType,
201 CompressionOperation: types.Decompress,
202 },
203 })
204 assert.Nil(t, err)
205
206 updatedManifestBytes, err := origManifest.Serialize()
207 assert.Nil(t, err)
208
209 bytes, err = ioutil.ReadFile("fixtures/v2s2.nondistributable.manifest.json")
210 assert.Nil(t, err)
211
212 expectedManifest, err := Schema2FromManifest(bytes)
213 assert.Nil(t, err)
214
215 expectedManifestBytes, err := expectedManifest.Serialize()
216 assert.Nil(t, err)
217
218 assert.Equal(t, string(expectedManifestBytes), string(updatedManifestBytes))
219 }
0 {
1 "schemaVersion": 2,
2 "config": {
3 "mediaType": "application/vnd.oci.image.config.v1+json",
4 "size": 7023,
5 "digest": "sha256:b5b2b2c507a0944348e0303114d8d93aaaa081732b86451d9bce1f432a537bc7"
6 },
7 "layers": [
8 {
9 "mediaType": "application/vnd.oci.image.layer.v1.tar+unknown",
10 "size": 32654,
11 "digest": "sha256:e692418e4cbaf90ca69d05a66403747baa33ee08806650b51fab815ad7fc331f"
12 },
13 {
14 "mediaType": "application/vnd.oci.image.layer.v1.tar+gzip",
15 "size": 16724,
16 "digest": "sha256:3c3a4604a545cdc127456d94e421cd355bca5b528f4a9c1905b15da2eb4a4c6b"
17 },
18 {
19 "mediaType": "application/vnd.oci.image.layer.v1.tar+gzip",
20 "size": 73109,
21 "digest": "sha256:ec4b8955958665577945c89419d1af06b5f7636b4ac3da7f12184802ad867736"
22 }
23 ],
24 "annotations": {
25 "com.example.key1": "value1",
26 "com.example.key2": "value2"
27 }
28 }
0 {
1 "schemaVersion": 2,
2 "config": {
3 "mediaType": "application/vnd.oci.image.config.v1+json",
4 "size": 7023,
5 "digest": "sha256:b5b2b2c507a0944348e0303114d8d93aaaa081732b86451d9bce1f432a537bc7"
6 },
7 "layers": [
8 {
9 "mediaType": "application/vnd.oci.image.layer.nondistributable.v1.tar+gzip",
10 "size": 32654,
11 "digest": "sha256:e692418e4cbaf90ca69d05a66403747baa33ee08806650b51fab815ad7fc331f"
12 }
13 ],
14 "annotations": {
15 "com.example.key1": "value1",
16 "com.example.key2": "value2"
17 }
18 }
0 {
1 "schemaVersion": 2,
2 "config": {
3 "mediaType": "application/vnd.oci.image.config.v1+json",
4 "size": 7023,
5 "digest": "sha256:b5b2b2c507a0944348e0303114d8d93aaaa081732b86451d9bce1f432a537bc7"
6 },
7 "layers": [
8 {
9 "mediaType": "application/vnd.oci.image.layer.nondistributable.v1.tar",
10 "size": 32654,
11 "digest": "sha256:e692418e4cbaf90ca69d05a66403747baa33ee08806650b51fab815ad7fc331f"
12 }
13 ],
14 "annotations": {
15 "com.example.key1": "value1",
16 "com.example.key2": "value2"
17 }
18 }
0 {
1 "schemaVersion": 2,
2 "config": {
3 "mediaType": "application/vnd.oci.image.config.v1+json",
4 "size": 7023,
5 "digest": "sha256:b5b2b2c507a0944348e0303114d8d93aaaa081732b86451d9bce1f432a537bc7"
6 },
7 "layers": [
8 {
9 "mediaType": "application/vnd.oci.image.layer.nondistributable.v1.tar+zstd",
10 "size": 32654,
11 "digest": "sha256:e692418e4cbaf90ca69d05a66403747baa33ee08806650b51fab815ad7fc331f"
12 }
13 ],
14 "annotations": {
15 "com.example.key1": "value1",
16 "com.example.key2": "value2"
17 }
18 }
0 {
1 "schemaVersion": 2,
2 "config": {
3 "mediaType": "application/vnd.oci.image.config.v1+json",
4 "size": 7023,
5 "digest": "sha256:b5b2b2c507a0944348e0303114d8d93aaaa081732b86451d9bce1f432a537bc7"
6 },
7 "layers": [
8 {
9 "mediaType": "application/vnd.oci.image.layer.v1.tar",
10 "size": 32654,
11 "digest": "sha256:e692418e4cbaf90ca69d05a66403747baa33ee08806650b51fab815ad7fc331f"
12 },
13 {
14 "mediaType": "application/vnd.oci.image.layer.v1.tar",
15 "size": 16724,
16 "digest": "sha256:3c3a4604a545cdc127456d94e421cd355bca5b528f4a9c1905b15da2eb4a4c6b"
17 },
18 {
19 "mediaType": "application/vnd.oci.image.layer.v1.tar",
20 "size": 73109,
21 "digest": "sha256:ec4b8955958665577945c89419d1af06b5f7636b4ac3da7f12184802ad867736"
22 }
23 ],
24 "annotations": {
25 "com.example.key1": "value1",
26 "com.example.key2": "value2"
27 }
28 }
0 {
1 "schemaVersion": 2,
2 "config": {
3 "mediaType": "application/vnd.oci.image.config.v1+json",
4 "size": 7023,
5 "digest": "sha256:b5b2b2c507a0944348e0303114d8d93aaaa081732b86451d9bce1f432a537bc7"
6 },
7 "layers": [
8 {
9 "mediaType": "application/vnd.oci.image.layer.v1.tar+zstd",
10 "size": 32654,
11 "digest": "sha256:e692418e4cbaf90ca69d05a66403747baa33ee08806650b51fab815ad7fc331f"
12 },
13 {
14 "mediaType": "application/vnd.oci.image.layer.v1.tar+zstd",
15 "size": 16724,
16 "digest": "sha256:3c3a4604a545cdc127456d94e421cd355bca5b528f4a9c1905b15da2eb4a4c6b"
17 },
18 {
19 "mediaType": "application/vnd.oci.image.layer.v1.tar+zstd",
20 "size": 73109,
21 "digest": "sha256:ec4b8955958665577945c89419d1af06b5f7636b4ac3da7f12184802ad867736"
22 }
23 ],
24 "annotations": {
25 "com.example.key1": "value1",
26 "com.example.key2": "value2"
27 }
28 }
0 {
1 "schemaVersion": 2,
2 "mediaType": "application/vnd.docker.distribution.manifest.v2+json",
3 "config": {
4 "mediaType": "application/vnd.oci.image.config.v1+json",
5 "size": 7023,
6 "digest": "sha256:b5b2b2c507a0944348e0303114d8d93aaaa081732b86451d9bce1f432a537bc7"
7 },
8 "layers": [
9 {
10 "mediaType": "application/vnd.docker.image.rootfs.foreign.diff.tar.gzip",
11 "size": 32654,
12 "digest": "sha256:e692418e4cbaf90ca69d05a66403747baa33ee08806650b51fab815ad7fc331f"
13 }
14 ],
15 "annotations": {
16 "com.example.key1": "value1",
17 "com.example.key2": "value2"
18 }
19 }
0 {
1 "schemaVersion": 2,
2 "mediaType": "application/vnd.docker.distribution.manifest.v2+json",
3 "config": {
4 "mediaType": "application/vnd.oci.image.config.v1+json",
5 "size": 7023,
6 "digest": "sha256:b5b2b2c507a0944348e0303114d8d93aaaa081732b86451d9bce1f432a537bc7"
7 },
8 "layers": [
9 {
10 "mediaType": "application/vnd.docker.image.rootfs.foreign.diff.tar",
11 "size": 32654,
12 "digest": "sha256:e692418e4cbaf90ca69d05a66403747baa33ee08806650b51fab815ad7fc331f"
13 }
14 ],
15 "annotations": {
16 "com.example.key1": "value1",
17 "com.example.key2": "value2"
18 }
19 }
0 {
1 "schemaVersion": 2,
2 "mediaType": "application/vnd.docker.distribution.manifest.v2+json",
3 "config": {
4 "mediaType": "application/vnd.docker.container.image.v1+json",
5 "size": 7023,
6 "digest": "sha256:b5b2b2c507a0944348e0303114d8d93aaaa081732b86451d9bce1f432a537bc7"
7 },
8 "layers": [
9 {
10 "mediaType": "application/vnd.docker.image.rootfs.diff.tar",
11 "size": 32654,
12 "digest": "sha256:e692418e4cbaf90ca69d05a66403747baa33ee08806650b51fab815ad7fc331f"
13 },
14 {
15 "mediaType": "application/vnd.docker.image.rootfs.diff.tar",
16 "size": 16724,
17 "digest": "sha256:3c3a4604a545cdc127456d94e421cd355bca5b528f4a9c1905b15da2eb4a4c6b"
18 },
19 {
20 "mediaType": "application/vnd.docker.image.rootfs.diff.tar",
21 "size": 73109,
22 "digest": "sha256:ec4b8955958665577945c89419d1af06b5f7636b4ac3da7f12184802ad867736"
23 }
24 ]
25 }
1111
1212 // FIXME: Should we just use docker/distribution and docker/docker implementations directly?
1313
14 // FIXME(runcom, mitr): should we havea mediatype pkg??
14 // FIXME(runcom, mitr): should we have a mediatype pkg??
1515 const (
1616 // DockerV2Schema1MediaType MIME type represents Docker manifest schema 1
1717 DockerV2Schema1MediaType = "application/vnd.docker.distribution.manifest.v1+json"
2323 DockerV2Schema2ConfigMediaType = "application/vnd.docker.container.image.v1+json"
2424 // DockerV2Schema2LayerMediaType is the MIME type used for schema 2 layers.
2525 DockerV2Schema2LayerMediaType = "application/vnd.docker.image.rootfs.diff.tar.gzip"
26 // DockerV2SchemaLayerMediaTypeUncompressed is the mediaType used for uncompressed layers.
27 DockerV2SchemaLayerMediaTypeUncompressed = "application/vnd.docker.image.rootfs.diff.tar"
2628 // DockerV2ListMediaType MIME type represents Docker manifest schema 2 list
2729 DockerV2ListMediaType = "application/vnd.docker.distribution.manifest.list.v2+json"
2830 // DockerV2Schema2ForeignLayerMediaType is the MIME type used for schema 2 foreign layers.
29 DockerV2Schema2ForeignLayerMediaType = "application/vnd.docker.image.rootfs.foreign.diff.tar.gzip"
31 DockerV2Schema2ForeignLayerMediaType = "application/vnd.docker.image.rootfs.foreign.diff.tar"
32 // DockerV2Schema2ForeignLayerMediaType is the MIME type used for gzippped schema 2 foreign layers.
33 DockerV2Schema2ForeignLayerMediaTypeGzip = "application/vnd.docker.image.rootfs.foreign.diff.tar.gzip"
3034 )
35
36 // SupportedSchema2MediaType checks if the specified string is a supported Docker v2s2 media type.
37 func SupportedSchema2MediaType(m string) error {
38 switch m {
39 case DockerV2ListMediaType, DockerV2Schema1MediaType, DockerV2Schema1SignedMediaType, DockerV2Schema2ConfigMediaType, DockerV2Schema2ForeignLayerMediaType, DockerV2Schema2ForeignLayerMediaTypeGzip, DockerV2Schema2LayerMediaType, DockerV2Schema2MediaType, DockerV2SchemaLayerMediaTypeUncompressed:
40 return nil
41 default:
42 return fmt.Errorf("unsupported docker v2s2 media type: %q", m)
43 }
44 }
3145
3246 // DefaultRequestedManifestMIMETypes is a list of MIME types a types.ImageSource
3347 // should request from the backend unless directed otherwise.
11
22 import (
33 "encoding/json"
4
4 "fmt"
5
6 "github.com/containers/image/pkg/compression"
57 "github.com/containers/image/types"
68 "github.com/opencontainers/go-digest"
79 "github.com/opencontainers/image-spec/specs-go"
810 imgspecv1 "github.com/opencontainers/image-spec/specs-go/v1"
911 "github.com/pkg/errors"
12 "github.com/sirupsen/logrus"
1013 )
1114
1215 // BlobInfoFromOCI1Descriptor returns a types.BlobInfo based on the input OCI1 descriptor.
2629 imgspecv1.Manifest
2730 }
2831
32 // SupportedOCI1MediaType checks if the specified string is a supported OCI1 media type.
33 func SupportedOCI1MediaType(m string) error {
34 switch m {
35 case imgspecv1.MediaTypeDescriptor, imgspecv1.MediaTypeImageConfig, imgspecv1.MediaTypeImageLayer, imgspecv1.MediaTypeImageLayerGzip, imgspecv1.MediaTypeImageLayerNonDistributable, imgspecv1.MediaTypeImageLayerNonDistributableGzip, imgspecv1.MediaTypeImageLayerNonDistributableZstd, imgspecv1.MediaTypeImageLayerZstd, imgspecv1.MediaTypeImageManifest, imgspecv1.MediaTypeLayoutHeader:
36 return nil
37 default:
38 return fmt.Errorf("unsupported OCIv1 media type: %q", m)
39 }
40 }
41
2942 // OCI1FromManifest creates an OCI1 manifest instance from a manifest blob.
3043 func OCI1FromManifest(manifest []byte) (*OCI1, error) {
3144 oci1 := OCI1{}
3245 if err := json.Unmarshal(manifest, &oci1); err != nil {
3346 return nil, err
47 }
48 // Check manifest's and layers' media types.
49 if err := SupportedOCI1MediaType(oci1.Config.MediaType); err != nil {
50 return nil, err
51 }
52 for _, layer := range oci1.Layers {
53 if err := SupportedOCI1MediaType(layer.MediaType); err != nil {
54 return nil, err
55 }
3456 }
3557 return &oci1, nil
3658 }
80102 original := m.Layers
81103 m.Layers = make([]imgspecv1.Descriptor, len(layerInfos))
82104 for i, info := range layerInfos {
83 m.Layers[i].MediaType = original[i].MediaType
105 // First make sure we support the media type of the original layer.
106 if err := SupportedOCI1MediaType(original[i].MediaType); err != nil {
107 return fmt.Errorf("Error preparing updated manifest: unknown media type of original layer: %q", original[i].MediaType)
108 }
109
110 // Set the correct media types based on the specified compression
111 // operation, the desired compression algorithm AND the original media
112 // type.
113 switch info.CompressionOperation {
114 case types.PreserveOriginal:
115 // Keep the original media type.
116 m.Layers[i].MediaType = original[i].MediaType
117
118 case types.Decompress:
119 // Decompress the original media type and check if it was
120 // non-distributable one or not.
121 switch original[i].MediaType {
122 case imgspecv1.MediaTypeImageLayerNonDistributableGzip, imgspecv1.MediaTypeImageLayerNonDistributableZstd:
123 m.Layers[i].MediaType = imgspecv1.MediaTypeImageLayerNonDistributable
124 default:
125 m.Layers[i].MediaType = imgspecv1.MediaTypeImageLayer
126 }
127
128 case types.Compress:
129 if info.CompressionAlgorithm == nil {
130 logrus.Debugf("Error preparing updated manifest: blob %q was compressed but does not specify by which algorithm: falling back to use the original blob", info.Digest)
131 m.Layers[i].MediaType = original[i].MediaType
132 break
133 }
134 // Compress the original media type and set the new one based on
135 // that type (distributable or not) and the specified compression
136 // algorithm. Throw an error if the algorithm is not supported.
137 switch info.CompressionAlgorithm.Name() {
138 case compression.Gzip.Name():
139 switch original[i].MediaType {
140 case imgspecv1.MediaTypeImageLayerNonDistributable, imgspecv1.MediaTypeImageLayerNonDistributableZstd:
141 m.Layers[i].MediaType = imgspecv1.MediaTypeImageLayerNonDistributableGzip
142
143 default:
144 m.Layers[i].MediaType = imgspecv1.MediaTypeImageLayerGzip
145 }
146
147 case compression.Zstd.Name():
148 switch original[i].MediaType {
149 case imgspecv1.MediaTypeImageLayerNonDistributable, imgspecv1.MediaTypeImageLayerNonDistributableGzip:
150 m.Layers[i].MediaType = imgspecv1.MediaTypeImageLayerNonDistributableZstd
151
152 default:
153 m.Layers[i].MediaType = imgspecv1.MediaTypeImageLayerZstd
154 }
155
156 default:
157 return fmt.Errorf("Error preparing updated manifest: unknown compression algorithm %q for layer %q", info.CompressionAlgorithm.Name(), info.Digest)
158 }
159
160 default:
161 return fmt.Errorf("Error preparing updated manifest: unknown compression operation (%d) for layer %q", info.CompressionOperation, info.Digest)
162 }
84163 m.Layers[i].Digest = info.Digest
85164 m.Layers[i].Size = info.Size
86165 m.Layers[i].Annotations = info.Annotations
0 package manifest
1
2 import (
3 "io/ioutil"
4 "testing"
5
6 "github.com/containers/image/pkg/compression"
7 "github.com/containers/image/types"
8 imgspecv1 "github.com/opencontainers/image-spec/specs-go/v1"
9 "github.com/stretchr/testify/assert"
10 )
11
12 func TestSupportedOCI1MediaType(t *testing.T) {
13 type testData struct {
14 m string
15 mustFail bool
16 }
17 data := []testData{
18 {
19 imgspecv1.MediaTypeDescriptor,
20 false,
21 },
22 {
23 imgspecv1.MediaTypeImageConfig,
24 false,
25 },
26 {
27 imgspecv1.MediaTypeImageLayer,
28 false,
29 },
30 {
31 imgspecv1.MediaTypeImageLayerGzip,
32 false,
33 },
34 {
35 imgspecv1.MediaTypeImageLayerNonDistributable,
36 false,
37 },
38 {
39 imgspecv1.MediaTypeImageLayerNonDistributableGzip,
40 false,
41 },
42 {
43 imgspecv1.MediaTypeImageLayerNonDistributableZstd,
44 false,
45 },
46 {
47 imgspecv1.MediaTypeImageLayerZstd,
48 false,
49 },
50 {
51 imgspecv1.MediaTypeImageManifest,
52 false,
53 },
54 {
55 imgspecv1.MediaTypeLayoutHeader,
56 false,
57 },
58 {
59 "application/vnd.oci.image.layer.nondistributable.v1.tar+unknown",
60 true,
61 },
62 }
63 for _, d := range data {
64 err := SupportedOCI1MediaType(d.m)
65 if d.mustFail {
66 assert.NotNil(t, err)
67 } else {
68 assert.Nil(t, err)
69 }
70 }
71 }
72
73 func TestInvalidOCI1MediaType(t *testing.T) {
74 bytes, err := ioutil.ReadFile("fixtures/ociv1.invalid.mediatype.manifest.json")
75 assert.Nil(t, err)
76
77 _, err = OCI1FromManifest(bytes)
78 assert.NotNil(t, err)
79 }
80
81 func TestUpdateLayerInfosOCIGzipToZstd(t *testing.T) {
82 bytes, err := ioutil.ReadFile("fixtures/ociv1.manifest.json")
83 assert.Nil(t, err)
84
85 manifest, err := OCI1FromManifest(bytes)
86 assert.Nil(t, err)
87
88 err = manifest.UpdateLayerInfos([]types.BlobInfo{
89 {
90 Digest: "sha256:e692418e4cbaf90ca69d05a66403747baa33ee08806650b51fab815ad7fc331f",
91 Size: 32654,
92 MediaType: imgspecv1.MediaTypeImageLayerGzip,
93 CompressionOperation: types.Compress,
94 CompressionAlgorithm: &compression.Zstd,
95 },
96 {
97 Digest: "sha256:3c3a4604a545cdc127456d94e421cd355bca5b528f4a9c1905b15da2eb4a4c6b",
98 Size: 16724,
99 MediaType: imgspecv1.MediaTypeImageLayerGzip,
100 CompressionOperation: types.Compress,
101 CompressionAlgorithm: &compression.Zstd,
102 },
103 {
104 Digest: "sha256:ec4b8955958665577945c89419d1af06b5f7636b4ac3da7f12184802ad867736",
105 Size: 73109,
106 MediaType: imgspecv1.MediaTypeImageLayerGzip,
107 CompressionOperation: types.Compress,
108 CompressionAlgorithm: &compression.Zstd,
109 },
110 })
111 assert.Nil(t, err)
112
113 updatedManifestBytes, err := manifest.Serialize()
114 assert.Nil(t, err)
115
116 bytes, err = ioutil.ReadFile("fixtures/ociv1.zstd.manifest.json")
117 assert.Nil(t, err)
118
119 expectedManifest, err := OCI1FromManifest(bytes)
120 assert.Nil(t, err)
121
122 expectedManifestBytes, err := expectedManifest.Serialize()
123 assert.Nil(t, err)
124
125 assert.Equal(t, string(expectedManifestBytes), string(updatedManifestBytes))
126 }
127
128 func TestUpdateLayerInfosOCIZstdToGzip(t *testing.T) {
129 bytes, err := ioutil.ReadFile("fixtures/ociv1.zstd.manifest.json")
130 assert.Nil(t, err)
131
132 manifest, err := OCI1FromManifest(bytes)
133 assert.Nil(t, err)
134
135 err = manifest.UpdateLayerInfos([]types.BlobInfo{
136 {
137 Digest: "sha256:e692418e4cbaf90ca69d05a66403747baa33ee08806650b51fab815ad7fc331f",
138 Size: 32654,
139 MediaType: imgspecv1.MediaTypeImageLayerZstd,
140 CompressionOperation: types.Compress,
141 CompressionAlgorithm: &compression.Gzip,
142 },
143 {
144 Digest: "sha256:3c3a4604a545cdc127456d94e421cd355bca5b528f4a9c1905b15da2eb4a4c6b",
145 Size: 16724,
146 MediaType: imgspecv1.MediaTypeImageLayerZstd,
147 CompressionOperation: types.Compress,
148 CompressionAlgorithm: &compression.Gzip,
149 },
150 {
151 Digest: "sha256:ec4b8955958665577945c89419d1af06b5f7636b4ac3da7f12184802ad867736",
152 Size: 73109,
153 MediaType: imgspecv1.MediaTypeImageLayerZstd,
154 CompressionOperation: types.Compress,
155 CompressionAlgorithm: &compression.Gzip,
156 },
157 })
158 assert.Nil(t, err)
159
160 updatedManifestBytes, err := manifest.Serialize()
161 assert.Nil(t, err)
162
163 bytes, err = ioutil.ReadFile("fixtures/ociv1.manifest.json")
164 assert.Nil(t, err)
165
166 expectedManifest, err := OCI1FromManifest(bytes)
167 assert.Nil(t, err)
168
169 expectedManifestBytes, err := expectedManifest.Serialize()
170 assert.Nil(t, err)
171
172 assert.Equal(t, string(expectedManifestBytes), string(updatedManifestBytes))
173 }
174
175 func TestUpdateLayerInfosOCIZstdToUncompressed(t *testing.T) {
176 bytes, err := ioutil.ReadFile("fixtures/ociv1.zstd.manifest.json")
177 assert.Nil(t, err)
178
179 manifest, err := OCI1FromManifest(bytes)
180 assert.Nil(t, err)
181
182 err = manifest.UpdateLayerInfos([]types.BlobInfo{
183 {
184 Digest: "sha256:e692418e4cbaf90ca69d05a66403747baa33ee08806650b51fab815ad7fc331f",
185 Size: 32654,
186 MediaType: imgspecv1.MediaTypeImageLayerZstd,
187 CompressionOperation: types.Decompress,
188 },
189 {
190 Digest: "sha256:3c3a4604a545cdc127456d94e421cd355bca5b528f4a9c1905b15da2eb4a4c6b",
191 Size: 16724,
192 MediaType: imgspecv1.MediaTypeImageLayerZstd,
193 CompressionOperation: types.Decompress,
194 },
195 {
196 Digest: "sha256:ec4b8955958665577945c89419d1af06b5f7636b4ac3da7f12184802ad867736",
197 Size: 73109,
198 MediaType: imgspecv1.MediaTypeImageLayerZstd,
199 CompressionOperation: types.Decompress,
200 },
201 })
202 assert.Nil(t, err)
203
204 updatedManifestBytes, err := manifest.Serialize()
205 assert.Nil(t, err)
206
207 bytes, err = ioutil.ReadFile("fixtures/ociv1.uncompressed.manifest.json")
208 assert.Nil(t, err)
209
210 expectedManifest, err := OCI1FromManifest(bytes)
211 assert.Nil(t, err)
212
213 expectedManifestBytes, err := expectedManifest.Serialize()
214 assert.Nil(t, err)
215
216 assert.Equal(t, string(expectedManifestBytes), string(updatedManifestBytes))
217 }
218
219 func TestUpdateLayerInfosInvalidCompressionOperation(t *testing.T) {
220 bytes, err := ioutil.ReadFile("fixtures/ociv1.zstd.manifest.json")
221 assert.Nil(t, err)
222
223 manifest, err := OCI1FromManifest(bytes)
224 assert.Nil(t, err)
225
226 err = manifest.UpdateLayerInfos([]types.BlobInfo{
227 {
228 Digest: "sha256:e692418e4cbaf90ca69d05a66403747baa33ee08806650b51fab815ad7fc331f",
229 Size: 32654,
230 MediaType: imgspecv1.MediaTypeImageLayerZstd,
231 CompressionOperation: types.Compress,
232 CompressionAlgorithm: &compression.Gzip,
233 },
234 {
235 Digest: "sha256:3c3a4604a545cdc127456d94e421cd355bca5b528f4a9c1905b15da2eb4a4c6b",
236 Size: 16724,
237 MediaType: imgspecv1.MediaTypeImageLayerZstd,
238 CompressionOperation: 42, // MUST fail here
239 CompressionAlgorithm: &compression.Gzip,
240 },
241 {
242 Digest: "sha256:ec4b8955958665577945c89419d1af06b5f7636b4ac3da7f12184802ad867736",
243 Size: 73109,
244 MediaType: imgspecv1.MediaTypeImageLayerZstd,
245 CompressionOperation: types.Compress,
246 CompressionAlgorithm: &compression.Gzip,
247 },
248 })
249 assert.NotNil(t, err)
250 }
251
252 func TestUpdateLayerInfosInvalidCompressionAlgorithm(t *testing.T) {
253 bytes, err := ioutil.ReadFile("fixtures/ociv1.zstd.manifest.json")
254 assert.Nil(t, err)
255
256 manifest, err := OCI1FromManifest(bytes)
257 assert.Nil(t, err)
258
259 customCompression := compression.Algorithm{}
260 err = manifest.UpdateLayerInfos([]types.BlobInfo{
261 {
262 Digest: "sha256:e692418e4cbaf90ca69d05a66403747baa33ee08806650b51fab815ad7fc331f",
263 Size: 32654,
264 MediaType: imgspecv1.MediaTypeImageLayerZstd,
265 CompressionOperation: types.Compress,
266 CompressionAlgorithm: &compression.Gzip,
267 },
268 {
269 Digest: "sha256:3c3a4604a545cdc127456d94e421cd355bca5b528f4a9c1905b15da2eb4a4c6b",
270 Size: 16724,
271 MediaType: imgspecv1.MediaTypeImageLayerZstd,
272 CompressionOperation: 42,
273 CompressionAlgorithm: &compression.Gzip,
274 },
275 {
276 Digest: "sha256:ec4b8955958665577945c89419d1af06b5f7636b4ac3da7f12184802ad867736",
277 Size: 73109,
278 MediaType: imgspecv1.MediaTypeImageLayerZstd,
279 CompressionOperation: types.Compress,
280 CompressionAlgorithm: &customCompression, // MUST fail here
281 },
282 })
283 assert.NotNil(t, err)
284 }
285
286 func TestUpdateLayerInfosOCIGzipToUncompressed(t *testing.T) {
287 bytes, err := ioutil.ReadFile("fixtures/ociv1.manifest.json")
288 assert.Nil(t, err)
289
290 manifest, err := OCI1FromManifest(bytes)
291 assert.Nil(t, err)
292
293 err = manifest.UpdateLayerInfos([]types.BlobInfo{
294 {
295 Digest: "sha256:e692418e4cbaf90ca69d05a66403747baa33ee08806650b51fab815ad7fc331f",
296 Size: 32654,
297 MediaType: imgspecv1.MediaTypeImageLayerGzip,
298 CompressionOperation: types.Decompress,
299 },
300 {
301 Digest: "sha256:3c3a4604a545cdc127456d94e421cd355bca5b528f4a9c1905b15da2eb4a4c6b",
302 Size: 16724,
303 MediaType: imgspecv1.MediaTypeImageLayerGzip,
304 CompressionOperation: types.Decompress,
305 },
306 {
307 Digest: "sha256:ec4b8955958665577945c89419d1af06b5f7636b4ac3da7f12184802ad867736",
308 Size: 73109,
309 MediaType: imgspecv1.MediaTypeImageLayerGzip,
310 CompressionOperation: types.Decompress,
311 },
312 })
313 assert.Nil(t, err)
314
315 updatedManifestBytes, err := manifest.Serialize()
316 assert.Nil(t, err)
317
318 bytes, err = ioutil.ReadFile("fixtures/ociv1.uncompressed.manifest.json")
319 assert.Nil(t, err)
320
321 expectedManifest, err := OCI1FromManifest(bytes)
322 assert.Nil(t, err)
323
324 expectedManifestBytes, err := expectedManifest.Serialize()
325 assert.Nil(t, err)
326
327 assert.Equal(t, string(expectedManifestBytes), string(updatedManifestBytes))
328 }
329
330 func TestUpdateLayerInfosOCINondistributableToGzip(t *testing.T) {
331 bytes, err := ioutil.ReadFile("fixtures/ociv1.nondistributable.manifest.json")
332 assert.Nil(t, err)
333
334 manifest, err := OCI1FromManifest(bytes)
335 assert.Nil(t, err)
336
337 err = manifest.UpdateLayerInfos([]types.BlobInfo{
338 {
339 Digest: "sha256:e692418e4cbaf90ca69d05a66403747baa33ee08806650b51fab815ad7fc331f",
340 Size: 32654,
341 MediaType: imgspecv1.MediaTypeImageLayerGzip,
342 CompressionOperation: types.Compress,
343 CompressionAlgorithm: &compression.Gzip,
344 },
345 })
346 assert.Nil(t, err)
347
348 updatedManifestBytes, err := manifest.Serialize()
349 assert.Nil(t, err)
350
351 bytes, err = ioutil.ReadFile("fixtures/ociv1.nondistributable.gzip.manifest.json")
352 assert.Nil(t, err)
353
354 expectedManifest, err := OCI1FromManifest(bytes)
355 assert.Nil(t, err)
356
357 expectedManifestBytes, err := expectedManifest.Serialize()
358 assert.Nil(t, err)
359
360 assert.Equal(t, string(expectedManifestBytes), string(updatedManifestBytes))
361 }
362
363 func TestUpdateLayerInfosOCINondistributableToZstd(t *testing.T) {
364 bytes, err := ioutil.ReadFile("fixtures/ociv1.nondistributable.manifest.json")
365 assert.Nil(t, err)
366
367 manifest, err := OCI1FromManifest(bytes)
368 assert.Nil(t, err)
369
370 err = manifest.UpdateLayerInfos([]types.BlobInfo{
371 {
372 Digest: "sha256:e692418e4cbaf90ca69d05a66403747baa33ee08806650b51fab815ad7fc331f",
373 Size: 32654,
374 MediaType: imgspecv1.MediaTypeImageLayerGzip,
375 CompressionOperation: types.Compress,
376 CompressionAlgorithm: &compression.Zstd,
377 },
378 })
379 assert.Nil(t, err)
380
381 updatedManifestBytes, err := manifest.Serialize()
382 assert.Nil(t, err)
383
384 bytes, err = ioutil.ReadFile("fixtures/ociv1.nondistributable.zstd.manifest.json")
385 assert.Nil(t, err)
386
387 expectedManifest, err := OCI1FromManifest(bytes)
388 assert.Nil(t, err)
389
390 expectedManifestBytes, err := expectedManifest.Serialize()
391 assert.Nil(t, err)
392
393 assert.Equal(t, string(expectedManifestBytes), string(updatedManifestBytes))
394 }
395
396 func TestUpdateLayerInfosOCINondistributableGzipToUncompressed(t *testing.T) {
397 bytes, err := ioutil.ReadFile("fixtures/ociv1.nondistributable.gzip.manifest.json")
398 assert.Nil(t, err)
399
400 manifest, err := OCI1FromManifest(bytes)
401 assert.Nil(t, err)
402
403 err = manifest.UpdateLayerInfos([]types.BlobInfo{
404 {
405 Digest: "sha256:e692418e4cbaf90ca69d05a66403747baa33ee08806650b51fab815ad7fc331f",
406 Size: 32654,
407 MediaType: imgspecv1.MediaTypeImageLayerGzip,
408 CompressionOperation: types.Decompress,
409 },
410 })
411 assert.Nil(t, err)
412
413 updatedManifestBytes, err := manifest.Serialize()
414 assert.Nil(t, err)
415
416 bytes, err = ioutil.ReadFile("fixtures/ociv1.nondistributable.manifest.json")
417 assert.Nil(t, err)
418
419 expectedManifest, err := OCI1FromManifest(bytes)
420 assert.Nil(t, err)
421
422 expectedManifestBytes, err := expectedManifest.Serialize()
423 assert.Nil(t, err)
424
425 assert.Equal(t, string(expectedManifestBytes), string(updatedManifestBytes))
426 }
1111 "github.com/sirupsen/logrus"
1212 "github.com/ulikunitz/xz"
1313 )
14
15 // Algorithm is a compression algorithm that can be used for CompressStream.
16 type Algorithm struct {
17 name string
18 prefix []byte
19 decompressor DecompressorFunc
20 compressor compressorFunc
21 }
22
23 var (
24 // Gzip compression.
25 Gzip = Algorithm{"gzip", []byte{0x1F, 0x8B, 0x08}, GzipDecompressor, gzipCompressor}
26 // Bzip2 compression.
27 Bzip2 = Algorithm{"bzip2", []byte{0x42, 0x5A, 0x68}, Bzip2Decompressor, bzip2Compressor}
28 // Xz compression.
29 Xz = Algorithm{"Xz", []byte{0xFD, 0x37, 0x7A, 0x58, 0x5A, 0x00}, XzDecompressor, xzCompressor}
30 // Zstd compression.
31 Zstd = Algorithm{"zstd", []byte{0x28, 0xb5, 0x2f, 0xfd}, ZstdDecompressor, zstdCompressor}
32
33 compressionAlgorithms = map[string]Algorithm{
34 Gzip.name: Gzip,
35 Bzip2.name: Bzip2,
36 Xz.name: Xz,
37 Zstd.name: Zstd,
38 }
39 )
40
41 // Name returns the name for the compression algorithm.
42 func (c Algorithm) Name() string {
43 return c.name
44 }
45
46 // AlgorithmByName returns the compressor by its name
47 func AlgorithmByName(name string) (Algorithm, error) {
48 algorithm, ok := compressionAlgorithms[name]
49 if ok {
50 return algorithm, nil
51 }
52 return Algorithm{}, fmt.Errorf("cannot find compressor for %q", name)
53 }
1454
1555 // DecompressorFunc returns the decompressed stream, given a compressed stream.
1656 // The caller must call Close() on the decompressed stream (even if the compressed input stream does not need closing!).
5797 return xz.NewWriter(r)
5898 }
5999
60 // Algorithm is a compression algorithm that can be used for CompressStream.
61 type Algorithm struct {
62 name string
63 prefix []byte
64 decompressor DecompressorFunc
65 compressor compressorFunc
66 }
67
68 // Name returns the name for the compression algorithm.
69 func (c Algorithm) Name() string {
70 return c.name
71 }
72
73 // compressionAlgos is an internal implementation detail of DetectCompression
74 var compressionAlgos = []Algorithm{
75 {"gzip", []byte{0x1F, 0x8B, 0x08}, GzipDecompressor, gzipCompressor}, // gzip (RFC 1952)
76 {"bzip2", []byte{0x42, 0x5A, 0x68}, Bzip2Decompressor, bzip2Compressor}, // bzip2 (decompress.c:BZ2_decompress)
77 {"xz", []byte{0xFD, 0x37, 0x7A, 0x58, 0x5A, 0x00}, XzDecompressor, xzCompressor}, // xz (/usr/share/doc/xz/xz-file-format.txt)
78 {"zstd", []byte{0x28, 0xb5, 0x2f, 0xfd}, ZstdDecompressor, zstdCompressor}, // zstd (http://www.zstd.net)
79 }
80
81 // AlgorithmByName returns the compressor by its name
82 func AlgorithmByName(name string) (Algorithm, error) {
83 for _, c := range compressionAlgos {
84 if c.name == name {
85 return c, nil
86 }
87 }
88 return Algorithm{}, fmt.Errorf("cannot find compressor for %q", name)
89 }
90
91100 // CompressStream returns the compressor by its name
92101 func CompressStream(dest io.Writer, algo Algorithm, level *int) (io.WriteCloser, error) {
93102 return algo.compressor(dest, level)
107116
108117 var retAlgo Algorithm
109118 var decompressor DecompressorFunc
110 for _, algo := range compressionAlgos {
119 for _, algo := range compressionAlgorithms {
111120 if bytes.HasPrefix(buffer[:n], algo.prefix) {
112121 logrus.Debugf("Detected compression format %s", algo.name)
113122 retAlgo = algo
344344 }
345345
346346 func (s *storageImageDestination) DesiredLayerCompression() types.LayerCompression {
347 // We ultimately have to decompress layers to populate trees on disk,
348 // so callers shouldn't bother compressing them before handing them to
349 // us, if they're not already compressed.
347 // We ultimately have to decompress layers to populate trees on disk
348 // and need to explicitly ask for it here, so that the layers' MIME
349 // types can be set accordingly.
350350 return types.PreserveOriginal
351351 }
352352
77 "github.com/containers/image/docker/reference"
88 "github.com/containers/image/pkg/compression"
99 "github.com/opencontainers/go-digest"
10 "github.com/opencontainers/image-spec/specs-go/v1"
10 v1 "github.com/opencontainers/image-spec/specs-go/v1"
1111 )
1212
1313 // ImageTransport is a top-level namespace for ways to to store/load an image.
9090 DeleteImage(ctx context.Context, sys *SystemContext) error
9191 }
9292
93 // LayerCompression indicates if layers must be compressed, decompressed or preserved
94 type LayerCompression int
95
96 const (
97 // PreserveOriginal indicates the layer must be preserved, ie
98 // no compression or decompression.
99 PreserveOriginal LayerCompression = iota
100 // Decompress indicates the layer must be decompressed
101 Decompress
102 // Compress indicates the layer must be compressed
103 Compress
104 )
105
93106 // BlobInfo collects known information about a blob (layer/config).
94107 // In some situations, some fields may be unknown, in others they may be mandatory; documenting an “unknown” value here does not override that.
95108 type BlobInfo struct {
98111 URLs []string
99112 Annotations map[string]string
100113 MediaType string
114 // CompressionOperation is used in Image.UpdateLayerInfos to instruct
115 // whether the original layer should be preserved or (de)compressed. The
116 // field defaults to preserve the original layer.
117 CompressionOperation LayerCompression
118 // CompressionAlgorithm is used in Image.UpdateLayerInfos to set the correct
119 // MIME type for compressed layers (e.g., gzip or zstd). This field MUST be
120 // set when `CompressionOperation == Compress`.
121 CompressionAlgorithm *compression.Algorithm
101122 }
102123
103124 // BICTransportScope encapsulates transport-dependent representation of a “scope” where blobs are or are not present.
210231 // WARNING: The list may contain duplicates, and they are semantically relevant.
211232 LayerInfosForCopy(ctx context.Context) ([]BlobInfo, error)
212233 }
213
214 // LayerCompression indicates if layers must be compressed, decompressed or preserved
215 type LayerCompression int
216
217 const (
218 // PreserveOriginal indicates the layer must be preserved, ie
219 // no compression or decompression.
220 PreserveOriginal LayerCompression = iota
221 // Decompress indicates the layer must be decompressed
222 Decompress
223 // Compress indicates the layer must be compressed
224 Compress
225 )
226234
227235 // ImageDestination is a service, possibly remote (= slow), to store components of a single image.
228236 //