X-Git-Url: https://git.arvados.org/arvados.git/blobdiff_plain/43fdb06a1620d926bdaec00582c82a4190805d86..469ea187586ea8017e26874c2d80414ce7571fae:/lib/crunchrun/singularity.go diff --git a/lib/crunchrun/singularity.go b/lib/crunchrun/singularity.go index 921f58ff0a..1da401f859 100644 --- a/lib/crunchrun/singularity.go +++ b/lib/crunchrun/singularity.go @@ -12,9 +12,11 @@ import ( "net" "os" "os/exec" + "os/user" "regexp" "sort" "strconv" + "strings" "syscall" "time" @@ -24,6 +26,7 @@ import ( type singularityExecutor struct { logf func(string, ...interface{}) + fakeroot bool // use --fakeroot flag, allow --network=bridge when non-root (currently only used by tests) spec containerSpec tmpdir string child *exec.Cmd @@ -41,7 +44,13 @@ func newSingularityExecutor(logf func(string, ...interface{})) (*singularityExec }, nil } -func (e *singularityExecutor) Runtime() string { return "singularity" } +func (e *singularityExecutor) Runtime() string { + buf, err := exec.Command("singularity", "--version").CombinedOutput() + if err != nil { + return "singularity (unknown version)" + } + return strings.TrimSuffix(string(buf), "\n") +} func (e *singularityExecutor) getOrCreateProject(ownerUuid string, name string, containerClient *arvados.Client) (*arvados.Group, error) { var gp arvados.GroupList @@ -247,9 +256,21 @@ func (e *singularityExecutor) Create(spec containerSpec) error { } func (e *singularityExecutor) execCmd(path string) *exec.Cmd { - args := []string{path, "exec", "--containall", "--cleanenv", "--pwd", e.spec.WorkingDir, "--net"} + args := []string{path, "exec", "--containall", "--cleanenv", "--pwd=" + e.spec.WorkingDir} + if e.fakeroot { + args = append(args, "--fakeroot") + } if !e.spec.EnableNetwork { - args = append(args, "--network=none") + args = append(args, "--net", "--network=none") + } else if u, err := user.Current(); err == nil && u.Uid == "0" || e.fakeroot { + // Specifying --network=bridge fails unless (a) we are + // root, (b) we are using --fakeroot, or (c) + // singularity has been configured to allow our + // uid/gid to use it like so: + // + // singularity config global --set 'allow net networks' bridge + // singularity config global --set 'allow net groups' mygroup + args = append(args, "--net", "--network=bridge") } if e.spec.CUDADeviceCount != 0 { args = append(args, "--nv") @@ -267,7 +288,7 @@ func (e *singularityExecutor) execCmd(path string) *exec.Cmd { for _, path := range binds { mount := e.spec.BindMounts[path] if path == e.spec.Env["HOME"] { - // Singularity treates $HOME as special case + // Singularity treats $HOME as special case args = append(args, "--home", mount.HostPath+":"+path) } else { args = append(args, "--bind", mount.HostPath+":"+path+":"+readonlyflag[mount.ReadOnly]) @@ -281,8 +302,8 @@ func (e *singularityExecutor) execCmd(path string) *exec.Cmd { env := make([]string, 0, len(e.spec.Env)) for k, v := range e.spec.Env { if k == "HOME" { - // Singularity treates $HOME as special case, this is handled - // with --home above + // Singularity treats $HOME as special case, + // this is handled with --home above continue } env = append(env, "SINGULARITYENV_"+k+"="+v) @@ -296,6 +317,14 @@ func (e *singularityExecutor) execCmd(path string) *exec.Cmd { // us to select specific devices we need to propagate that. env = append(env, "SINGULARITYENV_CUDA_VISIBLE_DEVICES="+cudaVisibleDevices) } + // Singularity's default behavior is to evaluate each + // SINGULARITYENV_* env var with a shell as a double-quoted + // string and pass the result to the contained + // process. Singularity 3.10+ has an option to pass env vars + // through literally without evaluating, which is what we + // want. See https://github.com/sylabs/singularity/pull/704 + // and https://dev.arvados.org/issues/19081 + env = append(env, "SINGULARITY_NO_EVAL=1") args = append(args, e.imageFilename) args = append(args, e.spec.Command...)