Skip to content

Commit

Permalink
Add container run from oci-archive
Browse files Browse the repository at this point in the history
Signed-off-by: Austin Vazquez <macedonv@amazon.com>
  • Loading branch information
austinvazquez committed Oct 14, 2024
1 parent 7ac6183 commit b3e38d1
Show file tree
Hide file tree
Showing 3 changed files with 82 additions and 0 deletions.
28 changes: 28 additions & 0 deletions cmd/nerdctl/container/container_run.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ import (
"github.com/containerd/nerdctl/v2/pkg/containerutil"
"github.com/containerd/nerdctl/v2/pkg/defaults"
"github.com/containerd/nerdctl/v2/pkg/errutil"
"github.com/containerd/nerdctl/v2/pkg/imgutil/load"
"github.com/containerd/nerdctl/v2/pkg/labels"
"github.com/containerd/nerdctl/v2/pkg/logging"
"github.com/containerd/nerdctl/v2/pkg/netutil"
Expand Down Expand Up @@ -362,6 +363,33 @@ func runAction(cmd *cobra.Command, args []string) error {
return err
}

if imageRef := args[0]; strings.HasPrefix(imageRef, "oci-archive://") {
// Load and run the platform specified by the user.
// If none specified, fallback to the default platform.
platform := []string{}
if createOpt.Platform != "" {
platform = append(platform, createOpt.Platform)
}

images, err := load.FromOCIArchive(ctx, client, imageRef, types.ImageLoadOptions{
Stdout: cmd.OutOrStdout(),
GOptions: createOpt.GOptions,
Platform: platform,
AllPlatforms: false,
Quiet: createOpt.ImagePullOpt.Quiet,
})
if err != nil {
return err
}

// Running multi-image OCI archives is not yet supported.
if len(images) != 1 {
return errors.New("multiple images in OCI archive found")
}

args[0] = images[0].Name
}

c, gc, err := container.Create(ctx, client, args, netManager, createOpt)
if err != nil {
if gc != nil {
Expand Down
26 changes: 26 additions & 0 deletions cmd/nerdctl/container/container_run_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -631,3 +631,29 @@ func TestRunAttachFlag(t *testing.T) {
})
}
}

func TestRunFromOCIArchive(t *testing.T) {
testutil.RequiresBuild(t)
testutil.RegisterBuildCacheCleanup(t)
testutil.DockerIncompatible(t)

base := testutil.NewBase(t)
imageName := testutil.Identifier(t)

teardown := func() {
base.Cmd("rmi", imageName).Run()
}
t.Cleanup(teardown)
teardown()

const sentinel = "test-nerdctl-run-from-oci-archive"
dockerfile := fmt.Sprintf(`FROM %s
CMD ["echo", "%s"]`, testutil.CommonImage, sentinel)

buildCtx := helpers.CreateBuildContext(t, dockerfile)
tag := fmt.Sprintf("%s:latest", imageName)
tarPath := fmt.Sprintf("%s/%s.tar", buildCtx, imageName)

base.Cmd("build", "--tag", tag, fmt.Sprintf("--output=type=oci,dest=%s", tarPath), buildCtx).AssertOK()
base.Cmd("run", "--rm", fmt.Sprintf("oci-archive://%s", tarPath)).AssertOutContainsAll(fmt.Sprintf("Loaded image: %s", tag), sentinel)
}
28 changes: 28 additions & 0 deletions pkg/imgutil/load/load.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import (
"fmt"
"io"
"os"
"strings"

containerd "github.com/containerd/containerd/v2/client"
"github.com/containerd/containerd/v2/core/images"
Expand Down Expand Up @@ -76,6 +77,33 @@ func FromArchive(ctx context.Context, client *containerd.Client, options types.I
return unpackedImages, nil
}

// FromOCIArchive will load and unpack the image from the on-disk OCI archive.
func FromOCIArchive(ctx context.Context, client *containerd.Client, pathToOCIArchive string, options types.ImageLoadOptions) ([]images.Image, error) {
const ociArchivePrefix = "oci-archive://"
if !strings.Contains(pathToOCIArchive, ociArchivePrefix) {
return []images.Image{}, errors.New("improperly formatted OCI archive image reference")
}

pathToOCIArchive = strings.TrimPrefix(pathToOCIArchive, ociArchivePrefix)
const separator = ":"
if strings.Contains(pathToOCIArchive, separator) {
subs := strings.Split(pathToOCIArchive, separator)
if len(subs) != 2 {
return []images.Image{}, errors.New("error too many seperators found")
}
pathToOCIArchive = subs[0]
}

_, err := os.Stat(pathToOCIArchive)
if os.IsNotExist(err) {
return []images.Image{}, errors.New("error directory does not exist")
}

options.Input = pathToOCIArchive

return FromArchive(ctx, client, options)
}

type readCounter struct {
io.Reader
N int
Expand Down

0 comments on commit b3e38d1

Please sign in to comment.