From 886a2ed1b33b422d922ea99f5dfe63566fb25d6c Mon Sep 17 00:00:00 2001 From: Nalin Dahyabhai Date: Mon, 29 Nov 2021 10:17:03 -0500 Subject: [PATCH] manifest.GuessMIMEType(): recognize self-described OCI manifests Newer versions of the OCI spec include the mediaType field in image manifests and image indexes, and if they include them, save ourselves some work by using that information. Signed-off-by: Nalin Dahyabhai --- go.mod | 2 +- go.sum | 2 ++ image/fixtures/oci1-all-media-types.json | 1 + image/fixtures/oci1.json | 1 + image/fixtures/oci1index.json | 1 + image/fixtures/schema1-to-oci1.json | 1 + .../schema2-all-media-types-to-oci1.json | 1 + image/fixtures/schema2-to-oci1.json | 1 + manifest/fixtures/ociv1.artifact.json | 1 + manifest/fixtures/ociv1.image.index.json | 1 + manifest/fixtures/ociv1.manifest.json | 1 + .../ociv1.nondistributable.gzip.manifest.json | 1 + .../ociv1.nondistributable.manifest.json | 1 + .../ociv1.nondistributable.zstd.manifest.json | 1 + .../fixtures/ociv1.uncompressed.manifest.json | 1 + manifest/fixtures/ociv1.zstd.manifest.json | 1 + manifest/fixtures/ociv1nomime.artifact.json | 9 ++++++ .../fixtures/ociv1nomime.image.index.json | 30 +++++++++++++++++++ manifest/fixtures/ociv1nomime.manifest.json | 29 ++++++++++++++++++ manifest/manifest.go | 9 +++--- manifest/manifest_test.go | 3 ++ manifest/oci.go | 1 + manifest/oci_index.go | 2 ++ 23 files changed, 96 insertions(+), 5 deletions(-) create mode 100644 manifest/fixtures/ociv1nomime.artifact.json create mode 100644 manifest/fixtures/ociv1nomime.image.index.json create mode 100644 manifest/fixtures/ociv1nomime.manifest.json diff --git a/go.mod b/go.mod index 80e152bb9..dea9207a2 100644 --- a/go.mod +++ b/go.mod @@ -26,7 +26,7 @@ require ( github.com/morikuni/aec v1.0.0 // indirect github.com/mtrmac/gpgme v0.1.2 github.com/opencontainers/go-digest v1.0.0 - github.com/opencontainers/image-spec v1.0.2-0.20210819154149-5ad6f50d6283 + github.com/opencontainers/image-spec v1.0.2-0.20211123152302-43a7dee1ec31 github.com/opencontainers/selinux v1.10.0 github.com/ostreedev/ostree-go v0.0.0-20190702140239-759a8c1ac913 github.com/pkg/errors v0.9.1 diff --git a/go.sum b/go.sum index d97a0b1c0..a4c9e408d 100644 --- a/go.sum +++ b/go.sum @@ -529,6 +529,8 @@ github.com/opencontainers/image-spec v1.0.0/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zM github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= github.com/opencontainers/image-spec v1.0.2-0.20210819154149-5ad6f50d6283 h1:TVzvdjOalkJBNkbpPVMAr4KV9QRf2IjfxdyxwAK78Gs= github.com/opencontainers/image-spec v1.0.2-0.20210819154149-5ad6f50d6283/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= +github.com/opencontainers/image-spec v1.0.2-0.20211123152302-43a7dee1ec31 h1:Wh4aR2I6JFwySre9m3iHJYuMnvUFE/HT6qAXozRWi/E= +github.com/opencontainers/image-spec v1.0.2-0.20211123152302-43a7dee1ec31/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= github.com/opencontainers/runc v0.0.0-20190115041553-12f6a991201f/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= github.com/opencontainers/runc v0.1.1/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= github.com/opencontainers/runc v1.0.0-rc8.0.20190926000215-3e425f80a8c9/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= diff --git a/image/fixtures/oci1-all-media-types.json b/image/fixtures/oci1-all-media-types.json index 1e57cfbe2..965539282 100644 --- a/image/fixtures/oci1-all-media-types.json +++ b/image/fixtures/oci1-all-media-types.json @@ -1,5 +1,6 @@ { "schemaVersion": 2, + "mediaType": "application/vnd.oci.image.manifest.v1+json", "config": { "mediaType": "application/vnd.oci.image.config.v1+json", "size": 4651, diff --git a/image/fixtures/oci1.json b/image/fixtures/oci1.json index dbac4a955..26efc23db 100644 --- a/image/fixtures/oci1.json +++ b/image/fixtures/oci1.json @@ -1,5 +1,6 @@ { "schemaVersion": 2, + "mediaType": "application/vnd.oci.image.manifest.v1+json", "config": { "mediaType": "application/vnd.oci.image.config.v1+json", "size": 5940, diff --git a/image/fixtures/oci1index.json b/image/fixtures/oci1index.json index 066f058db..a85b4d889 100644 --- a/image/fixtures/oci1index.json +++ b/image/fixtures/oci1index.json @@ -1,5 +1,6 @@ { "schemaVersion": 2, + "mediaType": "application/vnd.oci.image.index.v1+json", "manifests": [ { "mediaType": "application/vnd.oci.image.manifest.v1+json", diff --git a/image/fixtures/schema1-to-oci1.json b/image/fixtures/schema1-to-oci1.json index a16608b7f..0af1fbecf 100644 --- a/image/fixtures/schema1-to-oci1.json +++ b/image/fixtures/schema1-to-oci1.json @@ -1,5 +1,6 @@ { "schemaVersion": 2, + "mediaType": "application/vnd.oci.image.manifest.v1+json", "config": { "mediaType": "application/vnd.oci.image.config.v1+json", "size": -1, diff --git a/image/fixtures/schema2-all-media-types-to-oci1.json b/image/fixtures/schema2-all-media-types-to-oci1.json index 0ac99a87c..8287c48a1 100644 --- a/image/fixtures/schema2-all-media-types-to-oci1.json +++ b/image/fixtures/schema2-all-media-types-to-oci1.json @@ -1,5 +1,6 @@ { "schemaVersion": 2, + "mediaType": "application/vnd.oci.image.manifest.v1+json", "config": { "mediaType": "application/vnd.oci.image.config.v1+json", "size": 4651, diff --git a/image/fixtures/schema2-to-oci1.json b/image/fixtures/schema2-to-oci1.json index cd081b7d1..01dfda5cc 100644 --- a/image/fixtures/schema2-to-oci1.json +++ b/image/fixtures/schema2-to-oci1.json @@ -1,5 +1,6 @@ { "schemaVersion": 2, + "mediaType": "application/vnd.oci.image.manifest.v1+json", "config": { "mediaType": "application/vnd.oci.image.config.v1+json", "size": 4651, diff --git a/manifest/fixtures/ociv1.artifact.json b/manifest/fixtures/ociv1.artifact.json index 5091d1f61..a53807924 100644 --- a/manifest/fixtures/ociv1.artifact.json +++ b/manifest/fixtures/ociv1.artifact.json @@ -1,5 +1,6 @@ { "schemaVersion": 2, + "mediaType": "application/vnd.oci.image.manifest.v1+json", "config": { "mediaType": "application/vnd.oci.custom.artifact.config.v1+json", "digest": "", diff --git a/manifest/fixtures/ociv1.image.index.json b/manifest/fixtures/ociv1.image.index.json index 066f058db..a85b4d889 100644 --- a/manifest/fixtures/ociv1.image.index.json +++ b/manifest/fixtures/ociv1.image.index.json @@ -1,5 +1,6 @@ { "schemaVersion": 2, + "mediaType": "application/vnd.oci.image.index.v1+json", "manifests": [ { "mediaType": "application/vnd.oci.image.manifest.v1+json", diff --git a/manifest/fixtures/ociv1.manifest.json b/manifest/fixtures/ociv1.manifest.json index 1e1047ca7..7e2e2e827 100644 --- a/manifest/fixtures/ociv1.manifest.json +++ b/manifest/fixtures/ociv1.manifest.json @@ -1,5 +1,6 @@ { "schemaVersion": 2, + "mediaType": "application/vnd.oci.image.manifest.v1+json", "config": { "mediaType": "application/vnd.oci.image.config.v1+json", "size": 7023, diff --git a/manifest/fixtures/ociv1.nondistributable.gzip.manifest.json b/manifest/fixtures/ociv1.nondistributable.gzip.manifest.json index 2729e03a0..ba1d37741 100644 --- a/manifest/fixtures/ociv1.nondistributable.gzip.manifest.json +++ b/manifest/fixtures/ociv1.nondistributable.gzip.manifest.json @@ -1,5 +1,6 @@ { "schemaVersion": 2, + "mediaType": "application/vnd.oci.image.manifest.v1+json", "config": { "mediaType": "application/vnd.oci.image.config.v1+json", "size": 7023, diff --git a/manifest/fixtures/ociv1.nondistributable.manifest.json b/manifest/fixtures/ociv1.nondistributable.manifest.json index 73b20b9b5..6ed348998 100644 --- a/manifest/fixtures/ociv1.nondistributable.manifest.json +++ b/manifest/fixtures/ociv1.nondistributable.manifest.json @@ -1,5 +1,6 @@ { "schemaVersion": 2, + "mediaType": "application/vnd.oci.image.manifest.v1+json", "config": { "mediaType": "application/vnd.oci.image.config.v1+json", "size": 7023, diff --git a/manifest/fixtures/ociv1.nondistributable.zstd.manifest.json b/manifest/fixtures/ociv1.nondistributable.zstd.manifest.json index 948f6f0c5..85e2b4513 100644 --- a/manifest/fixtures/ociv1.nondistributable.zstd.manifest.json +++ b/manifest/fixtures/ociv1.nondistributable.zstd.manifest.json @@ -1,5 +1,6 @@ { "schemaVersion": 2, + "mediaType": "application/vnd.oci.image.manifest.v1+json", "config": { "mediaType": "application/vnd.oci.image.config.v1+json", "size": 7023, diff --git a/manifest/fixtures/ociv1.uncompressed.manifest.json b/manifest/fixtures/ociv1.uncompressed.manifest.json index 206165c96..e3a137c8a 100644 --- a/manifest/fixtures/ociv1.uncompressed.manifest.json +++ b/manifest/fixtures/ociv1.uncompressed.manifest.json @@ -1,5 +1,6 @@ { "schemaVersion": 2, + "mediaType": "application/vnd.oci.image.manifest.v1+json", "config": { "mediaType": "application/vnd.oci.image.config.v1+json", "size": 7023, diff --git a/manifest/fixtures/ociv1.zstd.manifest.json b/manifest/fixtures/ociv1.zstd.manifest.json index c2a3ca13c..b1891582f 100644 --- a/manifest/fixtures/ociv1.zstd.manifest.json +++ b/manifest/fixtures/ociv1.zstd.manifest.json @@ -1,5 +1,6 @@ { "schemaVersion": 2, + "mediaType": "application/vnd.oci.image.manifest.v1+json", "config": { "mediaType": "application/vnd.oci.image.config.v1+json", "size": 7023, diff --git a/manifest/fixtures/ociv1nomime.artifact.json b/manifest/fixtures/ociv1nomime.artifact.json new file mode 100644 index 000000000..5091d1f61 --- /dev/null +++ b/manifest/fixtures/ociv1nomime.artifact.json @@ -0,0 +1,9 @@ +{ + "schemaVersion": 2, + "config": { + "mediaType": "application/vnd.oci.custom.artifact.config.v1+json", + "digest": "", + "size": 0 + }, + "layers": null +} diff --git a/manifest/fixtures/ociv1nomime.image.index.json b/manifest/fixtures/ociv1nomime.image.index.json new file mode 100644 index 000000000..066f058db --- /dev/null +++ b/manifest/fixtures/ociv1nomime.image.index.json @@ -0,0 +1,30 @@ +{ + "schemaVersion": 2, + "manifests": [ + { + "mediaType": "application/vnd.oci.image.manifest.v1+json", + "size": 7143, + "digest": "sha256:e692418e4cbaf90ca69d05a66403747baa33ee08806650b51fab815ad7fc331f", + "platform": { + "architecture": "ppc64le", + "os": "linux" + } + }, + { + "mediaType": "application/vnd.oci.image.manifest.v1+json", + "size": 7682, + "digest": "sha256:5b0bcabd1ed22e9fb1310cf6c2dec7cdef19f0ad69efa1f392e94a4333501270", + "platform": { + "architecture": "amd64", + "os": "linux", + "os.features": [ + "sse4" + ] + } + } + ], + "annotations": { + "com.example.key1": "value1", + "com.example.key2": "value2" + } +} diff --git a/manifest/fixtures/ociv1nomime.manifest.json b/manifest/fixtures/ociv1nomime.manifest.json new file mode 100644 index 000000000..1e1047ca7 --- /dev/null +++ b/manifest/fixtures/ociv1nomime.manifest.json @@ -0,0 +1,29 @@ +{ + "schemaVersion": 2, + "config": { + "mediaType": "application/vnd.oci.image.config.v1+json", + "size": 7023, + "digest": "sha256:b5b2b2c507a0944348e0303114d8d93aaaa081732b86451d9bce1f432a537bc7" + }, + "layers": [ + { + "mediaType": "application/vnd.oci.image.layer.v1.tar+gzip", + "size": 32654, + "digest": "sha256:e692418e4cbaf90ca69d05a66403747baa33ee08806650b51fab815ad7fc331f" + }, + { + "mediaType": "application/vnd.oci.image.layer.v1.tar+gzip", + "size": 16724, + "digest": "sha256:3c3a4604a545cdc127456d94e421cd355bca5b528f4a9c1905b15da2eb4a4c6b" + }, + { + "mediaType": "application/vnd.oci.image.layer.v1.tar+gzip", + "size": 73109, + "digest": "sha256:ec4b8955958665577945c89419d1af06b5f7636b4ac3da7f12184802ad867736" + } + ], + "annotations": { + "com.example.key1": "value1", + "com.example.key2": "value2" + } +} diff --git a/manifest/manifest.go b/manifest/manifest.go index 4b644f253..2e3e5da15 100644 --- a/manifest/manifest.go +++ b/manifest/manifest.go @@ -110,7 +110,8 @@ func GuessMIMEType(manifest []byte) string { } switch meta.MediaType { - case DockerV2Schema2MediaType, DockerV2ListMediaType: // A recognized type. + case DockerV2Schema2MediaType, DockerV2ListMediaType, + imgspecv1.MediaTypeImageManifest, imgspecv1.MediaTypeImageIndex: // A recognized type. return meta.MediaType } // this is the only way the function can return DockerV2Schema1MediaType, and recognizing that is essential for stripping the JWS signatures = computing the correct manifest digest. @@ -121,9 +122,9 @@ func GuessMIMEType(manifest []byte) string { } return DockerV2Schema1MediaType case 2: - // best effort to understand if this is an OCI image since mediaType - // isn't in the manifest for OCI anymore - // for docker v2s2 meta.MediaType should have been set. But given the data, this is our best guess. + // Best effort to understand if this is an OCI image since mediaType + // wasn't in the manifest for OCI image-spec < 1.0.2. + // For docker v2s2 meta.MediaType should have been set. But given the data, this is our best guess. ociMan := struct { Config struct { MediaType string `json:"mediaType"` diff --git a/manifest/manifest_test.go b/manifest/manifest_test.go index 7e6692ae8..88ea359ac 100644 --- a/manifest/manifest_test.go +++ b/manifest/manifest_test.go @@ -32,6 +32,9 @@ func TestGuessMIMEType(t *testing.T) { {"ociv1.manifest.json", imgspecv1.MediaTypeImageManifest}, {"ociv1.artifact.json", imgspecv1.MediaTypeImageManifest}, {"ociv1.image.index.json", imgspecv1.MediaTypeImageIndex}, + {"ociv1nomime.manifest.json", imgspecv1.MediaTypeImageManifest}, + {"ociv1nomime.artifact.json", imgspecv1.MediaTypeImageManifest}, + {"ociv1nomime.image.index.json", imgspecv1.MediaTypeImageIndex}, } for _, c := range cases { diff --git a/manifest/oci.go b/manifest/oci.go index c4616b965..5892184df 100644 --- a/manifest/oci.go +++ b/manifest/oci.go @@ -66,6 +66,7 @@ func OCI1FromComponents(config imgspecv1.Descriptor, layers []imgspecv1.Descript return &OCI1{ imgspecv1.Manifest{ Versioned: specs.Versioned{SchemaVersion: 2}, + MediaType: imgspecv1.MediaTypeImageManifest, Config: config, Layers: layers, }, diff --git a/manifest/oci_index.go b/manifest/oci_index.go index 5bec43ff9..c4f11e09c 100644 --- a/manifest/oci_index.go +++ b/manifest/oci_index.go @@ -119,6 +119,7 @@ func OCI1IndexFromComponents(components []imgspecv1.Descriptor, annotations map[ index := OCI1Index{ imgspecv1.Index{ Versioned: imgspec.Versioned{SchemaVersion: 2}, + MediaType: imgspecv1.MediaTypeImageIndex, Manifests: make([]imgspecv1.Descriptor, len(components)), Annotations: dupStringStringMap(annotations), }, @@ -195,6 +196,7 @@ func OCI1IndexFromManifest(manifest []byte) (*OCI1Index, error) { index := OCI1Index{ Index: imgspecv1.Index{ Versioned: imgspec.Versioned{SchemaVersion: 2}, + MediaType: imgspecv1.MediaTypeImageIndex, Manifests: []imgspecv1.Descriptor{}, Annotations: make(map[string]string), },