Skip to content

Commit

Permalink
Merge pull request #341 from edytuk/sylabs1785
Browse files Browse the repository at this point in the history
oci: Support --no-mount, to extent possible, from sylabs 1785
  • Loading branch information
edytuk authored Jul 11, 2023
2 parents dbf6a64 + 745c6ac commit 0e64d0c
Show file tree
Hide file tree
Showing 5 changed files with 145 additions and 3 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,9 @@ For older changes see the [archived Singularity change log](https://github.com/a
`<workdir>/scratch` on the host, rather than to tmpfs storage.
- OCI-mode now supports the `--no-home` flag, to prevent the container home
directory from being mounted.
- OCI-mode now supports the `--no-mount` flag to disable the `proc`, `sys`,
`devpts`, `tmp`, and `home` mounts in the container. `dev` cannot be disabled
in OCI-mode, and `bind-path` mounts are not supported.

### New Features & Functionality

Expand Down
1 change: 1 addition & 0 deletions e2e/actions/actions.go
Original file line number Diff line number Diff line change
Expand Up @@ -2947,5 +2947,6 @@ func E2ETests(env e2e.TestEnv) testhelper.Tests {
"ociCompat": np(c.actionOciCompat), // --oci equivalence to native mode --compat
"ociOverlay": (c.actionOciOverlay), // --overlay in OCI mode
"ociOverlayTeardown": np(c.actionOciOverlayTeardown), // proper overlay unmounting in OCI mode
"ociNo-mount": c.actionOciNoMount, // --no-mount in OCI mode
}
}
111 changes: 111 additions & 0 deletions e2e/actions/oci.go
Original file line number Diff line number Diff line change
Expand Up @@ -1462,3 +1462,114 @@ func (c actionTests) ociSTDPipe(t *testing.T) {
)
}
}

