@@ -22,12 +22,14 @@ import (
2222 "bytes"
2323 "context"
2424 "encoding/json"
25+ "fmt"
2526 "io"
2627 "io/ioutil"
2728 "path"
2829
2930 "github.com/containerd/containerd/archive/compression"
3031 "github.com/containerd/containerd/content"
32+ "github.com/containerd/containerd/errdefs"
3133 "github.com/containerd/containerd/images"
3234 "github.com/containerd/containerd/log"
3335 digest "github.com/opencontainers/go-digest"
@@ -137,19 +139,23 @@ func ImportIndex(ctx context.Context, store content.Store, reader io.Reader) (oc
137139 if ! ok {
138140 return ocispec.Descriptor {}, errors .Errorf ("image config %q not found" , mfst .Config )
139141 }
140- config .MediaType = ocispec . MediaTypeImageConfig
142+ config .MediaType = images . MediaTypeDockerSchema2Config
141143
142144 layers , err := resolveLayers (ctx , store , mfst .Layers , blobs )
143145 if err != nil {
144146 return ocispec.Descriptor {}, errors .Wrap (err , "failed to resolve layers" )
145147 }
146148
147- manifest := ocispec.Manifest {
148- Versioned : specs.Versioned {
149- SchemaVersion : 2 ,
150- },
151- Config : config ,
152- Layers : layers ,
149+ manifest := struct {
150+ SchemaVersion int `json:"schemaVersion"`
151+ MediaType string `json:"mediaType"`
152+ Config ocispec.Descriptor `json:"config"`
153+ Layers []ocispec.Descriptor `json:"layers"`
154+ }{
155+ SchemaVersion : 2 ,
156+ MediaType : images .MediaTypeDockerSchema2Manifest ,
157+ Config : config ,
158+ Layers : layers ,
153159 }
154160
155161 desc , err := writeManifest (ctx , store , manifest , ocispec .MediaTypeImageManifest )
@@ -214,35 +220,111 @@ func onUntarBlob(ctx context.Context, r io.Reader, store content.Ingester, size
214220}
215221
216222func resolveLayers (ctx context.Context , store content.Store , layerFiles []string , blobs map [string ]ocispec.Descriptor ) ([]ocispec.Descriptor , error ) {
217- var layers []ocispec.Descriptor
218- for _ , f := range layerFiles {
223+ layers := make ([]ocispec.Descriptor , len (layerFiles ))
224+ descs := map [digest.Digest ]* ocispec.Descriptor {}
225+ filters := []string {}
226+ for i , f := range layerFiles {
219227 desc , ok := blobs [f ]
220228 if ! ok {
221229 return nil , errors .Errorf ("layer %q not found" , f )
222230 }
231+ layers [i ] = desc
232+ descs [desc .Digest ] = & layers [i ]
233+ filters = append (filters , "labels.\" containerd.io/uncompressed\" ==" + desc .Digest .String ())
234+ }
235+
236+ err := store .Walk (ctx , func (info content.Info ) error {
237+ dgst , ok := info .Labels ["containerd.io/uncompressed" ]
238+ if ok {
239+ desc := descs [digest .Digest (dgst )]
240+ if desc != nil {
241+ desc .MediaType = images .MediaTypeDockerSchema2LayerGzip
242+ desc .Digest = info .Digest
243+ desc .Size = info .Size
244+ }
245+ }
246+ return nil
247+ }, filters ... )
248+ if err != nil {
249+ return nil , errors .Wrap (err , "failure checking for compressed blobs" )
250+ }
223251
252+ for i , desc := range layers {
253+ if desc .MediaType != "" {
254+ continue
255+ }
224256 // Open blob, resolve media type
225257 ra , err := store .ReaderAt (ctx , desc )
226258 if err != nil {
227- return nil , errors .Wrapf (err , "failed to open %q (%s)" , f , desc .Digest )
259+ return nil , errors .Wrapf (err , "failed to open %q (%s)" , layerFiles [ i ] , desc .Digest )
228260 }
229261 s , err := compression .DecompressStream (content .NewReader (ra ))
230262 if err != nil {
231- return nil , errors .Wrapf (err , "failed to detect compression for %q" , f )
263+ return nil , errors .Wrapf (err , "failed to detect compression for %q" , layerFiles [ i ] )
232264 }
233265 if s .GetCompression () == compression .Uncompressed {
234- // TODO: Support compressing and writing back to content store
235- desc .MediaType = ocispec .MediaTypeImageLayer
236- } else {
237- desc .MediaType = ocispec .MediaTypeImageLayerGzip
266+ ref := fmt .Sprintf ("compress-blob-%s-%s" , desc .Digest .Algorithm ().String (), desc .Digest .Encoded ())
267+ labels := map [string ]string {
268+ "containerd.io/uncompressed" : desc .Digest .String (),
269+ }
270+ layers [i ], err = compressBlob (ctx , store , s , ref , content .WithLabels (labels ))
271+ if err != nil {
272+ s .Close ()
273+ return nil , err
274+ }
238275 }
276+ layers [i ].MediaType = images .MediaTypeDockerSchema2LayerGzip
239277 s .Close ()
240278
241- layers = append (layers , desc )
242279 }
243280 return layers , nil
244281}
245282
283+ func compressBlob (ctx context.Context , cs content.Store , r io.Reader , ref string , opts ... content.Opt ) (desc ocispec.Descriptor , err error ) {
284+ w , err := content .OpenWriter (ctx , cs , content .WithRef (ref ))
285+ if err != nil {
286+ return ocispec.Descriptor {}, errors .Wrap (err , "failed to open writer" )
287+ }
288+
289+ defer func () {
290+ w .Close ()
291+ if err != nil {
292+ cs .Abort (ctx , ref )
293+ }
294+ }()
295+ if err := w .Truncate (0 ); err != nil {
296+ return ocispec.Descriptor {}, errors .Wrap (err , "failed to truncate writer" )
297+ }
298+
299+ cw , err := compression .CompressStream (w , compression .Gzip )
300+ if err != nil {
301+ return ocispec.Descriptor {}, err
302+ }
303+
304+ if _ , err := io .Copy (cw , r ); err != nil {
305+ return ocispec.Descriptor {}, err
306+ }
307+ if err := cw .Close (); err != nil {
308+ return ocispec.Descriptor {}, err
309+ }
310+
311+ cst , err := w .Status ()
312+ if err != nil {
313+ return ocispec.Descriptor {}, errors .Wrap (err , "failed to get writer status" )
314+ }
315+
316+ desc .Digest = w .Digest ()
317+ desc .Size = cst .Offset
318+
319+ if err := w .Commit (ctx , desc .Size , desc .Digest , opts ... ); err != nil {
320+ if ! errdefs .IsAlreadyExists (err ) {
321+ return ocispec.Descriptor {}, errors .Wrap (err , "failed to commit" )
322+ }
323+ }
324+
325+ return desc , nil
326+ }
327+
246328func writeManifest (ctx context.Context , cs content.Ingester , manifest interface {}, mediaType string ) (ocispec.Descriptor , error ) {
247329 manifestBytes , err := json .Marshal (manifest )
248330 if err != nil {
0 commit comments