diff --git a/containerd/containerd.go b/containerd/containerd.go index a588cf7..8271dad 100644 --- a/containerd/containerd.go +++ b/containerd/containerd.go @@ -20,12 +20,14 @@ package containerd import ( "context" "fmt" + "strconv" "strings" "time" etchosts "github.com/Roblox/nomad-driver-containerd/etchosts" "github.com/containerd/containerd" "github.com/containerd/containerd/cio" + "github.com/containerd/containerd/containers" "github.com/containerd/containerd/contrib/seccomp" "github.com/containerd/containerd/oci" refdocker "github.com/containerd/containerd/reference/docker" @@ -85,6 +87,21 @@ func (d *Driver) parshAuth(auth *RegistryAuth) CredentialsOpt { } } +// the containerd driver inexplicably appears to be missing options +// to set RLIMITS NOFILE so have to roll our own + +func withRLimitNoFile(hard, soft uint64) oci.SpecOpts { + return func(_ context.Context, _ oci.Client, _ *containers.Container, s *specs.Spec) error { + for i, _ := range s.Process.Rlimits { + if s.Process.Rlimits[i].Type == "RLIMIT_NOFILE" { + s.Process.Rlimits[i].Hard = hard + s.Process.Rlimits[i].Soft = soft + } + } + return nil + } +} + func withResolver(creds CredentialsOpt) containerd.RemoteOpt { resolver := remotesdocker.NewResolver(remotesdocker.ResolverOptions{ Hosts: remotesdocker.ConfigureDefaultRegistries(remotesdocker.WithAuthorizer( @@ -229,6 +246,29 @@ func (d *Driver) createContainer(containerConfig *ContainerConfig, config *TaskC opts = append(opts, oci.WithCPUsMems(config.CPUSetMEMs)) } + // allow setting of RLIMIT_NOFILE + if config.RLimitNoFile != "" { + s := strings.Split(config.RLimitNoFile, ":") + var hard, soft uint64 + + if tmp, err := strconv.ParseUint(s[0], 10, 64); err != nil { + return nil, fmt.Errorf("rlimit_nofile, failed to convert string to uint64: %s (%s)", s[0], err.Error()) + } else { + hard = tmp + } + + if len(s) > 1 { + if tmp, err := strconv.ParseUint(s[1], 10, 64); err != nil { + return nil, fmt.Errorf("rlimit_nofile, failed to convert string to uint64: %s (%s)", s[1], err.Error()) + } else { + soft = tmp + } + } else { + soft = hard + } + opts = append(opts, withRLimitNoFile(hard, soft)) + } + // Set current working directory (cwd). if config.Cwd != "" { opts = append(opts, oci.WithProcessCwd(config.Cwd)) diff --git a/containerd/driver.go b/containerd/driver.go index 3d01e0a..2ed748a 100644 --- a/containerd/driver.go +++ b/containerd/driver.go @@ -96,19 +96,20 @@ var ( // this is used to validate the configuration specified for the plugin // when a job is submitted. taskConfigSpec = hclspec.NewObject(map[string]*hclspec.Spec{ - "image": hclspec.NewAttr("image", "string", true), - "command": hclspec.NewAttr("command", "string", false), - "args": hclspec.NewAttr("args", "list(string)", false), - "cap_add": hclspec.NewAttr("cap_add", "list(string)", false), - "cap_drop": hclspec.NewAttr("cap_drop", "list(string)", false), - "cpuset_cpus": hclspec.NewAttr("cpuset_cpus", "string", false), - "cpuset_mems": hclspec.NewAttr("cpuset_mems", "string", false), - "cwd": hclspec.NewAttr("cwd", "string", false), - "devices": hclspec.NewAttr("devices", "list(string)", false), - "privileged": hclspec.NewAttr("privileged", "bool", false), - "pids_limit": hclspec.NewAttr("pids_limit", "number", false), - "pid_mode": hclspec.NewAttr("pid_mode", "string", false), - "hostname": hclspec.NewAttr("hostname", "string", false), + "image": hclspec.NewAttr("image", "string", true), + "command": hclspec.NewAttr("command", "string", false), + "args": hclspec.NewAttr("args", "list(string)", false), + "cap_add": hclspec.NewAttr("cap_add", "list(string)", false), + "cap_drop": hclspec.NewAttr("cap_drop", "list(string)", false), + "cpuset_cpus": hclspec.NewAttr("cpuset_cpus", "string", false), + "cpuset_mems": hclspec.NewAttr("cpuset_mems", "string", false), + "rlimit_nofile": hclspec.NewAttr("rlimit_nofile", "string", false), + "cwd": hclspec.NewAttr("cwd", "string", false), + "devices": hclspec.NewAttr("devices", "list(string)", false), + "privileged": hclspec.NewAttr("privileged", "bool", false), + "pids_limit": hclspec.NewAttr("pids_limit", "number", false), + "pid_mode": hclspec.NewAttr("pid_mode", "string", false), + "hostname": hclspec.NewAttr("hostname", "string", false), "host_dns": hclspec.NewDefault( hclspec.NewAttr("host_dns", "bool", false), hclspec.NewLiteral("true"), @@ -186,6 +187,7 @@ type TaskConfig struct { CapDrop []string `codec:"cap_drop"` CPUSetCPUs string `codec:"cpuset_cpus"` CPUSetMEMs string `codec:"cpuset_mems"` + RLimitNoFile string `codec:"rlimit_nofile"` Cwd string `codec:"cwd"` Devices []string `codec:"devices"` Seccomp bool `codec:"seccomp"`