Skip to content

Commit 5f67359

Browse files
committed
media-types: Define '+gzip' structured syntax suffix
Leaning heavily on the existing entries in RFC 6839. The suffix makes it easy to clarify DiffIDs without requiring a particular layer media type. It also allows you to create image-layout instances where the layers are stored uncompressed, which may be useful for cases such as: * Binary diffing between layer blobs for cheaper updates of large layers [1]. * Compressing an image-layout tarball for a smaller smaller overall tarball (by avoiding the unnecessary fragmentation of compressing the individual blob entries). Also update unpackLayer to handle both compressed and uncompressed layers. I expect unpackLayer will end up in image-tools, so I haven't invested a lot of time polishing this implementation. But without *some* sort of change the manifest tests fail. [1]: http://ircbot.wl.linuxfoundation.org/eavesdrop/%23opencontainers/%23opencontainers.2016-08-16.log.html#t2016-08-16T23:35:43 Signed-off-by: W. Trevor King <[email protected]>
1 parent ae3a252 commit 5f67359

File tree

8 files changed

+86
-25
lines changed

8 files changed

+86
-25
lines changed

config.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,8 @@ Changing it means creating a new derived image, instead of changing the existing
2828
A layer DiffID is a SHA256 digest over the layer's uncompressed tar archive and serialized in the descriptor digest format, e.g., `sha256:a9561eb1b190625c9adb5a9513e72c4dedafc1cb2d4c5236c9a6957ec7dfd5a9`.
2929
Layers must be packed and unpacked reproducibly to avoid changing the layer DiffID, for example by using tar-split to save the tar headers.
3030

