diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 724f3813e..b2f0ceedb 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -24,7 +24,7 @@ env: jobs: test: - runs-on: ubuntu-20.04 + runs-on: codebuild-new_soci_CLI-${{ github.run_id }}-${{ github.run_attempt }}-ubuntu-7.0-xlarge timeout-minutes: 15 steps: - uses: actions/checkout@v4 @@ -34,7 +34,7 @@ jobs: - run: make - run: make test integration: - runs-on: ubuntu-20.04 + runs-on: codebuild-new_soci_CLI-${{ github.run_id }}-${{ github.run_attempt }}-ubuntu-7.0-xlarge timeout-minutes: 40 strategy: fail-fast: false diff --git a/cmd/soci/commands/create.go b/cmd/soci/commands/create.go index e4d8aa615..fd55c2a83 100644 --- a/cmd/soci/commands/create.go +++ b/cmd/soci/commands/create.go @@ -122,12 +122,7 @@ var CreateCommand = cli.Command{ return err } - sociIndexWithMetadata, err := builder.Build(ctx, srcImg) - if err != nil { - return err - } - - err = soci.WriteSociIndex(ctx, sociIndexWithMetadata, blobStore, builder.ArtifactsDb) + _, err = builder.BuildAndPush(ctx, blobStore, srcImg) if err != nil { return err } diff --git a/integration/pull_test.go b/integration/pull_test.go index 0bfe1d372..d9929c470 100644 --- a/integration/pull_test.go +++ b/integration/pull_test.go @@ -236,7 +236,7 @@ func TestLazyPullWithSparseIndex(t *testing.T) { } func checkFuseMounts(t *testing.T, sh *shell.Shell, remoteSnapshotsExpectedCount int) { - mounts := string(sh.O("mount")) + mounts := string(sh.O("cat", "/proc/mounts")) remoteSnapshotsActualCount := strings.Count(mounts, "fuse.rawBridge") if remoteSnapshotsExpectedCount != remoteSnapshotsActualCount { t.Fatalf("incorrect number of remote snapshots; expected=%d, actual=%d", diff --git a/soci/soci_index.go b/soci/soci_index.go index f29d9c7f4..4c2cdd406 100644 --- a/soci/soci_index.go +++ b/soci/soci_index.go @@ -305,18 +305,42 @@ func NewIndexBuilder(contentStore content.Store, blobStore orascontent.Storage, }, nil } +// BuildAndPush builds a SOCI index, pushes and labels the artifacts, and returns the SOCI index. +func (b *IndexBuilder) BuildAndPush(ctx context.Context, blobStore store.Store, img images.Image) (*IndexWithMetadata, error) { + sociIndexWithMetadata, done, err := b.Build(ctx, blobStore, img) + if err != nil { + return nil, err + } + defer done(ctx) + + err = WriteSociIndex(ctx, sociIndexWithMetadata, blobStore, b.ArtifactsDb) + if err != nil { + return nil, err + } + return sociIndexWithMetadata, nil +} + // Build builds a soci index for `img` and return the index with metadata. -func (b *IndexBuilder) Build(ctx context.Context, img images.Image) (*IndexWithMetadata, error) { +// To prevent garbage collection of the zTOCs pushed by this command, +// we obtain a lease from containerd and also return it. +// It is the responsibility of the caller to terminate the lease. +// Ideally the lease ends after labelling and pushing the index and related zTOCs. +func (b *IndexBuilder) Build(ctx context.Context, blobStore store.Store, img images.Image) (*IndexWithMetadata, store.CleanupFunc, error) { // we get manifest descriptor before calling images.Manifest, since after calling // images.Manifest, images.Children will error out when reading the manifest blob (this happens on containerd side) imgManifestDesc, err := GetImageManifestDescriptor(ctx, b.contentStore, img.Target, platforms.OnlyStrict(b.config.platform)) if err != nil { - return nil, err + return nil, nil, err } manifest, err := images.Manifest(ctx, b.contentStore, img.Target, platforms.OnlyStrict(b.config.platform)) if err != nil { - return nil, err + return nil, nil, err + } + + ctx, done, err := blobStore.BatchOpen(ctx) + if err != nil { + return nil, nil, err } // attempt to build a ztoc for each layer @@ -357,8 +381,8 @@ func (b *IndexBuilder) Build(ctx context.Context, img images.Image) (*IndexWithM for _, err := range errs { errWrap = fmt.Errorf("%w; %v", errWrap, err) } - - return nil, errWrap + done(ctx) + return nil, nil, errWrap } ztocsDesc := make([]ocispec.Descriptor, 0, len(sociLayersDesc)) @@ -369,7 +393,8 @@ func (b *IndexBuilder) Build(ctx context.Context, img images.Image) (*IndexWithM } if len(ztocsDesc) == 0 { - return nil, ErrEmptyIndex + done(ctx) + return nil, nil, ErrEmptyIndex } annotations := map[string]string{ @@ -388,7 +413,7 @@ func (b *IndexBuilder) Build(ctx context.Context, img images.Image) (*IndexWithM Platform: &b.config.platform, ImageDigest: img.Target.Digest, CreatedAt: time.Now(), - }, nil + }, done, nil } // buildSociLayer builds a ztoc for an image layer (`desc`) and returns ztoc descriptor.