@@ -3,10 +3,12 @@ package ociindex
33import (
44 "encoding/json"
55 "io"
6+ "maps"
67 "os"
78 "path"
89 "syscall"
910
11+ "github.com/containerd/containerd/reference"
1012 "github.com/gofrs/flock"
1113 ocispecs "github.com/opencontainers/image-spec/specs-go/v1"
1214 "github.com/pkg/errors"
@@ -15,6 +17,8 @@ import (
1517const (
1618 // lockFileSuffix is the suffix of the lock file
1719 lockFileSuffix = ".lock"
20+
21+ annotationImageName = "io.containerd.image.name"
1822)
1923
2024type StoreIndex struct {
@@ -23,6 +27,19 @@ type StoreIndex struct {
2327 layoutPath string
2428}
2529
30+ type NameOrTag struct {
31+ isTag bool
32+ value string
33+ }
34+
35+ func Name (name string ) NameOrTag {
36+ return NameOrTag {value : name }
37+ }
38+
39+ func Tag (tag string ) NameOrTag {
40+ return NameOrTag {isTag : true , value : tag }
41+ }
42+
2643func NewStoreIndex (storePath string ) StoreIndex {
2744 indexPath := path .Join (storePath , ocispecs .ImageIndexFile )
2845 layoutPath := path .Join (storePath , ocispecs .ImageLayoutFile )
@@ -61,7 +78,7 @@ func (s StoreIndex) Read() (*ocispecs.Index, error) {
6178 return & idx , nil
6279}
6380
64- func (s StoreIndex ) Put (tag string , desc ocispecs.Descriptor ) error {
81+ func (s StoreIndex ) Put (desc ocispecs.Descriptor , names ... NameOrTag ) error {
6582 // lock the store to prevent concurrent access
6683 lock := flock .New (s .lockPath )
6784 locked , err := lock .TryLock ()
@@ -107,8 +124,19 @@ func (s StoreIndex) Put(tag string, desc ocispecs.Descriptor) error {
107124 }
108125
109126 setOCIIndexDefaults (& idx )
110- if err = insertDesc (& idx , desc , tag ); err != nil {
111- return err
127+
128+ namesp := make ([]* NameOrTag , 0 , len (names ))
129+ for _ , n := range names {
130+ namesp = append (namesp , & n )
131+ }
132+ if len (names ) == 0 {
133+ namesp = append (namesp , nil )
134+ }
135+
136+ for _ , name := range namesp {
137+ if err = insertDesc (& idx , desc , name ); err != nil {
138+ return err
139+ }
112140 }
113141
114142 idxData , err = json .Marshal (idx )
@@ -130,6 +158,12 @@ func (s StoreIndex) Get(tag string) (*ocispecs.Descriptor, error) {
130158 return nil , err
131159 }
132160
161+ for _ , m := range idx .Manifests {
162+ if t , ok := m .Annotations [annotationImageName ]; ok && t == tag {
163+ return & m , nil
164+ }
165+ }
166+
133167 for _ , m := range idx .Manifests {
134168 if t , ok := m .Annotations [ocispecs .AnnotationRefName ]; ok && t == tag {
135169 return & m , nil
@@ -165,20 +199,34 @@ func setOCIIndexDefaults(index *ocispecs.Index) {
165199
166200// insertDesc puts desc to index with tag.
167201// Existing manifests with the same tag will be removed from the index.
168- func insertDesc (index * ocispecs.Index , desc ocispecs.Descriptor , tag string ) error {
202+ func insertDesc (index * ocispecs.Index , in ocispecs.Descriptor , name * NameOrTag ) error {
169203 if index == nil {
170204 return nil
171205 }
172206
173- if tag != "" {
207+ // make a copy to not modify the input descriptor
208+ desc := in
209+ desc .Annotations = maps .Clone (in .Annotations )
210+
211+ if name != nil {
174212 if desc .Annotations == nil {
175213 desc .Annotations = make (map [string ]string )
176214 }
177- desc .Annotations [ocispecs .AnnotationRefName ] = tag
178- // remove existing manifests with the same tag
215+ imgName , refName := name .value , name .value
216+ if name .isTag {
217+ imgName = ""
218+ } else {
219+ refName = ociReferenceName (imgName )
220+ }
221+
222+ if imgName != "" {
223+ desc .Annotations [annotationImageName ] = imgName
224+ }
225+ desc .Annotations [ocispecs .AnnotationRefName ] = refName
226+ // remove existing manifests with the same tag/name
179227 var manifests []ocispecs.Descriptor
180228 for _ , m := range index .Manifests {
181- if m .Annotations [ocispecs .AnnotationRefName ] != tag {
229+ if m .Annotations [ocispecs .AnnotationRefName ] != refName || m . Annotations [ annotationImageName ] != imgName {
182230 manifests = append (manifests , m )
183231 }
184232 }
@@ -187,3 +235,20 @@ func insertDesc(index *ocispecs.Index, desc ocispecs.Descriptor, tag string) err
187235 index .Manifests = append (index .Manifests , desc )
188236 return nil
189237}
238+
239+ // ociReferenceName takes the loosely defined reference name same way as
240+ // containerd tar exporter does.
241+ func ociReferenceName (name string ) string {
242+ // OCI defines the reference name as only a tag excluding the
243+ // repository. The containerd annotation contains the full image name
244+ // since the tag is insufficient for correctly naming and referring to an
245+ // image
246+ var ociRef string
247+ if spec , err := reference .Parse (name ); err == nil {
248+ ociRef = spec .Object
249+ } else {
250+ ociRef = name
251+ }
252+
253+ return ociRef
254+ }
0 commit comments