Skip to content

Commit a0dafd9

Browse files
authored
Merge pull request #3649 from dmcgowan/generic-layer-support
Generic layer support
2 parents d4802a6 + 6f31417 commit a0dafd9

5 files changed

Lines changed: 101 additions & 102 deletions

File tree

diff/stream.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -88,11 +88,11 @@ type StreamProcessor interface {
8888
}
8989

9090
func compressedHandler(ctx context.Context, mediaType string) (StreamProcessorInit, bool) {
91-
compressed, err := images.IsCompressedDiff(ctx, mediaType)
91+
compressed, err := images.DiffCompression(ctx, mediaType)
9292
if err != nil {
9393
return nil, false
9494
}
95-
if compressed {
95+
if compressed != "" {
9696
return func(ctx context.Context, stream StreamProcessor, payloads map[string]*types.Any) (StreamProcessor, error) {
9797
ds, err := compression.DecompressStream(stream)
9898
if err != nil {

images/image.go

Lines changed: 4 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@ import (
2020
"context"
2121
"encoding/json"
2222
"sort"
23-
"strings"
2423
"time"
2524

2625
"github.com/containerd/containerd/content"
@@ -358,16 +357,11 @@ func Children(ctx context.Context, provider content.Provider, desc ocispec.Descr
358357
}
359358

360359
descs = append(descs, index.Manifests...)
361-
case MediaTypeDockerSchema2Layer, MediaTypeDockerSchema2LayerGzip,
362-
MediaTypeDockerSchema2LayerEnc, MediaTypeDockerSchema2LayerGzipEnc,
363-
MediaTypeDockerSchema2LayerForeign, MediaTypeDockerSchema2LayerForeignGzip,
364-
MediaTypeDockerSchema2Config, ocispec.MediaTypeImageConfig,
365-
ocispec.MediaTypeImageLayer, ocispec.MediaTypeImageLayerGzip,
366-
ocispec.MediaTypeImageLayerNonDistributable, ocispec.MediaTypeImageLayerNonDistributableGzip,
367-
MediaTypeContainerd1Checkpoint, MediaTypeContainerd1CheckpointConfig:
368-
// childless data types.
369-
return nil, nil
370360
default:
361+
if IsLayerType(desc.MediaType) || IsKnownConfig(desc.MediaType) {
362+
// childless data types.
363+
return nil, nil
364+
}
371365
log.G(ctx).Warnf("encountered unknown type %v; children may not be fetched", desc.MediaType)
372366
}
373367

@@ -390,72 +384,3 @@ func RootFS(ctx context.Context, provider content.Provider, configDesc ocispec.D
390384
}
391385
return config.RootFS.DiffIDs, nil
392386
}
393-
394-
// IsCompressedDiff returns true if mediaType is a known compressed diff media type.
395-
// It returns false if the media type is a diff, but not compressed. If the media type
396-
// is not a known diff type, it returns errdefs.ErrNotImplemented
397-
func IsCompressedDiff(ctx context.Context, mediaType string) (bool, error) {
398-
switch mediaType {
399-
case ocispec.MediaTypeImageLayer, MediaTypeDockerSchema2Layer:
400-
case ocispec.MediaTypeImageLayerGzip, MediaTypeDockerSchema2LayerGzip:
401-
return true, nil
402-
default:
403-
// Still apply all generic media types *.tar[.+]gzip and *.tar
404-
if strings.HasSuffix(mediaType, ".tar.gzip") || strings.HasSuffix(mediaType, ".tar+gzip") {
405-
return true, nil
406-
} else if !strings.HasSuffix(mediaType, ".tar") {
407-
return false, errdefs.ErrNotImplemented
408-
}
409-
}
410-
return false, nil
411-
}
412-
413-
// GetImageLayerDescriptors gets the image layer Descriptors of an image; the array contains
414-
// a list of Descriptors belonging to one platform followed by lists of other platforms
415-
func GetImageLayerDescriptors(ctx context.Context, cs content.Store, desc ocispec.Descriptor) ([]ocispec.Descriptor, error) {
416-
var lis []ocispec.Descriptor
417-
418-
ds := platforms.DefaultSpec()
419-
platform := &ds
420-
421-
switch desc.MediaType {
422-
case MediaTypeDockerSchema2ManifestList, ocispec.MediaTypeImageIndex,
423-
MediaTypeDockerSchema2Manifest, ocispec.MediaTypeImageManifest:
424-
children, err := Children(ctx, cs, desc)
425-
if err != nil {
426-
if errdefs.IsNotFound(err) {
427-
return []ocispec.Descriptor{}, nil
428-
}
429-
return []ocispec.Descriptor{}, err
430-
}
431-
432-
if desc.Platform != nil {
433-
platform = desc.Platform
434-
}
435-
436-
for _, child := range children {
437-
var tmp []ocispec.Descriptor
438-
439-
switch child.MediaType {
440-
case MediaTypeDockerSchema2LayerGzip, MediaTypeDockerSchema2Layer,
441-
ocispec.MediaTypeImageLayerGzip, ocispec.MediaTypeImageLayer,
442-
MediaTypeDockerSchema2LayerGzipEnc, MediaTypeDockerSchema2LayerEnc:
443-
tdesc := child
444-
tdesc.Platform = platform
445-
tmp = append(tmp, tdesc)
446-
default:
447-
tmp, err = GetImageLayerDescriptors(ctx, cs, child)
448-
}
449-
450-
if err != nil {
451-
return []ocispec.Descriptor{}, err
452-
}
453-
454-
lis = append(lis, tmp...)
455-
}
456-
case MediaTypeDockerSchema2Config, ocispec.MediaTypeImageConfig:
457-
default:
458-
return nil, errors.Wrapf(errdefs.ErrInvalidArgument, "GetImageLayerInfo: unhandled media type %s", desc.MediaType)
459-
}
460-
return lis, nil
461-
}

images/mediatypes.go

Lines changed: 84 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,16 +16,23 @@
1616

1717
package images
1818

19+
import (
20+
"context"
21+
"sort"
22+
"strings"
23+
24+
"github.com/containerd/containerd/errdefs"
25+
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
26+
)
27+
1928
// mediatype definitions for image components handled in containerd.
2029
//
2130
// oci components are generally referenced directly, although we may centralize
2231
// here for clarity.
2332
const (
2433
MediaTypeDockerSchema2Layer = "application/vnd.docker.image.rootfs.diff.tar"
25-
MediaTypeDockerSchema2LayerEnc = "application/vnd.docker.image.rootfs.diff.tar+enc"
2634
MediaTypeDockerSchema2LayerForeign = "application/vnd.docker.image.rootfs.foreign.diff.tar"
2735
MediaTypeDockerSchema2LayerGzip = "application/vnd.docker.image.rootfs.diff.tar.gzip"
28-
MediaTypeDockerSchema2LayerGzipEnc = "application/vnd.docker.image.rootfs.diff.tar.gzip+enc"
2936
MediaTypeDockerSchema2LayerForeignGzip = "application/vnd.docker.image.rootfs.foreign.diff.tar.gzip"
3037
MediaTypeDockerSchema2Config = "application/vnd.docker.container.image.v1+json"
3138
MediaTypeDockerSchema2Manifest = "application/vnd.docker.distribution.manifest.v2+json"
@@ -42,3 +49,78 @@ const (
4249
// Legacy Docker schema1 manifest
4350
MediaTypeDockerSchema1Manifest = "application/vnd.docker.distribution.manifest.v1+prettyjws"
4451
)
52+
53+
// DiffCompression returns the compression as defined by the layer diff media
54+
// type. For Docker media types without compression, "unknown" is returned to
55+
// indicate that the media type may be compressed. If the media type is not
56+
// recognized as a layer diff, then it returns errdefs.ErrNotImplemented
57+
func DiffCompression(ctx context.Context, mediaType string) (string, error) {
58+
base, ext := parseMediaTypes(mediaType)
59+
switch base {
60+
case MediaTypeDockerSchema2Layer, MediaTypeDockerSchema2LayerForeign:
61+
if len(ext) > 0 {
62+
// Type is wrapped
63+
return "", nil
64+
}
65+
// These media types may have been compressed but failed to
66+
// use the correct media type. The decompression function
67+
// should detect and handle this case.
68+
return "unknown", nil
69+
case MediaTypeDockerSchema2LayerGzip, MediaTypeDockerSchema2LayerForeignGzip:
70+
if len(ext) > 0 {
71+
// Type is wrapped
72+
return "", nil
73+
}
74+
return "gzip", nil
75+
case ocispec.MediaTypeImageLayer, ocispec.MediaTypeImageLayerNonDistributable:
76+
if len(ext) > 0 {
77+
switch ext[len(ext)-1] {
78+
case "gzip":
79+
return "gzip", nil
80+
}
81+
}
82+
return "", nil
83+
default:
84+
return "", errdefs.ErrNotImplemented
85+
}
86+
}
87+
88+
// parseMediaTypes splits the media type into the base type and
89+
// an array of sorted extensions
90+
func parseMediaTypes(mt string) (string, []string) {
91+
if mt == "" {
92+
return "", []string{}
93+
}
94+
95+
s := strings.Split(mt, "+")
96+
ext := s[1:]
97+
sort.Strings(ext)
98+
99+
return s[0], ext
100+
}
101+
102+
// IsLayerTypes returns true if the media type is a layer
103+
func IsLayerType(mt string) bool {
104+
if strings.HasPrefix(mt, "application/vnd.oci.image.layer.") {
105+
return true
106+
}
107+
108+
// Parse Docker media types, strip off any + suffixes first
109+
base, _ := parseMediaTypes(mt)
110+
switch base {
111+
case MediaTypeDockerSchema2Layer, MediaTypeDockerSchema2LayerGzip,
112+
MediaTypeDockerSchema2LayerForeign, MediaTypeDockerSchema2LayerForeignGzip:
113+
return true
114+
}
115+
return false
116+
}
117+
118+
// IsKnownConfig returns true if the media type is a known config type
119+
func IsKnownConfig(mt string) bool {
120+
switch mt {
121+
case MediaTypeDockerSchema2Config, ocispec.MediaTypeImageConfig,
122+
MediaTypeContainerd1Checkpoint, MediaTypeContainerd1CheckpointConfig:
123+
return true
124+
}
125+
return false
126+
}

remotes/handlers.go

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -62,21 +62,17 @@ func MakeRefKey(ctx context.Context, desc ocispec.Descriptor) string {
6262
}
6363
}
6464

65-
switch desc.MediaType {
66-
case images.MediaTypeDockerSchema2Manifest, ocispec.MediaTypeImageManifest:
65+
switch mt := desc.MediaType; {
66+
case mt == images.MediaTypeDockerSchema2Manifest || mt == ocispec.MediaTypeImageManifest:
6767
return "manifest-" + desc.Digest.String()
68-
case images.MediaTypeDockerSchema2ManifestList, ocispec.MediaTypeImageIndex:
68+
case mt == images.MediaTypeDockerSchema2ManifestList || mt == ocispec.MediaTypeImageIndex:
6969
return "index-" + desc.Digest.String()
70-
case images.MediaTypeDockerSchema2Layer, images.MediaTypeDockerSchema2LayerGzip,
71-
images.MediaTypeDockerSchema2LayerForeign, images.MediaTypeDockerSchema2LayerForeignGzip,
72-
ocispec.MediaTypeImageLayer, ocispec.MediaTypeImageLayerGzip,
73-
ocispec.MediaTypeImageLayerNonDistributable, ocispec.MediaTypeImageLayerNonDistributableGzip,
74-
images.MediaTypeDockerSchema2LayerEnc, images.MediaTypeDockerSchema2LayerGzipEnc:
70+
case images.IsLayerType(mt):
7571
return "layer-" + desc.Digest.String()
76-
case images.MediaTypeDockerSchema2Config, ocispec.MediaTypeImageConfig:
72+
case images.IsKnownConfig(mt):
7773
return "config-" + desc.Digest.String()
7874
default:
79-
log.G(ctx).Warnf("reference for unknown type: %s", desc.MediaType)
75+
log.G(ctx).Warnf("reference for unknown type: %s", mt)
8076
return "unknown-" + desc.Digest.String()
8177
}
8278
}

unpacker.go

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -204,12 +204,12 @@ func (u *unpacker) handlerWrapper(uctx context.Context, unpacks *int32) (func(im
204204
// one manifest to handle, and manifest list can be
205205
// safely skipped.
206206
// TODO: support multi-platform unpack.
207-
switch desc.MediaType {
208-
case images.MediaTypeDockerSchema1Manifest:
207+
switch mt := desc.MediaType; {
208+
case mt == images.MediaTypeDockerSchema1Manifest:
209209
lock.Lock()
210210
schema1 = true
211211
lock.Unlock()
212-
case images.MediaTypeDockerSchema2Manifest, ocispec.MediaTypeImageManifest:
212+
case mt == images.MediaTypeDockerSchema2Manifest || mt == ocispec.MediaTypeImageManifest:
213213
lock.Lock()
214214
for _, child := range children {
215215
if child.MediaType == images.MediaTypeDockerSchema2Config ||
@@ -219,7 +219,7 @@ func (u *unpacker) handlerWrapper(uctx context.Context, unpacks *int32) (func(im
219219
layers = append(layers, child)
220220
}
221221
lock.Unlock()
222-
case images.MediaTypeDockerSchema2Config, ocispec.MediaTypeImageConfig:
222+
case mt == images.MediaTypeDockerSchema2Config || mt == ocispec.MediaTypeImageConfig:
223223
lock.Lock()
224224
l := append([]ocispec.Descriptor{}, layers...)
225225
lock.Unlock()
@@ -229,11 +229,7 @@ func (u *unpacker) handlerWrapper(uctx context.Context, unpacks *int32) (func(im
229229
return u.unpack(uctx, desc, l)
230230
})
231231
}
232-
case images.MediaTypeDockerSchema2Layer, images.MediaTypeDockerSchema2LayerGzip,
233-
images.MediaTypeDockerSchema2LayerForeign, images.MediaTypeDockerSchema2LayerForeignGzip,
234-
ocispec.MediaTypeImageLayer, ocispec.MediaTypeImageLayerGzip,
235-
ocispec.MediaTypeImageLayerNonDistributable, ocispec.MediaTypeImageLayerNonDistributableGzip,
236-
images.MediaTypeDockerSchema2LayerEnc, images.MediaTypeDockerSchema2LayerGzipEnc:
232+
case images.IsLayerType(mt):
237233
lock.Lock()
238234
update := !schema1
239235
lock.Unlock()

0 commit comments

Comments
 (0)