Skip to content

Commit b7f093e

Browse files
Merge pull request containerd#3296 from dmcgowan/fix-export-labels
Use a single custom annotation for export
2 parents 2088fc9 + 5e2d7ef commit b7f093e

File tree

6 files changed

+137
-164
lines changed

6 files changed

+137
-164
lines changed

images/annotations.go

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,4 @@ const (
2020
// AnnotationImageName is an annotation on a Descriptor in an index.json
2121
// containing the `Name` value as used by an `Image` struct
2222
AnnotationImageName = "io.containerd.image.name"
23-
24-
// AnnotationImageNamePrefix is used the same way as AnnotationImageName
25-
// but may be used to refer to additional names in the annotation map
26-
// using user-defined suffixes (i.e. "extra.1")
27-
AnnotationImageNamePrefix = AnnotationImageName + "."
2823
)

images/archive/exporter.go

Lines changed: 102 additions & 122 deletions
Original file line numberDiff line numberDiff line change
@@ -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.
9794
func 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.
108103
func 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-
131114
func 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-
272253
func 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)

images/archive/importer.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -181,7 +181,8 @@ func ImportIndex(ctx context.Context, store content.Store, reader io.Reader) (oc
181181
}
182182

183183
mfstdesc.Annotations = map[string]string{
184-
ocispec.AnnotationRefName: normalized,
184+
images.AnnotationImageName: normalized,
185+
ocispec.AnnotationRefName: ociReferenceName(normalized),
185186
}
186187

187188
idx.Manifests = append(idx.Manifests, mfstdesc)

0 commit comments

Comments
 (0)