Skip to content

Commit 5e2d7ef

Browse files
committed
Use a single custom annotation for export
Remove annotation prefix and add multiple index records for manifests with multiple image names. This makes the custom annotation more consistent with the OCI image annotation. Additionally, ensure the OCI image annotation always represents the tag (partial image name) as recommended by the specification. The containerd image name annotation will always contain the full image name. Signed-off-by: Derek McGowan <[email protected]>
1 parent cafda1c commit 5e2d7ef

6 files changed

Lines changed: 137 additions & 164 deletions

File tree

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)