@@ -6,12 +6,13 @@ import (
6
6
"fmt"
7
7
"io"
8
8
9
+ cdcompression "github.com/containerd/containerd/archive/compression"
9
10
"github.com/containerd/containerd/content"
10
11
"github.com/containerd/containerd/errdefs"
11
12
"github.com/containerd/containerd/images"
12
13
"github.com/containerd/containerd/images/converter"
13
- "github.com/containerd/containerd/images/converter/uncompress"
14
14
"github.com/containerd/containerd/labels"
15
+ "github.com/klauspost/compress/zstd"
15
16
"github.com/moby/buildkit/util/compression"
16
17
digest "github.com/opencontainers/go-digest"
17
18
ocispecs "github.com/opencontainers/image-spec/specs-go/v1"
@@ -23,11 +24,15 @@ import (
23
24
func needsConversion (mediaType string , compressionType compression.Type ) (bool , error ) {
24
25
switch compressionType {
25
26
case compression .Uncompressed :
26
- if ! images .IsLayerType (mediaType ) || uncompress . IsUncompressedType (mediaType ) {
27
+ if ! images .IsLayerType (mediaType ) || compression . FromMediaType (mediaType ) == compression . Uncompressed {
27
28
return false , nil
28
29
}
29
30
case compression .Gzip :
30
- if ! images .IsLayerType (mediaType ) || isGzipCompressedType (mediaType ) {
31
+ if ! images .IsLayerType (mediaType ) || compression .FromMediaType (mediaType ) == compression .Gzip {
32
+ return false , nil
33
+ }
34
+ case compression .Zstd :
35
+ if ! images .IsLayerType (mediaType ) || compression .FromMediaType (mediaType ) == compression .Zstd {
31
36
return false , nil
32
37
}
33
38
case compression .EStargz :
@@ -49,113 +54,129 @@ func getConverter(desc ocispecs.Descriptor, compressionType compression.Type) (c
49
54
// No conversion. No need to return an error here.
50
55
return nil , nil
51
56
}
57
+
58
+ c := conversion {target : compressionType }
59
+
60
+ from := compression .FromMediaType (desc .MediaType )
61
+ switch from {
62
+ case compression .Uncompressed :
63
+ case compression .Gzip , compression .Zstd :
64
+ c .decompress = cdcompression .DecompressStream
65
+ default :
66
+ return nil , errors .Errorf ("unsupported source compression type %q from mediatype %q" , from , desc .MediaType )
67
+ }
68
+
52
69
switch compressionType {
53
70
case compression .Uncompressed :
54
- return uncompress .LayerConvertFunc , nil
55
71
case compression .Gzip :
56
- convertFunc := func (w io.Writer ) (io.WriteCloser , error ) { return gzip .NewWriter (w ), nil }
57
- return gzipLayerConvertFunc (compressionType , convertFunc , nil ), nil
72
+ c .compress = func (w io.Writer ) (io.WriteCloser , error ) {
73
+ return gzip .NewWriter (w ), nil
74
+ }
75
+ case compression .Zstd :
76
+ c .compress = func (w io.Writer ) (io.WriteCloser , error ) {
77
+ return zstd .NewWriter (w )
78
+ }
58
79
case compression .EStargz :
59
80
compressorFunc , finalize := writeEStargz ()
60
- convertFunc := func (w io.Writer ) (io.WriteCloser , error ) { return compressorFunc (w , ocispecs .MediaTypeImageLayerGzip ) }
61
- return gzipLayerConvertFunc (compressionType , convertFunc , finalize ), nil
81
+ c .compress = func (w io.Writer ) (io.WriteCloser , error ) {
82
+ return compressorFunc (w , ocispecs .MediaTypeImageLayerGzip )
83
+ }
84
+ c .finalize = finalize
62
85
default :
63
- return nil , fmt .Errorf ("unknown compression type during conversion: %q" , compressionType )
86
+ return nil , errors .Errorf ("unknown target compression type during conversion: %q" , compressionType )
64
87
}
88
+
89
+ return (& c ).convert , nil
65
90
}
66
91
67
- func gzipLayerConvertFunc (compressionType compression.Type , convertFunc func (w io.Writer ) (io.WriteCloser , error ), finalize func (context.Context , content.Store ) (map [string ]string , error )) converter.ConvertFunc {
68
- return func (ctx context.Context , cs content.Store , desc ocispecs.Descriptor ) (* ocispecs.Descriptor , error ) {
69
- // prepare the source and destination
70
- info , err := cs .Info (ctx , desc .Digest )
71
- if err != nil {
72
- return nil , err
73
- }
74
- labelz := info .Labels
75
- if labelz == nil {
76
- labelz = make (map [string ]string )
77
- }
78
- ra , err := cs .ReaderAt (ctx , desc )
79
- if err != nil {
80
- return nil , err
81
- }
82
- defer ra .Close ()
83
- ref := fmt .Sprintf ("convert-from-%s-to-%s" , desc .Digest , compressionType .String ())
84
- w , err := cs .Writer (ctx , content .WithRef (ref ))
85
- if err != nil {
86
- return nil , err
87
- }
88
- defer w .Close ()
89
- if err := w .Truncate (0 ); err != nil { // Old written data possibly remains
90
- return nil , err
91
- }
92
- zw , err := convertFunc (w )
92
+ type conversion struct {
93
+ target compression.Type
94
+ decompress func (io.Reader ) (cdcompression.DecompressReadCloser , error )
95
+ compress func (w io.Writer ) (io.WriteCloser , error )
96
+ finalize func (context.Context , content.Store ) (map [string ]string , error )
97
+ }
98
+
99
+ func (c * conversion ) convert (ctx context.Context , cs content.Store , desc ocispecs.Descriptor ) (* ocispecs.Descriptor , error ) {
100
+ // prepare the source and destination
101
+ info , err := cs .Info (ctx , desc .Digest )
102
+ if err != nil {
103
+ return nil , err
104
+ }
105
+ labelz := info .Labels
106
+ if labelz == nil {
107
+ labelz = make (map [string ]string )
108
+ }
109
+ ra , err := cs .ReaderAt (ctx , desc )
110
+ if err != nil {
111
+ return nil , err
112
+ }
113
+ defer ra .Close ()
114
+ ref := fmt .Sprintf ("convert-from-%s-to-%s" , desc .Digest , c .target .String ())
115
+ w , err := cs .Writer (ctx , content .WithRef (ref ))
116
+ if err != nil {
117
+ return nil , err
118
+ }
119
+ defer w .Close ()
120
+ if err := w .Truncate (0 ); err != nil { // Old written data possibly remains
121
+ return nil , err
122
+ }
123
+ var zw io.WriteCloser = w
124
+ var compress io.WriteCloser
125
+ if c .compress != nil {
126
+ zw , err = c .compress (zw )
93
127
if err != nil {
94
128
return nil , err
95
129
}
96
130
defer zw .Close ()
131
+ compress = zw
132
+ }
97
133
98
- // convert this layer
99
- diffID := digest .Canonical .Digester ()
100
- if _ , err := io .Copy (zw , io .TeeReader (io .NewSectionReader (ra , 0 , ra .Size ()), diffID .Hash ())); err != nil {
101
- return nil , err
102
- }
103
- if err := zw .Close (); err != nil { // Flush the writer
104
- return nil , err
105
- }
106
- labelz [labels .LabelUncompressed ] = diffID .Digest ().String () // update diffID label
107
- if err = w .Commit (ctx , 0 , "" , content .WithLabels (labelz )); err != nil && ! errdefs .IsAlreadyExists (err ) {
108
- return nil , err
109
- }
110
- if err := w .Close (); err != nil {
111
- return nil , err
112
- }
113
- info , err = cs .Info (ctx , w .Digest ())
134
+ // convert this layer
135
+ diffID := digest .Canonical .Digester ()
136
+ var rdr io.Reader = io .NewSectionReader (ra , 0 , ra .Size ())
137
+ if c .decompress != nil {
138
+ rc , err := c .decompress (rdr )
114
139
if err != nil {
115
140
return nil , err
116
141
}
117
-
118
- newDesc := desc
119
- newDesc .MediaType = convertMediaTypeToGzip (desc .MediaType )
120
- newDesc .Digest = info .Digest
121
- newDesc .Size = info .Size
122
- if finalize != nil {
123
- a , err := finalize (ctx , cs )
124
- if err != nil {
125
- return nil , errors .Wrapf (err , "failed finalize compression" )
126
- }
127
- for k , v := range a {
128
- if newDesc .Annotations == nil {
129
- newDesc .Annotations = make (map [string ]string )
130
- }
131
- newDesc .Annotations [k ] = v
132
- }
142
+ defer rc .Close ()
143
+ rdr = rc
144
+ }
145
+ if _ , err := io .Copy (zw , io .TeeReader (rdr , diffID .Hash ())); err != nil {
146
+ return nil , err
147
+ }
148
+ if compress != nil {
149
+ if err := compress .Close (); err != nil { // Flush the writer
150
+ return nil , err
133
151
}
134
- return & newDesc , nil
135
152
}
136
- }
137
-
138
- func isGzipCompressedType (mt string ) bool {
139
- switch mt {
140
- case
141
- images .MediaTypeDockerSchema2LayerGzip ,
142
- images .MediaTypeDockerSchema2LayerForeignGzip ,
143
- ocispecs .MediaTypeImageLayerGzip ,
144
- ocispecs .MediaTypeImageLayerNonDistributableGzip :
145
- return true
146
- default :
147
- return false
153
+ labelz [labels .LabelUncompressed ] = diffID .Digest ().String () // update diffID label
154
+ if err = w .Commit (ctx , 0 , "" , content .WithLabels (labelz )); err != nil && ! errdefs .IsAlreadyExists (err ) {
155
+ return nil , err
156
+ }
157
+ if err := w .Close (); err != nil {
158
+ return nil , err
159
+ }
160
+ info , err = cs .Info (ctx , w .Digest ())
161
+ if err != nil {
162
+ return nil , err
148
163
}
149
- }
150
164
151
- func convertMediaTypeToGzip (mt string ) string {
152
- if uncompress .IsUncompressedType (mt ) {
153
- if images .IsDockerType (mt ) {
154
- mt += ".gzip"
155
- } else {
156
- mt += "+gzip"
165
+ newDesc := desc
166
+ newDesc .MediaType = c .target .DefaultMediaType ()
167
+ newDesc .Digest = info .Digest
168
+ newDesc .Size = info .Size
169
+ if c .finalize != nil {
170
+ a , err := c .finalize (ctx , cs )
171
+ if err != nil {
172
+ return nil , errors .Wrapf (err , "failed finalize compression" )
173
+ }
174
+ for k , v := range a {
175
+ if newDesc .Annotations == nil {
176
+ newDesc .Annotations = make (map [string ]string )
177
+ }
178
+ newDesc .Annotations [k ] = v
157
179
}
158
- return mt
159
180
}
160
- return mt
181
+ return & newDesc , nil
161
182
}
0 commit comments