Skip to content

Commit

Permalink
feat: run OCI image sources via temporary bundle
Browse files Browse the repository at this point in the history
When running containers in `--oci` mode, the argument to run is now an
image reference corresponding to a native OCI format handled by
containers/image, i.e.

* docker://
* docker-archive:
* docker-daemon:
* oci-archive:
* oci:

The source image is extracted into a temporary OCI bundle, with a
minimally valid configuration that:

* Runs the process specified by CMD & ENTRYPOINT only.
* Sets the environment specified by the image ENV only.

The approach is very naive - we pull through Singularity's OCI blob
cache into a temporary oci layout dir, before creating the bundle from
it. Auth handling for registries is not yet wired up. There is
duplication of various pieces of code from the build / SIF OCI flows
as these are not easily exposed to the area we are working in.

The intent of the PR, at this stage, is simply to allow e.g.

    singularity run --oci docker://sylabsio/lolcow

Closes sylabs/singularity#1036

Signed-off-by: Edita Kizinevic <edita.kizinevic@cern.ch>
  • Loading branch information
dtrudg authored and edytuk committed May 24, 2023
1 parent 18b4201 commit f676faa
Show file tree
Hide file tree
Showing 8 changed files with 354 additions and 8 deletions.
8 changes: 8 additions & 0 deletions cmd/internal/cli/actions.go
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,14 @@ func replaceURIWithImage(ctx context.Context, cmd *cobra.Command, args []string)
sylog.Fatalf("failed to create a new image cache handle")
}

// The OCI runtime launcher will handle OCI image sources directly.
if ociRuntime {
if oci.IsSupported(t) != t {
sylog.Fatalf("OCI runtime only supports OCI image sources. %s is not supported.", t)
}
return
}

switch t {
case uri.Library:
image, err = handleLibrary(ctx, imgCache, args[0])
Expand Down
2 changes: 1 addition & 1 deletion cmd/internal/cli/oci_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -322,7 +322,7 @@ var OciMountCmd = &cobra.Command{
DisableFlagsInUseLine: true,
PreRun: CheckRoot,
Run: func(cmd *cobra.Command, args []string) {
if err := apptainer.OciMount(args[0], args[1]); err != nil {
if err := apptainer.OciMount(cmd.Context(), args[0], args[1]); err != nil {
sylog.Fatalf("%s", err)
}
},
Expand Down
4 changes: 2 additions & 2 deletions internal/app/apptainer/oci_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,12 +83,12 @@ func OciUpdate(containerID string, args *OciArgs) error {
}

// OciMount mount a SIF image to create an OCI bundle
func OciMount(image string, bundle string) error {
func OciMount(ctx context.Context, image string, bundle string) error {
d, err := ocibundle.FromSif(image, bundle, true)
if err != nil {
return err
}
return d.Create(nil)
return d.Create(ctx, nil)
}

// OciUmount umount SIF and delete OCI bundle
Expand Down
55 changes: 54 additions & 1 deletion internal/pkg/runtime/launcher/oci/launcher_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,17 @@ import (
"context"
"errors"
"fmt"
"os"
"strings"

"github.com/apptainer/apptainer/internal/pkg/buildcfg"
"github.com/apptainer/apptainer/internal/pkg/cache"
"github.com/apptainer/apptainer/internal/pkg/runtime/launcher"
"github.com/apptainer/apptainer/pkg/ocibundle/native"
"github.com/apptainer/apptainer/pkg/syfs"
"github.com/apptainer/apptainer/pkg/sylog"
useragent "github.com/apptainer/apptainer/pkg/util/user-agent"
"github.com/containers/image/v5/types"
"github.com/google/uuid"
)

Expand Down Expand Up @@ -235,6 +242,7 @@ func checkOpts(lo launcher.Options) error {
}

// Exec will interactively execute a container via the runc low-level runtime.
// image is a reference to an OCI image, e.g. docker://ubuntu or oci:/tmp/mycontainer
func (l *Launcher) Exec(ctx context.Context, image string, cmd string, args []string, instanceName string) error {
if instanceName != "" {
return fmt.Errorf("%w: instanceName", ErrNotImplemented)
Expand All @@ -248,9 +256,54 @@ func (l *Launcher) Exec(ctx context.Context, image string, cmd string, args []st
return fmt.Errorf("%w: args %v", ErrNotImplemented, args)
}

bundleDir, err := os.MkdirTemp("", "oci-bundle")
if err != nil {
return nil
}
defer func() {
sylog.Debugf("Removing OCI bundle at: %s", bundleDir)
if err := os.RemoveAll(bundleDir); err != nil {
sylog.Errorf("Couldn't remove OCI bundle %s: %v", bundleDir, err)
}
}()

sylog.Debugf("Creating OCI bundle at: %s", bundleDir)

// TODO - propagate auth config
sysCtx := &types.SystemContext{
// OCIInsecureSkipTLSVerify: cp.b.Opts.NoHTTPS,
// DockerAuthConfig: cp.b.Opts.DockerAuthConfig,
// DockerDaemonHost: cp.b.Opts.DockerDaemonHost,
OSChoice: "linux",
AuthFilePath: syfs.DockerConf(),
DockerRegistryUserAgent: useragent.Value(),
}
// if cp.b.Opts.NoHTTPS {
// cp.sysCtx.DockerInsecureSkipTLSVerify = types.NewOptionalBool(true)
// }

var imgCache *cache.Handle
if !l.cfg.CacheDisabled {
imgCache, err = cache.New(cache.Config{
ParentDir: os.Getenv(cache.DirEnv),
})
if err != nil {
return err
}
}

b, err := native.FromImageRef(image, bundleDir, sysCtx, imgCache)
if err != nil {
return err
}

if err := b.Create(ctx, nil); err != nil {
return err
}

id, err := uuid.NewRandom()
if err != nil {
return fmt.Errorf("while generating container id: %w", err)
}
return Run(ctx, id.String(), image, "")
return Run(ctx, id.String(), b.Path(), "")
}
5 changes: 4 additions & 1 deletion pkg/ocibundle/bundle.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,14 @@
package ocibundle

import (
"context"

"github.com/opencontainers/runtime-spec/specs-go"
)

// Bundle defines an OCI bundle interface to create/delete OCI bundles
type Bundle interface {
Create(*specs.Spec) error
Create(context.Context, *specs.Spec) error
Delete() error
Path() string
}
Loading

0 comments on commit f676faa

Please sign in to comment.