@@ -20,11 +20,9 @@ import (
2020 "archive/tar"
2121 "context"
2222 "encoding/json"
23- "fmt"
2423 "io"
2524 "path"
2625 "sort"
27- "strings"
2826
2927 "github.com/containerd/containerd/content"
3028 "github.com/containerd/containerd/errdefs"
@@ -83,9 +81,8 @@ func WithImage(is images.Store, name string) ExportOpt {
8381 return err
8482 }
8583
86- var i int
87- o .manifests , i = appendDescriptor (o .manifests , img .Target )
88- o .manifests [i ].Annotations = addNameAnnotation (name , o .manifests [i ].Annotations )
84+ img .Target .Annotations = addNameAnnotation (name , img .Target .Annotations )
85+ o .manifests = append (o .manifests , img .Target )
8986
9087 return nil
9188 }
@@ -96,9 +93,7 @@ func WithImage(is images.Store, name string) ExportOpt {
9693// descriptor if needed.
9794func WithManifest (manifest ocispec.Descriptor ) ExportOpt {
9895 return func (ctx context.Context , o * exportOptions ) error {
99- var i int
100- o .manifests , i = appendDescriptor (o .manifests , manifest )
101- o .manifests [i ].Annotations = manifest .Annotations
96+ o .manifests = append (o .manifests , manifest )
10297 return nil
10398 }
10499}
@@ -107,49 +102,23 @@ func WithManifest(manifest ocispec.Descriptor) ExportOpt {
107102// with the provided names.
108103func WithNamedManifest (manifest ocispec.Descriptor , names ... string ) ExportOpt {
109104 return func (ctx context.Context , o * exportOptions ) error {
110- var i int
111- o .manifests , i = appendDescriptor (o .manifests , manifest )
112105 for _ , name := range names {
113- o .manifests [i ].Annotations = addNameAnnotation (name , o .manifests [i ].Annotations )
106+ manifest .Annotations = addNameAnnotation (name , manifest .Annotations )
107+ o .manifests = append (o .manifests , manifest )
114108 }
115109
116110 return nil
117111 }
118112}
119113
120- func appendDescriptor (descs []ocispec.Descriptor , desc ocispec.Descriptor ) ([]ocispec.Descriptor , int ) {
121- i := 0
122- for i < len (descs ) {
123- if descs [i ].Digest == desc .Digest {
124- return descs , i
125- }
126- i ++
127- }
128- return append (descs , desc ), i
129- }
130-
131114func addNameAnnotation (name string , annotations map [string ]string ) map [string ]string {
132115 if annotations == nil {
133116 annotations = map [string ]string {}
134117 }
135118
136- i := 0
137- for {
138- key := images .AnnotationImageName
139- if i > 0 {
140- key = fmt .Sprintf ("%sextra.%d" , images .AnnotationImageNamePrefix , i )
141- }
142- i ++
119+ annotations [images .AnnotationImageName ] = name
120+ annotations [ocispec .AnnotationRefName ] = ociReferenceName (name )
143121
144- if val , ok := annotations [key ]; ok {
145- if val != name {
146- continue
147- }
148- } else {
149- annotations [key ] = name
150- }
151- break
152- }
153122 return annotations
154123}
155124
@@ -168,78 +137,100 @@ func Export(ctx context.Context, store content.Provider, writer io.Writer, opts
168137 }
169138
170139 algorithms := map [string ]struct {}{}
171- manifestTags := map [string ]ocispec.Descriptor {}
140+ dManifests := map [digest.Digest ]* exportManifest {}
141+ resolvedIndex := map [digest.Digest ]digest.Digest {}
172142 for _ , desc := range eo .manifests {
173143 switch desc .MediaType {
174144 case images .MediaTypeDockerSchema2Manifest , ocispec .MediaTypeImageManifest :
175- r , err := getRecords (ctx , store , desc , algorithms )
176- if err != nil {
177- return err
178- }
179- records = append (records , r ... )
180-
181- for _ , name := range imageNames (desc .Annotations ) {
182- manifestTags [name ] = desc
183- }
184- case images .MediaTypeDockerSchema2ManifestList , ocispec .MediaTypeImageIndex :
185- records = append (records , blobRecord (store , desc ))
145+ mt , ok := dManifests [desc .Digest ]
146+ if ! ok {
147+ // TODO(containerd): Skip if already added
148+ r , err := getRecords (ctx , store , desc , algorithms )
149+ if err != nil {
150+ return err
151+ }
152+ records = append (records , r ... )
186153
187- p , err := content .ReadBlob (ctx , store , desc )
188- if err != nil {
189- return err
154+ mt = & exportManifest {
155+ manifest : desc ,
156+ }
157+ dManifests [desc .Digest ] = mt
190158 }
191159
192- var index ocispec. Index
193- if err := json . Unmarshal ( p , & index ); err != nil {
194- return err
160+ name := desc . Annotations [ images . AnnotationImageName ]
161+ if name != "" && ! eo . skipDockerManifest {
162+ mt . names = append ( mt . names , name )
195163 }
164+ case images .MediaTypeDockerSchema2ManifestList , ocispec .MediaTypeImageIndex :
165+ d , ok := resolvedIndex [desc .Digest ]
166+ if ! ok {
167+ records = append (records , blobRecord (store , desc ))
196168
197- names := imageNames (desc .Annotations )
198- var manifests []ocispec.Descriptor
199- for _ , m := range index .Manifests {
200- if eo .platform != nil {
201- if m .Platform == nil || eo .platform .Match (* m .Platform ) {
202- manifests = append (manifests , m )
203- } else if ! eo .allPlatforms {
204- continue
205- }
169+ p , err := content .ReadBlob (ctx , store , desc )
170+ if err != nil {
171+ return err
206172 }
207173
208- r , err := getRecords ( ctx , store , m , algorithms )
209- if err != nil {
174+ var index ocispec. Index
175+ if err := json . Unmarshal ( p , & index ); err != nil {
210176 return err
211177 }
212178
213- records = append (records , r ... )
214- }
179+ var manifests []ocispec.Descriptor
180+ for _ , m := range index .Manifests {
181+ if eo .platform != nil {
182+ if m .Platform == nil || eo .platform .Match (* m .Platform ) {
183+ manifests = append (manifests , m )
184+ } else if ! eo .allPlatforms {
185+ continue
186+ }
187+ }
215188
216- if len (names ) > 0 && ! eo .skipDockerManifest {
217- if len (manifests ) >= 1 {
218- if len (manifests ) > 1 {
219- sort .SliceStable (manifests , func (i , j int ) bool {
220- if manifests [i ].Platform == nil {
221- return false
222- }
223- if manifests [j ].Platform == nil {
224- return true
225- }
226- return eo .platform .Less (* manifests [i ].Platform , * manifests [j ].Platform )
227- })
189+ r , err := getRecords (ctx , store , m , algorithms )
190+ if err != nil {
191+ return err
228192 }
229- for _ , name := range names {
230- manifestTags [name ] = manifests [0 ]
193+
194+ records = append (records , r ... )
195+ }
196+
197+ if ! eo .skipDockerManifest {
198+ if len (manifests ) >= 1 {
199+ if len (manifests ) > 1 {
200+ sort .SliceStable (manifests , func (i , j int ) bool {
201+ if manifests [i ].Platform == nil {
202+ return false
203+ }
204+ if manifests [j ].Platform == nil {
205+ return true
206+ }
207+ return eo .platform .Less (* manifests [i ].Platform , * manifests [j ].Platform )
208+ })
209+ }
210+ d = manifests [0 ].Digest
211+ dManifests [d ] = & exportManifest {
212+ manifest : manifests [0 ],
213+ }
214+ } else if eo .platform != nil {
215+ return errors .Wrap (errdefs .ErrNotFound , "no manifest found for platform" )
231216 }
232- } else if eo .platform != nil {
233- return errors .Wrap (errdefs .ErrNotFound , "no manifest found for platform" )
234217 }
218+ resolvedIndex [desc .Digest ] = d
219+ }
220+ if d != "" {
221+ if name := desc .Annotations [images .AnnotationImageName ]; name != "" {
222+ mt := dManifests [d ]
223+ mt .names = append (mt .names , name )
224+ }
225+
235226 }
236227 default :
237228 return errors .Wrap (errdefs .ErrInvalidArgument , "only manifests may be exported" )
238229 }
239230 }
240231
241- if len (manifestTags ) > 0 {
242- tr , err := manifestsRecord (ctx , store , manifestTags )
232+ if len (dManifests ) > 0 {
233+ tr , err := manifestsRecord (ctx , store , dManifests )
243234 if err != nil {
244235 return errors .Wrap (err , "unable to create manifests file" )
245236 }
@@ -259,16 +250,6 @@ func Export(ctx context.Context, store content.Provider, writer io.Writer, opts
259250 return writeTar (ctx , tw , records )
260251}
261252
262- func imageNames (annotations map [string ]string ) []string {
263- var names []string
264- for k , v := range annotations {
265- if k == images .AnnotationImageName || strings .HasPrefix (k , images .AnnotationImageName ) {
266- names = append (names , v )
267- }
268- }
269- return names
270- }
271-
272253func getRecords (ctx context.Context , store content.Provider , desc ocispec.Descriptor , algorithms map [string ]struct {}) ([]tarRecord , error ) {
273254 var records []tarRecord
274255 exportHandler := func (ctx context.Context , desc ocispec.Descriptor ) ([]ocispec.Descriptor , error ) {
@@ -394,16 +375,21 @@ func ociIndexRecord(manifests []ocispec.Descriptor) tarRecord {
394375 }
395376}
396377
397- func manifestsRecord (ctx context.Context , store content.Provider , manifests map [string ]ocispec.Descriptor ) (tarRecord , error ) {
398- type mfst struct {
378+ type exportManifest struct {
379+ manifest ocispec.Descriptor
380+ names []string
381+ }
382+
383+ func manifestsRecord (ctx context.Context , store content.Provider , manifests map [digest.Digest ]* exportManifest ) (tarRecord , error ) {
384+ mfsts := make ([]struct {
399385 Config string
400386 RepoTags []string
401387 Layers []string
402- }
388+ }, len ( manifests ))
403389
404- images := map [digest. Digest ] mfst {}
405- for name , m := range manifests {
406- p , err := content .ReadBlob (ctx , store , m )
390+ var i int
391+ for _ , m := range manifests {
392+ p , err := content .ReadBlob (ctx , store , m . manifest )
407393 if err != nil {
408394 return tarRecord {}, err
409395 }
@@ -413,32 +399,26 @@ func manifestsRecord(ctx context.Context, store content.Provider, manifests map[
413399 return tarRecord {}, err
414400 }
415401 if err := manifest .Config .Digest .Validate (); err != nil {
416- return tarRecord {}, errors .Wrapf (err , "invalid manifest %q" , m .Digest )
417- }
418-
419- nname , err := familiarizeReference (name )
420- if err != nil {
421- return tarRecord {}, err
402+ return tarRecord {}, errors .Wrapf (err , "invalid manifest %q" , m .manifest .Digest )
422403 }
423404
424405 dgst := manifest .Config .Digest
425- mf , ok := images [dgst ]
426- if ! ok {
427- mf .Config = path .Join ("blobs" , dgst .Algorithm ().String (), dgst .Encoded ())
428- for _ , l := range manifest .Layers {
429- path := path .Join ("blobs" , l .Digest .Algorithm ().String (), l .Digest .Encoded ())
430- mf .Layers = append (mf .Layers , path )
431- }
406+ mfsts [i ].Config = path .Join ("blobs" , dgst .Algorithm ().String (), dgst .Encoded ())
407+ for _ , l := range manifest .Layers {
408+ path := path .Join ("blobs" , l .Digest .Algorithm ().String (), l .Digest .Encoded ())
409+ mfsts [i ].Layers = append (mfsts [i ].Layers , path )
432410 }
433411
434- mf .RepoTags = append (mf .RepoTags , nname )
412+ for _ , name := range m .names {
413+ nname , err := familiarizeReference (name )
414+ if err != nil {
415+ return tarRecord {}, err
416+ }
435417
436- images [ dgst ] = mf
437- }
418+ mfsts [ i ]. RepoTags = append ( mfsts [ i ]. RepoTags , nname )
419+ }
438420
439- var mfsts []mfst
440- for _ , mf := range images {
441- mfsts = append (mfsts , mf )
421+ i ++
442422 }
443423
444424 b , err := json .Marshal (mfsts )
0 commit comments