@@ -39,7 +39,6 @@ import (
3939 "github.com/opencontainers/go-digest"
4040 ocispec "github.com/opencontainers/image-spec/specs-go/v1"
4141
42- orascontent "oras.land/oras-go/v2/content"
4342 "oras.land/oras-go/v2/errdef"
4443)
4544
@@ -273,14 +272,14 @@ func WithArtifactsDb(db *ArtifactsDb) BuildOption {
273272// IndexBuilder creates soci indices.
274273type IndexBuilder struct {
275274 contentStore content.Store
276- blobStore orascontent. Storage
275+ blobStore store. Store
277276 ArtifactsDb * ArtifactsDb
278277 config * buildConfig
279278 ztocBuilder * ztoc.Builder
280279}
281280
282281// 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 ) {
284283 defaultPlatform := platforms .DefaultSpec ()
285284 config := & buildConfig {
286285 spanSize : defaultSpanSize ,
@@ -305,8 +304,35 @@ func NewIndexBuilder(contentStore content.Store, blobStore orascontent.Storage,
305304 }, nil
306305}
307306
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.
309309func (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 ) {
310336 // we get manifest descriptor before calling images.Manifest, since after calling
311337 // images.Manifest, images.Children will error out when reading the manifest blob (this happens on containerd side)
312338 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
357383 for _ , err := range errs {
358384 errWrap = fmt .Errorf ("%w; %v" , errWrap , err )
359385 }
360-
361386 return nil , errWrap
362387 }
363388
@@ -393,6 +418,7 @@ func (b *IndexBuilder) Build(ctx context.Context, img images.Image) (*IndexWithM
393418
394419// buildSociLayer builds a ztoc for an image layer (`desc`) and returns ztoc descriptor.
395420// 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.
396422func (b * IndexBuilder ) buildSociLayer (ctx context.Context , desc ocispec.Descriptor ) (* ocispec.Descriptor , error ) {
397423 if ! images .IsLayerType (desc .MediaType ) {
398424 return nil , errNotLayerType
@@ -541,15 +567,9 @@ func GetImageManifestDescriptor(ctx context.Context, cs content.Store, imageTarg
541567 return nil , nil
542568}
543569
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 {
553573 manifest , err := MarshalIndex (indexWithMetadata .Index )
554574 if err != nil {
555575 return err
@@ -559,7 +579,7 @@ func WriteSociIndex(ctx context.Context, indexWithMetadata *IndexWithMetadata, c
559579 // empty config objct in the store as well. We will need to push this to the
560580 // registry later.
561581 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 ))
563583 if err != nil && ! errors .Is (err , errdef .ErrAlreadyExists ) {
564584 return fmt .Errorf ("error creating OCI 1.0 empty config: %w" , err )
565585 }
@@ -572,25 +592,25 @@ func WriteSociIndex(ctx context.Context, indexWithMetadata *IndexWithMetadata, c
572592 Size : size ,
573593 }
574594
575- err = contentStore .Push (ctx , desc , bytes .NewReader (manifest ))
595+ err = b . blobStore .Push (ctx , desc , bytes .NewReader (manifest ))
576596 if err != nil && ! errors .Is (err , errdef .ErrAlreadyExists ) {
577597 return fmt .Errorf ("cannot write SOCI index to local store: %w" , err )
578598 }
579599
580600 log .G (ctx ).WithField ("digest" , dgst .String ()).Debugf ("soci index has been written" )
581601
582- err = store .LabelGCRoot (ctx , contentStore , desc )
602+ err = store .LabelGCRoot (ctx , b . blobStore , desc )
583603 if err != nil {
584604 return fmt .Errorf ("cannot apply garbage collection label to index %s: %w" , desc .Digest .String (), err )
585605 }
586- err = store .LabelGCRefContent (ctx , contentStore , desc , "config" , defaultConfigDescriptor .Digest .String ())
606+ err = store .LabelGCRefContent (ctx , b . blobStore , desc , "config" , defaultConfigDescriptor .Digest .String ())
587607 if err != nil {
588608 return fmt .Errorf ("cannot apply garbage collection label to index %s referencing default config: %w" , desc .Digest .String (), err )
589609 }
590610
591611 var allErr error
592612 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 ())
594614 if err != nil {
595615 errors .Join (allErr , err )
596616 }
@@ -617,7 +637,7 @@ func WriteSociIndex(ctx context.Context, indexWithMetadata *IndexWithMetadata, c
617637 MediaType : indexWithMetadata .Index .MediaType ,
618638 CreatedAt : indexWithMetadata .CreatedAt ,
619639 }
620- return artifactsDb .WriteArtifactEntry (entry )
640+ return b . ArtifactsDb .WriteArtifactEntry (entry )
621641}
622642
623643func (b * IndexBuilder ) maybeAddDisableXattrAnnotation (ztocDesc * ocispec.Descriptor , ztoc * ztoc.Ztoc ) {
0 commit comments