31-
NOTE: the DiffID is different than the digest in the manifest list because the manifest digest is taken over the gzipped layer for `application/vnd.oci.image.layer.v1.tar+gzip` types.
31+
The difference between DiffIDs and the layer digests in the [manifest's `layers`](manifest.md#image-manifest-property-descriptions) is that the layer digest is taken over the blob regardless of compression, while the DiffID is taken after removing any compression.
32+
For an `application/vnd.oci.image.layer.tar+gzip` layer, the layer digest is taken over the `application/vnd.oci.image.layer.tar+gzip` content, while the DiffID is take over the `application/vnd.oci.image.layer.tar` content.
3233

3334
### Layer ChainID
3435

image/manifest.go

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -89,8 +89,16 @@ func (m *manifest) validate(w walker) error {
8989
}
9090

9191
func (m *manifest) unpack(w walker, dest string) error {
92+
recognizedLayerTypes := map[string]bool{
93+
v1.MediaTypeImageLayer: true,
94+
v1.MediaTypeImageLayer + "+gzip": true,
95+
v1.MediaTypeImageLayerNonDistributable: true,
96+
v1.MediaTypeImageLayerNonDistributable + "+gzip": true,
97+
}
98+
9299
for _, d := range m.Layers {
93-
if d.MediaType != string(schema.MediaTypeImageLayer) {
100+
_, ok := recognizedLayerTypes[d.MediaType]
101+
if !ok {
94102
continue
95103
}
96104

@@ -104,7 +112,7 @@ func (m *manifest) unpack(w walker, dest string) error {
104112
return nil
105113
}
106114

107-
if err := unpackLayer(dest, r); err != nil {
115+
if err := unpackLayer(dest, r, d.MediaType); err != nil {
108116
return errors.Wrap(err, "error extracting layer")
109117
}
110118

@@ -120,16 +128,20 @@ func (m *manifest) unpack(w walker, dest string) error {
120128
return nil
121129
}
122130

123-
func unpackLayer(dest string, r io.Reader) error {
131+
func unpackLayer(dest string, r io.Reader, mediaType string) error {
124132
entries := make(map[string]bool)
125-
gz, err := gzip.NewReader(r)
126-
if err != nil {
127-
return errors.Wrap(err, "error creating gzip reader")
133+
134+
if strings.HasSuffix(mediaType, "+gzip") {
135+
gz, err := gzip.NewReader(r)
136+
if err != nil {
137+
return errors.Wrap(err, "error creating gzip reader")
138+
}
139+
defer gz.Close()
140+
r = gz
128141
}
129-
defer gz.Close()
130142

131143
var dirs []*tar.Header
132-
tr := tar.NewReader(gz)
144+
tr := tar.NewReader(r)
133145

134146
loop:
135147
for {

image/manifest_test.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,8 @@ func TestUnpackLayerDuplicateEntries(t *testing.T) {
6060
t.Fatal(err)
6161
}
6262
defer os.RemoveAll(tmp2)
63-
if err := unpackLayer(tmp2, r); err != nil && !strings.Contains(err.Error(), "duplicate entry for") {
63+
err = unpackLayer(tmp2, r, "application/vnd.oci.image.layer.v1.tar+gzip")
64+
if err != nil && !strings.Contains(err.Error(), "duplicate entry for") {
6465
t.Fatalf("Expected to fail with duplicate entry, got %v", err)
6566
}
6667
}

img/media-types.dot

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ digraph G {
33
manifestList [shape=note, label="Manifest list\n<<optional>>\napplication/vnd.oci.image.manifest.list.v1+json"]
44
manifest [shape=note, label="Image manifest\napplication/vnd.oci.image.manifest.v1+json"]
55
config [shape=note, label="Image JSON\napplication/vnd.oci.image.config.v1+json"]
6-
layer [shape=note, label="Layer tar+gzip\napplication/vnd.oci.image.layer.v1.tar+gzip\napplication/vnd.oci.image.layer.nondistributable.v1.tar+gzip"]
6+
layer [shape=note, label="Layer tar archive\napplication/vnd.oci.image.layer.v1.tar\napplication/vnd.oci.image.layer.nondistributable.v1.tar"]
77
}
88

99
manifestList -> manifest [label="1..*"]

layer.md

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,12 @@ This document describes how to serialize a filesystem and filesystem changes lik
44
One or more layers are applied on top of each other to create a complete filesystem.
55
This document will use a concrete example to illustrate how to create and consume these filesystem layers.
66

7-
This section defines the `application/vnd.oci.image.layer.v1.tar+gzip` and `application/vnd.oci.image.layer.nondistributable.v1.tar+gzip` [media types](media-types.md).
7+
This section defines the `application/vnd.oci.image.layer.v1.tar` and `application/vnd.oci.image.layer.nondistributable.v1.tar` [media types](media-types.md).
88

99
## Distributable Format
1010

11-
Layer Changesets for the [mediatype](./media-types.md) `application/vnd.oci.image.layer.v1.tar+gzip` MUST be packaged in a [tar archive][tar-archive] compressed with [gzip][gzip].
12-
Layer Changesets for the [mediatype](./media-types.md) `application/vnd.oci.image.layer.v1.tar+gzip` MUST NOT include duplicate entries for file paths in the resulting [tar archive][tar-archive].
11+
Layer Changesets for the [media type](./media-types.md) `application/vnd.oci.image.layer.v1.tar` MUST be packaged in [tar archive][tar-archive].
12+
Layer Changesets for the [media type](./media-types.md) `application/vnd.oci.image.layer.v1.tar` MUST NOT include duplicate entries for file paths in the resulting [tar archive][tar-archive].
1313

1414
## Change Types
1515

@@ -208,7 +208,7 @@ Where the basename name of `./etc/my-app-config` is now prefixed with `.wh.`, an
208208

209209
## Applying
210210

211-
Layer Changesets of [mediatype](./media-types.md) `application/vnd.oci.image.layer.v1.tar+gzip` are applied rather than strictly extracted in normal fashion for tar archives.
211+
Layer Changesets of [media type](./media-types.md) `application/vnd.oci.image.layer.v1.tar` are applied rather than strictly extracted in normal fashion for tar archives.
212212

213213
Applying a layer changeset requires consideration for the [whiteout](#whiteouts) files.
214214
In the absence of any [whiteout](#whiteouts) files in a layer changeset, the archive is extracted like a regular tar archive.
@@ -313,11 +313,10 @@ Any given image is likely to be composed of several of these Image Filesystem Ch
313313
Certain layers, due to legal requirements, may not be regularly distributable.
314314
Typically, such layers are downloaded directly from a distributor but are never uploaded.
315315

316-
Layers that have these restrictions SHOULD be tagged with an alternative mediatype of `application/vnd.oci.image.layer.nondistributable.v1.tar+gzip`.
316+
Layers that have these restrictions SHOULD be tagged with an alternative media type of `application/vnd.oci.image.layer.nondistributable.v1.tar`.
317317
[Descriptors](descriptor.md) referencing these layers MAY include `urls` for downloading these layers.
318318
It is implementation-defined whether or not implementations upload layers tagged with this media type.
319319

320320
[libarchive-tar]: https://github.com/libarchive/libarchive/wiki/ManPageTar5#POSIX_ustar_Archives
321321
[gnu-tar-standard]: http://www.gnu.org/software/tar/manual/html_node/Standard.html
322322
[tar-archive]: https://en.wikipedia.org/wiki/Tar_(computing)
323-
[gzip]: http://www.zlib.org/rfc-gzip.html

manifest.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,11 +53,13 @@ Unlike the [Manifest List](manifest-list.md), which contains information about a
5353
This [descriptor property](descriptor.md#properties) has additional restrictions for `layers[]`.
5454
Implementations MUST support at least the following media types:
5555

56-
- [`application/vnd.oci.image.layer.v1.tar+gzip`](layer.md)
57-
- [`application/vnd.oci.image.layer.nondistributable.v1.tar+gzip`](layer.md#non-distributable-layers)
56+
- [`application/vnd.oci.image.layer.v1.tar`](layer.md)
57+
- [`application/vnd.oci.image.layer.nondistributable.v1.tar`](layer.md#non-distributable-layers)
5858

5959
Manifests concerned with portability SHOULD use one of the above media types.
6060

61+
Entries in this field will frequently use the [`+gzip` structured syntax suffix](media-types.md#the-gzip-structured-syntax-suffix).
62+
6163
- **`annotations`** *string-string map*
6264

6365
This OPTIONAL property contains arbitrary metadata for the image manifest.

media-types.md

Lines changed: 50 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,48 @@ The following media types identify the formats described here and their referenc
66
- `application/vnd.oci.image.manifest.list.v1+json`: [Manifest list](manifest-list.md#manifest-list)
77
- `application/vnd.oci.image.manifest.v1+json`: [Image manifest](manifest.md#image-manifest)
88
- `application/vnd.oci.image.config.v1+json`: [Image config](config.md)
9-
- `application/vnd.oci.image.layer.v1.tar+gzip`: ["Layer", as a gzipped tar archive](layer.md)
10-
- `application/vnd.oci.image.layer.nondistributable.v1.tar+gzip`: ["Layer", as a gzipped tar archive with distribution restrictions](layer.md#non-distributable-layers)
9+
- `application/vnd.oci.image.layer.v1.tar`: ["Layer", as a tar archive](layer.md)
10+
- `application/vnd.oci.image.layer.nondistributable.v1.tar`: ["Layer", as a tar archive with distribution restrictions](layer.md#non-distributable-layers)
11+
12+
## Suffixes
13+
14+
[RFC 6839][rfc6839] defines several structured syntax suffixes for use with media types.
15+
This section adds additional structured syntax suffixes for use with media types in OCI Image contexts.
16+
17+
### The +gzip Structured Syntax Suffix
18+
19+
[GZIP][rfc1952] is a widely used compression format.
20+
The media type [`application/gzip`][rfc6713] has been registered for such files.
21+
The suffix `+gzip` MAY be used with any media type whose representation follows that established for `application/gzip`.
22+
The media type structured syntax suffix registration form follows:
23+
24+
Name: GZIP file format
25+
26+
`+suffix`: `+gzip`
27+
28+
References: [[GZIP][rfc1952]]
29+
30+
Encoding considerations: GZIP is a binary encoding.
31+
32+
Fragment identifier considerations:
33+
34+
The syntax and semantics of fragment identifiers specified for `+gzip` SHOULD be as specified for `application/gzip`.
35+
(At publication of this document, there is no fragment identification syntax defined for `application/gzip`.)
36+
The syntax and semantics for fragment identifiers for a specific `xxx/yyy+gzip` SHOULD be processed as follows:
37+
38+
* For cases defined in `+gzip`, where the fragment identifier resolves per the `+gzip` rules, then process as specified in `+gzip`.
39+
* For cases defined in `+gzip`, where the fragment identifier does not resolve per the `+gzip` rules, then process as specified in `xxx/yyy+gzip`.
40+
* For cases not defined in `+gzip`, then process as specified in `xxx/yyy+gzip`.
41+
42+
Interoperability considerations: n/a
43+
44+
Security considerations:
45+
46+
See the "Security Considerations" sections of [RFC 1952][rfc1952] and [RFC 6713][rfc6713].
47+
Each individual media type registered with a `+gzip` suffix can have additional security considerations.
48+
49+
Implementations MUST support the `+gzip` suffix for all [OCI Image Media Types](#oci-image-media-types).
50+
For example, they MUST support `application/vnd.oci.image.layer.v1.tar+gzip` and `application/vnd.oci.image.layer.nondistributable.v1.tar+gzip` for [manifest `layers`](manifest.md#image-manifest-property-descriptions) and `application/vnd.oci.image.manifest.v1+json+gzip` for [manifest list `manifests`](manifest-list.md#manifest-list-property-descriptions).
1151

1252
## Compatibility Matrix
1353

@@ -27,11 +67,13 @@ This section shows where the OCI Image Specification is compatible with formats
2767

2868
- [application/vnd.docker.distribution.manifest.v2+json](https://github.com/docker/distribution/blob/master/docs/spec/manifest-v2-2.md#image-manifest-field-descriptions)
2969

30-
### application/vnd.oci.image.rootfs.tar.gzip
70+
### application/vnd.oci.image.layer.tar
3171

3272
**Interchangeable and fully compatible mime-types**
3373

34-
- [application/vnd.docker.image.rootfs.diff.tar.gzip](https://github.com/docker/docker/blob/master/image/spec/v1.md#creating-an-image-filesystem-changeset)
74+
- With `+gzip`
75+
76+
- [application/vnd.docker.image.rootfs.diff.tar.gzip](https://github.com/docker/docker/blob/master/image/spec/v1.md#creating-an-image-filesystem-changeset)
3577

3678
### application/vnd.oci.image.config.v1+json
3779

@@ -47,3 +89,7 @@ The following figure shows how the above media types reference each other:
4789

4890
[Descriptors](descriptor.md) are used for all references.
4991
The manifest list being a "fat manifest" references one or more image manifests per target platform. An image manifest references exactly one target configuration and possibly many layers.
92+
93+
[rfc1952]: https://tools.ietf.org/html/rfc1952
94+
[rfc6713]: https://tools.ietf.org/html/rfc6713
95+
[rfc6839]: https://tools.ietf.org/html/rfc6839

specs-go/v1/mediatype.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,11 @@ const (
2525
MediaTypeImageManifestList = "application/vnd.oci.image.manifest.list.v1+json"
2626

2727
// MediaTypeImageLayer is the media type used for layers referenced by the manifest.
28-
MediaTypeImageLayer = "application/vnd.oci.image.layer.v1.tar+gzip"
28+
MediaTypeImageLayer = "application/vnd.oci.image.layer.v1.tar"
2929

3030
// MediaTypeImageLayerNonDistributable is the media type for layers referenced by
3131
// the manifest but with distribution restrictions.
32-
MediaTypeImageLayerNonDistributable = "application/vnd.oci.image.layer.nondistributable.v1.tar+gzip"
32+
MediaTypeImageLayerNonDistributable = "application/vnd.oci.image.layer.nondistributable.v1.tar"
3333

3434
// MediaTypeImageConfig specifies the media type for the image configuration.
3535
MediaTypeImageConfig = "application/vnd.oci.image.config.v1+json"

0 commit comments

Comments
 (0)