func (c actionTests) actionOciNoMount(t *testing.T) {
e2e.EnsureOCIArchive(t, c.env)
imageRef := "oci-archive:" + c.env.OCIArchivePath

tests := []struct {
name string
noMount string
noMatch string
warnMatch string
exit int
}{
{
name: "proc",
noMount: "proc",
noMatch: "on /proc",
exit: 1, // mount fails with exit code 1 when there is no `/proc`
},
{
name: "sys",
noMount: "sys",
noMatch: "on /sys",
exit: 0,
},
{
name: "dev",
noMount: "dev",
warnMatch: "--no-mount dev is not supported in OCI mode, ignoring.",
exit: 0,
},
{
name: "devpts",
noMount: "devpts",
noMatch: "on /dev/pts",
exit: 0,
},
{
name: "tmp",
noMount: "tmp",
noMatch: "on /tmp",
exit: 0,
},
{
name: "home",
noMount: "home",
noMatch: "on /home",
exit: 0,
},
{
name: "cwd",
noMount: "cwd",
warnMatch: "--no-mount cwd is not supported in OCI mode, ignoring.",
exit: 0,
},
//
// TODO - apptainer.conf bind path handling is not implemented yet in OCI mode.
// If/when it is, consider how to handle below.
//
// /etc/hosts & /etc/localtime are default 'bind path' entries we should
// be able to disable by abs path. Although other 'bind path' entries
// are ignored under '--contain' these two are handled specially in
// addBindsMount(), so make sure that `--no-mount` applies properly
// under contain also.
{
name: "/etc/hosts",
noMount: "/etc/hosts",
// noMatch: "on /etc/hosts",
warnMatch: "--no-mount /etc/hosts is not supported in OCI mode, ignoring.",
exit: 0,
},
{
name: "/etc/localtime",
noMount: "/etc/localtime",
// noMatch: "on /etc/localtime",
warnMatch: "--no-mount /etc/localtime is not supported in OCI mode, ignoring.",
exit: 0,
},
// bind-paths should disable all of the bind path mounts - including both defaults
{
name: "binds-paths-hosts",
noMount: "bind-paths",
// noMatch: "on /etc/hosts",
warnMatch: "--no-mount bind-paths is not supported in OCI mode, ignoring.",
exit: 0,
},
{
name: "bind-paths-localtime",
noMount: "bind-paths",
// noMatch: "on /etc/localtime",
warnMatch: "--no-mount bind-paths is not supported in OCI mode, ignoring.",
exit: 0,
},
}

for _, tt := range tests {

expectOp := e2e.ExpectOutput(e2e.UnwantedContainMatch, tt.noMatch)
if tt.warnMatch != "" {
expectOp = e2e.ExpectError(e2e.ContainMatch, tt.warnMatch)
}

c.env.RunApptainer(
t,
e2e.AsSubtest(tt.name),
e2e.WithProfile(e2e.OCIUserProfile),
e2e.WithCommand("exec"),
e2e.WithArgs("--no-mount", tt.noMount, imageRef, "mount"),
e2e.ExpectExit(tt.exit, expectOp),
)
}
}
9 changes: 7 additions & 2 deletions internal/pkg/runtime/launcher/oci/launcher_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ import (
"github.com/apptainer/apptainer/pkg/ocibundle/tools"
"github.com/apptainer/apptainer/pkg/sylog"
"github.com/apptainer/apptainer/pkg/util/apptainerconf"
"github.com/apptainer/apptainer/pkg/util/slice"
"github.com/container-orchestrated-devices/container-device-interface/pkg/cdi"
"github.com/google/uuid"
lccgroups "github.com/opencontainers/runc/libcontainer/cgroups"
Expand All @@ -44,6 +45,8 @@ import (
var (
ErrUnsupportedOption = errors.New("not supported by OCI launcher")
ErrNotImplemented = errors.New("not implemented by OCI launcher")

unsupportedNoMount = []string{"dev", "cwd", "bind-paths"}
)

// Launcher will holds configuration for, and will launch a container using an
Expand Down Expand Up @@ -107,8 +110,10 @@ func checkOpts(lo launcher.Options) error {
badOpt = append(badOpt, "FuseMount")
}

if len(lo.NoMount) > 0 {
badOpt = append(badOpt, "NoMount")
for _, nm := range lo.NoMount {
if strings.HasPrefix(nm, "/") || slice.ContainsString(unsupportedNoMount, nm) {
sylog.Warningf("--no-mount %s is not supported in OCI mode, ignoring.", nm)
}
}

if lo.NvCCLI {
Expand Down
24 changes: 23 additions & 1 deletion internal/pkg/runtime/launcher/oci/mounts_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import (
"github.com/apptainer/apptainer/internal/pkg/util/user"
"github.com/apptainer/apptainer/pkg/sylog"
"github.com/apptainer/apptainer/pkg/util/bind"
"github.com/apptainer/apptainer/pkg/util/slice"
"github.com/opencontainers/runtime-spec/specs-go"
)

Expand Down Expand Up @@ -77,6 +78,10 @@ func (l *Launcher) addTmpMounts(mounts *[]specs.Mount) error {
sylog.Debugf("Skipping mount of /tmp due to apptainer.conf")
return nil
}
if slice.ContainsString(l.cfg.NoMount, "tmp") {
sylog.Debugf("Skipping mount of /tmp due to --no-mount")
return nil
}

if len(l.cfg.WorkDir) > 0 {
sylog.Debugf("WorkDir specification provided: %s", l.cfg.WorkDir)
Expand Down Expand Up @@ -193,7 +198,6 @@ func (l *Launcher) addDevMounts(mounts *[]specs.Mount) error {
fmt.Sprintf("size=%dm", l.apptainerConf.SessiondirMaxSize),
},
},
ptsMount,
specs.Mount{
Destination: "/dev/shm",
Type: "tmpfs",
Expand All @@ -214,6 +218,12 @@ func (l *Launcher) addDevMounts(mounts *[]specs.Mount) error {
},
)

if slice.ContainsString(l.cfg.NoMount, "devpts") {
sylog.Debugf("Skipping mount of /dev/pts due to --no-mount")
return nil
}

*mounts = append(*mounts, ptsMount)
return nil
}

Expand All @@ -223,6 +233,10 @@ func (l *Launcher) addProcMount(mounts *[]specs.Mount) {
sylog.Debugf("Skipping mount of /proc due to apptainer.conf")
return
}
if slice.ContainsString(l.cfg.NoMount, "proc") {
sylog.Debugf("Skipping mount of /proc due to --no-mount")
return
}

*mounts = append(*mounts,
specs.Mount{
Expand All @@ -238,6 +252,10 @@ func (l *Launcher) addSysMount(mounts *[]specs.Mount) error {
sylog.Debugf("Skipping mount of /sys due to apptainer.conf")
return nil
}
if slice.ContainsString(l.cfg.NoMount, "sys") {
sylog.Debugf("Skipping mount of /sys due to --no-mount")
return nil
}

rootlessUID, err := rootless.Getuid()
if err != nil {
Expand Down Expand Up @@ -276,6 +294,10 @@ func (l *Launcher) addHomeMount(mounts *[]specs.Mount) error {
sylog.Debugf("Skipping mount of $HOME due to --no-home")
return nil
}
if slice.ContainsString(l.cfg.NoMount, "home") {
sylog.Debugf("Skipping mount of /home due to --no-mount")
return nil
}

if l.homeDest == "" {
return fmt.Errorf("cannot add home mount with empty destination")
Expand Down

0 comments on commit 0e64d0c

Please sign in to comment.