@@ -39,7 +39,6 @@ import (
39
39
"github.com/opencontainers/go-digest"
40
40
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
41
41
42
- orascontent "oras.land/oras-go/v2/content"
43
42
"oras.land/oras-go/v2/errdef"
44
43
)
45
44
@@ -273,14 +272,14 @@ func WithArtifactsDb(db *ArtifactsDb) BuildOption {
273
272
// IndexBuilder creates soci indices.
274
273
type IndexBuilder struct {
275
274
contentStore content.Store
276
- blobStore orascontent. Storage
275
+ blobStore store. Store
277
276
ArtifactsDb * ArtifactsDb
278
277
config * buildConfig
279
278
ztocBuilder * ztoc.Builder
280
279
}
281
280
282
281
// NewIndexBuilder returns an `IndexBuilder` that is used to create soci indices.
283
- func NewIndexBuilder (contentStore content.Store , blobStore orascontent. Storage , artifactsDb * ArtifactsDb , opts ... BuildOption ) (* IndexBuilder , error ) {
282
+ func NewIndexBuilder (contentStore content.Store , blobStore store. Store , artifactsDb * ArtifactsDb , opts ... BuildOption ) (* IndexBuilder , error ) {
284
283
defaultPlatform := platforms .DefaultSpec ()
285
284
config := & buildConfig {
286
285
spanSize : defaultSpanSize ,
@@ -305,8 +304,35 @@ func NewIndexBuilder(contentStore content.Store, blobStore orascontent.Storage,
305
304
}, nil
306
305
}
307
306
308
- // Build builds a soci index for `img` and return the index with metadata.
307
+ // Build builds a soci index for `img` and pushes it with its corresponding zTOCs to the blob store.
308
+ // Returns the SOCI index and its metadata.
309
309
func (b * IndexBuilder ) Build (ctx context.Context , img images.Image ) (* IndexWithMetadata , error ) {
310
+ // batch will prevent content from being garbage collected in the middle of the following operations
311
+ ctx , done , err := b .blobStore .BatchOpen (ctx )
312
+ if err != nil {
313
+ return nil , err
314
+ }
315
+ defer done (ctx )
316
+
317
+ // Create and push zTOCs to blob store
318
+ index , err := b .build (ctx , img )
319
+ if err != nil {
320
+ return nil , err
321
+ }
322
+
323
+ // Label zTOCs and push SOCI index
324
+ err = b .writeSociIndex (ctx , index )
325
+ if err != nil {
326
+ return nil , err
327
+ }
328
+
329
+ return index , nil
330
+ }
331
+
332
+ // build attempts to create a zTOC in each layer and pushes the zTOC to the blob store.
333
+ // It then creates the SOCI index and returns it with some metadata.
334
+ // This should be done within a Batch and followed by writeSociIndex() to prevent garbage collection.
335
+ func (b * IndexBuilder ) build (ctx context.Context , img images.Image ) (* IndexWithMetadata , error ) {
310
336
// we get manifest descriptor before calling images.Manifest, since after calling
311
337
// images.Manifest, images.Children will error out when reading the manifest blob (this happens on containerd side)
312
338
imgManifestDesc , err := GetImageManifestDescriptor (ctx , b .contentStore , img .Target , platforms .OnlyStrict (b .config .platform ))
@@ -357,7 +383,6 @@ func (b *IndexBuilder) Build(ctx context.Context, img images.Image) (*IndexWithM
357
383
for _ , err := range errs {
358
384
errWrap = fmt .Errorf ("%w; %v" , errWrap , err )
359
385
}
360
-
361
386
return nil , errWrap
362
387
}
363
388
@@ -393,6 +418,7 @@ func (b *IndexBuilder) Build(ctx context.Context, img images.Image) (*IndexWithM
393
418
394
419
// buildSociLayer builds a ztoc for an image layer (`desc`) and returns ztoc descriptor.
395
420
// It may skip building ztoc (e.g., if layer size < `minLayerSize`) and return nil.
421
+ // This should be done within a Batch and followed by Label calls to prevent garbage collection.
396
422
func (b * IndexBuilder ) buildSociLayer (ctx context.Context , desc ocispec.Descriptor ) (* ocispec.Descriptor , error ) {
397
423
if ! images .IsLayerType (desc .MediaType ) {
398
424
return nil , errNotLayerType
@@ -541,15 +567,9 @@ func GetImageManifestDescriptor(ctx context.Context, cs content.Store, imageTarg
541
567
return nil , nil
542
568
}
543
569
544
- // WriteSociIndex writes the SociIndex manifest to oras `store`.
545
- func WriteSociIndex (ctx context.Context , indexWithMetadata * IndexWithMetadata , contentStore store.Store , artifactsDb * ArtifactsDb ) error {
546
- // batch will prevent content from being garbage collected in the middle of the following operations
547
- ctx , batchDone , err := contentStore .BatchOpen (ctx )
548
- if err != nil {
549
- return err
550
- }
551
- defer batchDone (ctx )
552
-
570
+ // writeSociIndex writes the SociIndex manifest to the blob store.
571
+ // This should be done within a Batch to prevent garbage collection.
572
+ func (b * IndexBuilder ) writeSociIndex (ctx context.Context , indexWithMetadata * IndexWithMetadata ) error {
553
573
manifest , err := MarshalIndex (indexWithMetadata .Index )
554
574
if err != nil {
555
575
return err
@@ -559,7 +579,7 @@ func WriteSociIndex(ctx context.Context, indexWithMetadata *IndexWithMetadata, c
559
579
// empty config objct in the store as well. We will need to push this to the
560
580
// registry later.
561
581
if indexWithMetadata .Index .MediaType == ocispec .MediaTypeImageManifest {
562
- err = contentStore .Push (ctx , defaultConfigDescriptor , bytes .NewReader (defaultConfigContent ))
582
+ err = b . blobStore .Push (ctx , defaultConfigDescriptor , bytes .NewReader (defaultConfigContent ))
563
583
if err != nil && ! errors .Is (err , errdef .ErrAlreadyExists ) {
564
584
return fmt .Errorf ("error creating OCI 1.0 empty config: %w" , err )
565
585
}
@@ -572,25 +592,25 @@ func WriteSociIndex(ctx context.Context, indexWithMetadata *IndexWithMetadata, c
572
592
Size : size ,
573
593
}
574
594
575
- err = contentStore .Push (ctx , desc , bytes .NewReader (manifest ))
595
+ err = b . blobStore .Push (ctx , desc , bytes .NewReader (manifest ))
576
596
if err != nil && ! errors .Is (err , errdef .ErrAlreadyExists ) {
577
597
return fmt .Errorf ("cannot write SOCI index to local store: %w" , err )
578
598
}
579
599
580
600
log .G (ctx ).WithField ("digest" , dgst .String ()).Debugf ("soci index has been written" )
581
601
582
- err = store .LabelGCRoot (ctx , contentStore , desc )
602
+ err = store .LabelGCRoot (ctx , b . blobStore , desc )
583
603
if err != nil {
584
604
return fmt .Errorf ("cannot apply garbage collection label to index %s: %w" , desc .Digest .String (), err )
585
605
}
586
- err = store .LabelGCRefContent (ctx , contentStore , desc , "config" , defaultConfigDescriptor .Digest .String ())
606
+ err = store .LabelGCRefContent (ctx , b . blobStore , desc , "config" , defaultConfigDescriptor .Digest .String ())
587
607
if err != nil {
588
608
return fmt .Errorf ("cannot apply garbage collection label to index %s referencing default config: %w" , desc .Digest .String (), err )
589
609
}
590
610
591
611
var allErr error
592
612
for i , blob := range indexWithMetadata .Index .Blobs {
593
- err = store .LabelGCRefContent (ctx , contentStore , desc , "ztoc." + strconv .Itoa (i ), blob .Digest .String ())
613
+ err = store .LabelGCRefContent (ctx , b . blobStore , desc , "ztoc." + strconv .Itoa (i ), blob .Digest .String ())
594
614
if err != nil {
595
615
errors .Join (allErr , err )
596
616
}
@@ -617,7 +637,7 @@ func WriteSociIndex(ctx context.Context, indexWithMetadata *IndexWithMetadata, c
617
637
MediaType : indexWithMetadata .Index .MediaType ,
618
638
CreatedAt : indexWithMetadata .CreatedAt ,
619
639
}
620
- return artifactsDb .WriteArtifactEntry (entry )
640
+ return b . ArtifactsDb .WriteArtifactEntry (entry )
621
641
}
622
642
623
643
func (b * IndexBuilder ) maybeAddDisableXattrAnnotation (ztocDesc * ocispec.Descriptor , ztoc * ztoc.Ztoc ) {
0 commit comments