@@ -2,10 +2,12 @@ package main
22
33import (
44 "bytes"
5+ "cmp"
56 "context"
67 "encoding/json"
78 "errors"
89 "io"
10+ "iter"
911 "log"
1012 "net/http"
1113 "os"
@@ -40,7 +42,7 @@ type containerdRegistry struct {
4042 client * containerd.Client
4143}
4244
43- func (r containerdRegistry ) Repositories (ctx context.Context , startAfter string ) ociregistry. Seq [string ] {
45+ func (r containerdRegistry ) Repositories (ctx context.Context , startAfter string ) iter. Seq2 [string , error ] {
4446 is := r .client .ImageService ()
4547
4648 images , err := is .List (ctx )
@@ -80,7 +82,7 @@ func (r containerdRegistry) Repositories(ctx context.Context, startAfter string)
8082 return ociregistry.SliceSeq [string ](names )
8183}
8284
83- func (r containerdRegistry ) Tags (ctx context.Context , repo string , startAfter string ) ociregistry. Seq [string ] {
85+ func (r containerdRegistry ) Tags (ctx context.Context , repo string , startAfter string ) iter. Seq2 [string , error ] {
8486 is := r .client .ImageService ()
8587
8688 images , err := is .List (ctx , "name~=" + strconv .Quote ("^" + regexp .QuoteMeta (repo )+ ":" ))
@@ -117,6 +119,62 @@ func (r containerdRegistry) Tags(ctx context.Context, repo string, startAfter st
117119 return ociregistry.SliceSeq [string ](tags )
118120}
119121
122+ func (r containerdRegistry ) Referrers (ctx context.Context , repo string , digest ociregistry.Digest , artifactType string ) iter.Seq2 [ociregistry.Descriptor , error ] {
123+ yieldBreak := errors .New ("break " + string (digest ))
124+ return func (yield func (ociregistry.Descriptor , error ) bool ) {
125+ cs := r .client .ContentStore ()
126+ // TODO should this be a new "background" context?
127+ err := cs .Walk (ctx , func (info content.Info ) error {
128+ // TODO we should really pull "artifactType" and "mediaType" up into labels so we don't have to fetch/parse the manifest here to get them -- maybe even annotations?
129+ desc := ociregistry.Descriptor {
130+ Digest : info .Digest ,
131+ Size : info .Size ,
132+ }
133+ ra , err := cs .ReaderAt (ctx , desc )
134+ if err != nil {
135+ if ! yield (ociregistry.Descriptor {}, err ) {
136+ return yieldBreak
137+ }
138+ return nil
139+ }
140+ // wrap in a LimitedReader here to make sure we don't read an enormous amount of valid but useless JSON that DoS's us
141+ reader := io .LimitReader (content .NewReader (ra ), manifestSizeLimit )
142+ fields := struct {
143+ MediaType string `json:"mediaType"`
144+ ArtifactType string `json:"artifactType"`
145+ Config struct {
146+ // for the fallback ("If the artifactType is empty or missing in the image manifest, the value of artifactType MUST be set to the config descriptor mediaType value.")
147+ MediaType string `json:"mediaType"`
148+ } `json:"config"`
149+ Annotations map [string ]string `json:"annotations"`
150+ }{}
151+ if err := json .NewDecoder (reader ).Decode (& fields ); err != nil {
152+ if ! yield (ociregistry.Descriptor {}, err ) {
153+ return yieldBreak
154+ }
155+ return nil
156+ }
157+ desc .MediaType = fields .MediaType
158+ desc .ArtifactType = cmp .Or (fields .ArtifactType , fields .Config .MediaType )
159+ desc .Annotations = fields .Annotations
160+ if artifactType != "" && desc .ArtifactType != artifactType {
161+ return nil
162+ }
163+ if ! yield (desc , nil ) {
164+ return yieldBreak
165+ }
166+ return nil
167+ }, `labels."containerd.io/gc.bref.content.subject"==` + strconv .Quote (string (digest )))
168+ if err == yieldBreak {
169+ return
170+ }
171+ if err != nil {
172+ yield (ociregistry.Descriptor {}, err )
173+ return
174+ }
175+ }
176+ }
177+
120178type containerdBlobReader struct {
121179 client * containerd.Client
122180 ctx context.Context
@@ -130,7 +188,7 @@ func (br *containerdBlobReader) validate() error {
130188 info , err := br .client .ContentStore ().Info (br .ctx , br .desc .Digest )
131189 if err != nil {
132190 if errdefs .IsNotFound (err ) {
133- return ociregistry .ErrBlobUnknown
191+ return errors . Join ( err , ociregistry .ErrBlobUnknown )
134192 }
135193 return err
136194 }
@@ -223,7 +281,11 @@ func (r containerdRegistry) GetBlobRange(ctx context.Context, repo string, diges
223281 requestedSize := offset1 - offset0
224282 if offset1 < 0 || offset0 + requestedSize > br .desc .Size {
225283 // "If offset1 is negative or exceeds the actual size of the blob, GetBlobRange will return all the data starting from offset0."
226- return br , nil
284+ requestedSize = br .desc .Size - offset0
285+ }
286+ if offset0 < 0 { // TODO https://github.com/cue-labs/oci/issues/47
287+ offset0 = br .desc .Size - offset1
288+ requestedSize = offset1
227289 }
228290
229291 ra , err := br .ensureReaderAt ()
@@ -245,7 +307,7 @@ func (r containerdRegistry) GetManifest(ctx context.Context, repo string, digest
245307 ra , err := r .client .ContentStore ().ReaderAt (ctx , desc )
246308 if err != nil {
247309 if errdefs .IsNotFound (err ) {
248- return nil , ociregistry .ErrManifestUnknown
310+ return nil , errors . Join ( err , ociregistry .ErrManifestUnknown )
249311 }
250312 return nil , err
251313 }
@@ -290,7 +352,7 @@ func (r containerdRegistry) GetTag(ctx context.Context, repo string, tagName str
290352 }
291353
292354 // TODO differentiate ErrNameUnknown (repo unknown) from ErrManifestUnknown ?
293- return nil , ociregistry .ErrManifestUnknown
355+ return nil , errors . Join ( err , ociregistry .ErrManifestUnknown )
294356 }
295357 return nil , err
296358 }
@@ -330,16 +392,34 @@ func (r containerdRegistry) ResolveTag(ctx context.Context, repo string, tagName
330392
331393func (r containerdRegistry ) DeleteBlob (ctx context.Context , repo string , digest ociregistry.Digest ) error {
332394 // TODO should we stop this from removing things that are still tagged or children of tagged?
333- return r .client .ContentStore ().Delete (ctx , digest )
395+ if err := r .client .ContentStore ().Delete (ctx , digest ); err != nil {
396+ if errdefs .IsNotFound (err ) {
397+ return errors .Join (err , ociregistry .ErrBlobUnknown )
398+ }
399+ return err
400+ }
401+ return nil
334402}
335403
336404func (r containerdRegistry ) DeleteManifest (ctx context.Context , repo string , digest ociregistry.Digest ) error {
337405 // TODO should we stop this from removing things that are still tagged or children of tagged?
338- return r .client .ContentStore ().Delete (ctx , digest )
406+ if err := r .client .ContentStore ().Delete (ctx , digest ); err != nil {
407+ if errdefs .IsNotFound (err ) {
408+ return errors .Join (err , ociregistry .ErrManifestUnknown )
409+ }
410+ return err
411+ }
412+ return nil
339413}
340414
341415func (r containerdRegistry ) DeleteTag (ctx context.Context , repo string , name string ) error {
342- return r .client .ImageService ().Delete (ctx , repo + ":" + name )
416+ if err := r .client .ImageService ().Delete (ctx , repo + ":" + name ); err != nil {
417+ if errdefs .IsNotFound (err ) {
418+ return errors .Join (err , ociregistry .ErrManifestUnknown )
419+ }
420+ return err
421+ }
422+ return nil
343423}
344424
345425func (r containerdRegistry ) PushBlob (ctx context.Context , repo string , desc ociregistry.Descriptor , reader io.Reader ) (ociregistry.Descriptor , error ) {
@@ -367,6 +447,9 @@ func (r containerdRegistry) PushBlob(ctx context.Context, repo string, desc ocir
367447 if err := content .WriteBlob (ctx , cs , ingestRef , reader , desc ); err != nil {
368448 _ = cs .Abort (ctx , ingestRef )
369449 _ = deleteLease (ctx )
450+ if errdefs .IsFailedPrecondition (err ) {
451+ err = errors .Join (err , ociregistry .ErrDigestInvalid )
452+ }
370453 return ociregistry.Descriptor {}, err
371454 }
372455
@@ -425,6 +508,9 @@ func (bw *containerdBlobWriter) Commit(digest ociregistry.Digest) (ociregistry.D
425508 return ociregistry.Descriptor {}, err
426509 }
427510 if err := bw .Writer .Commit (bw .ctx , 0 , digest ); err != nil && ! errdefs .IsAlreadyExists (err ) {
511+ if errdefs .IsFailedPrecondition (err ) {
512+ err = errors .Join (err , ociregistry .ErrDigestInvalid )
513+ }
428514 return ociregistry.Descriptor {}, err
429515 }
430516 return ociregistry.Descriptor {
@@ -529,7 +615,6 @@ func (r containerdRegistry) PushManifest(ctx context.Context, repo string, tag s
529615 "l" : manifestChildren .Layers ,
530616 } {
531617 for i , d := range list {
532- d := d
533618 labelMappings [prefix + "." + strconv .Itoa (i )] = & d
534619 }
535620 }
@@ -555,6 +640,9 @@ func (r containerdRegistry) PushManifest(ctx context.Context, repo string, tag s
555640 return ociregistry.Descriptor {}, err
556641 }
557642 if err := content .WriteBlob (ctx , cs , ingestRef , bytes .NewReader (contents ), desc , content .WithLabels (labels )); err != nil {
643+ if errdefs .IsFailedPrecondition (err ) {
644+ err = errors .Join (err , ociregistry .ErrDigestInvalid )
645+ }
558646 return ociregistry.Descriptor {}, err
559647 }
560648
0 commit comments