@@ -111,6 +111,7 @@ type Index struct {
111111// IndexWithMetadata has a soci `Index` and its metadata.
112112type IndexWithMetadata struct {
113113 Index * Index
114+ Desc ocispec.Descriptor
114115 Platform * ocispec.Platform
115116 ImageDigest digest.Digest
116117 CreatedAt time.Time
@@ -299,6 +300,7 @@ type BuildOption func(*buildConfig) error
299300// buildConfig represents the config for a single index build operation.
300301type buildConfig struct {
301302 platform ocispec.Platform
303+ gcRoot bool
302304}
303305
304306// WithPlatform sets the platform for a single build operation.
@@ -309,6 +311,22 @@ func WithPlatform(platform ocispec.Platform) BuildOption {
309311 }
310312}
311313
314+ // WithNoGarbageCollectionLabel prevents the index builder from putting
315+ // a root GC label on the soci index. The builder will set content GC labels
316+ // to prevent the ztocs from being garbage collected.
317+ //
318+ // The caller is responsible for putting appropriate GC labels to prevent the
319+ // index from being garbage collected. The caller is also responsible for
320+ // ensuring that the SOCI index does not get garbage collected after the build finishes,
321+ // but before the GC label is applied. This can be done by calling `contentStore.BatchOpen`
322+ // before calling `Build`.
323+ func WithNoGarbageCollectionLabel () BuildOption {
324+ return func (bc * buildConfig ) error {
325+ bc .gcRoot = false
326+ return nil
327+ }
328+ }
329+
312330// IndexBuilder creates soci indices.
313331type IndexBuilder struct {
314332 contentStore content.Store
@@ -349,6 +367,17 @@ func NewIndexBuilder(contentStore content.Store, blobStore store.Store, opts ...
349367// Build builds a soci index for `img` and pushes it with its corresponding zTOCs to the blob store.
350368// Returns the SOCI index and its metadata.
351369func (b * IndexBuilder ) Build (ctx context.Context , img images.Image , opts ... BuildOption ) (* IndexWithMetadata , error ) {
370+ buildCfg := buildConfig {
371+ platform : platforms .DefaultSpec (),
372+ gcRoot : true ,
373+ }
374+ for _ , opt := range opts {
375+ err := opt (& buildCfg )
376+ if err != nil {
377+ return nil , err
378+ }
379+ }
380+
352381 // batch will prevent content from being garbage collected in the middle of the following operations
353382 ctx , done , err := b .blobStore .BatchOpen (ctx )
354383 if err != nil {
@@ -357,13 +386,13 @@ func (b *IndexBuilder) Build(ctx context.Context, img images.Image, opts ...Buil
357386 defer done (ctx )
358387
359388 // Create and push zTOCs to blob store
360- index , err := b .build (ctx , img , opts ... )
389+ index , err := b .build (ctx , img , buildCfg )
361390 if err != nil {
362391 return nil , err
363392 }
364393
365394 // Label zTOCs and push SOCI index
366- err = b .writeSociIndex (ctx , index )
395+ index . Desc , err = b .writeSociIndex (ctx , index , buildCfg . gcRoot )
367396 if err != nil {
368397 return nil , err
369398 }
@@ -374,17 +403,7 @@ func (b *IndexBuilder) Build(ctx context.Context, img images.Image, opts ...Buil
374403// build attempts to create a zTOC in each layer and pushes the zTOC to the blob store.
375404// It then creates the SOCI index and returns it with some metadata.
376405// This should be done within a Batch and followed by writeSociIndex() to prevent garbage collection.
377- func (b * IndexBuilder ) build (ctx context.Context , img images.Image , opts ... BuildOption ) (* IndexWithMetadata , error ) {
378- buildCfg := buildConfig {
379- platform : platforms .DefaultSpec (),
380- }
381- for _ , opt := range opts {
382- err := opt (& buildCfg )
383- if err != nil {
384- return nil , err
385- }
386- }
387-
406+ func (b * IndexBuilder ) build (ctx context.Context , img images.Image , buildCfg buildConfig ) (* IndexWithMetadata , error ) {
388407 platformMatcher := platforms .OnlyStrict (buildCfg .platform )
389408 // we get manifest descriptor before calling images.Manifest, since after calling
390409 // images.Manifest, images.Children will error out when reading the manifest blob (this happens on containerd side)
@@ -619,10 +638,10 @@ func GetImageManifestDescriptor(ctx context.Context, cs content.Store, imageTarg
619638
620639// writeSociIndex writes the SociIndex manifest to the blob store.
621640// This should be done within a Batch to prevent garbage collection.
622- func (b * IndexBuilder ) writeSociIndex (ctx context.Context , indexWithMetadata * IndexWithMetadata ) error {
641+ func (b * IndexBuilder ) writeSociIndex (ctx context.Context , indexWithMetadata * IndexWithMetadata , gcRoot bool ) (ocispec. Descriptor , error ) {
623642 manifest , err := MarshalIndex (indexWithMetadata .Index )
624643 if err != nil {
625- return err
644+ return ocispec. Descriptor {}, err
626645 }
627646
628647 // If we're serializing the SOCI index as an OCI 1.0 Manifest, create an
@@ -631,7 +650,7 @@ func (b *IndexBuilder) writeSociIndex(ctx context.Context, indexWithMetadata *In
631650 if indexWithMetadata .Index .MediaType == ocispec .MediaTypeImageManifest {
632651 err = b .blobStore .Push (ctx , defaultConfigDescriptor , bytes .NewReader (defaultConfigContent ))
633652 if err != nil && ! errors .Is (err , errdef .ErrAlreadyExists ) {
634- return fmt .Errorf ("error creating OCI 1.0 empty config: %w" , err )
653+ return ocispec. Descriptor {}, fmt .Errorf ("error creating OCI 1.0 empty config: %w" , err )
635654 }
636655 }
637656
@@ -644,18 +663,20 @@ func (b *IndexBuilder) writeSociIndex(ctx context.Context, indexWithMetadata *In
644663
645664 err = b .blobStore .Push (ctx , desc , bytes .NewReader (manifest ))
646665 if err != nil && ! errors .Is (err , errdef .ErrAlreadyExists ) {
647- return fmt .Errorf ("cannot write SOCI index to local store: %w" , err )
666+ return ocispec. Descriptor {}, fmt .Errorf ("cannot write SOCI index to local store: %w" , err )
648667 }
649668
650669 log .G (ctx ).WithField ("digest" , dgst .String ()).Debugf ("soci index has been written" )
651670
652- err = store .LabelGCRoot (ctx , b .blobStore , desc )
653- if err != nil {
654- return fmt .Errorf ("cannot apply garbage collection label to index %s: %w" , desc .Digest .String (), err )
671+ if gcRoot {
672+ err = store .LabelGCRoot (ctx , b .blobStore , desc )
673+ if err != nil {
674+ return ocispec.Descriptor {}, fmt .Errorf ("cannot apply garbage collection label to index %s: %w" , desc .Digest .String (), err )
675+ }
655676 }
656677 err = store .LabelGCRefContent (ctx , b .blobStore , desc , "config" , defaultConfigDescriptor .Digest .String ())
657678 if err != nil {
658- return fmt .Errorf ("cannot apply garbage collection label to index %s referencing default config: %w" , desc .Digest .String (), err )
679+ return ocispec. Descriptor {}, fmt .Errorf ("cannot apply garbage collection label to index %s referencing default config: %w" , desc .Digest .String (), err )
659680 }
660681
661682 var allErr error
@@ -666,13 +687,13 @@ func (b *IndexBuilder) writeSociIndex(ctx context.Context, indexWithMetadata *In
666687 }
667688 }
668689 if allErr != nil {
669- return fmt .Errorf ("cannot apply one or more garbage collection labels to index %s: %w" , desc .Digest .String (), allErr )
690+ return ocispec. Descriptor {}, fmt .Errorf ("cannot apply one or more garbage collection labels to index %s: %w" , desc .Digest .String (), allErr )
670691 }
671692
672693 refers := indexWithMetadata .Index .Subject
673694
674695 if refers == nil {
675- return errors .New ("cannot write soci index: the Refers field is nil" )
696+ return ocispec. Descriptor {}, errors .New ("cannot write soci index: the Refers field is nil" )
676697 }
677698
678699 // this entry is persisted to be used by cli push
@@ -687,7 +708,7 @@ func (b *IndexBuilder) writeSociIndex(ctx context.Context, indexWithMetadata *In
687708 MediaType : indexWithMetadata .Index .MediaType ,
688709 CreatedAt : indexWithMetadata .CreatedAt ,
689710 }
690- return b .config .artifactsDb .WriteArtifactEntry (entry )
711+ return desc , b .config .artifactsDb .WriteArtifactEntry (entry )
691712}
692713
693714func (b * IndexBuilder ) maybeAddDisableXattrAnnotation (ztocDesc * ocispec.Descriptor , ztoc * ztoc.Ztoc ) {
0 commit comments