mirror of
				https://github.com/moby/moby.git
				synced 2022-11-09 12:21:53 -05:00 
			
		
		
		
	Allow setting ulimits for containers
Signed-off-by: Brian Goff <cpuguy83@gmail.com>
This commit is contained in:
		
							parent
							
								
									321f4f0683
								
							
						
					
					
						commit
						3f39050637
					
				
					 16 changed files with 365 additions and 10 deletions
				
			
		| 
						 | 
				
			
			@ -6,6 +6,7 @@ import (
 | 
			
		|||
	"github.com/docker/docker/daemon/networkdriver"
 | 
			
		||||
	"github.com/docker/docker/opts"
 | 
			
		||||
	flag "github.com/docker/docker/pkg/mflag"
 | 
			
		||||
	"github.com/docker/docker/pkg/ulimit"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
| 
						 | 
				
			
			@ -44,6 +45,7 @@ type Config struct {
 | 
			
		|||
	Context                     map[string][]string
 | 
			
		||||
	TrustKeyPath                string
 | 
			
		||||
	Labels                      []string
 | 
			
		||||
	Ulimits                     map[string]*ulimit.Ulimit
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// InstallFlags adds command-line options to the top-level flag parser for
 | 
			
		||||
| 
						 | 
				
			
			@ -75,6 +77,8 @@ func (config *Config) InstallFlags() {
 | 
			
		|||
	opts.IPListVar(&config.Dns, []string{"#dns", "-dns"}, "DNS server to use")
 | 
			
		||||
	opts.DnsSearchListVar(&config.DnsSearch, []string{"-dns-search"}, "DNS search domains to use")
 | 
			
		||||
	opts.LabelListVar(&config.Labels, []string{"-label"}, "Set key=value labels to the daemon")
 | 
			
		||||
	config.Ulimits = make(map[string]*ulimit.Ulimit)
 | 
			
		||||
	opts.UlimitMapVar(config.Ulimits, []string{"-default-ulimit"}, "Set default ulimits for containers")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func getDefaultNetworkMtu() int {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -31,6 +31,7 @@ import (
 | 
			
		|||
	"github.com/docker/docker/pkg/networkfs/resolvconf"
 | 
			
		||||
	"github.com/docker/docker/pkg/promise"
 | 
			
		||||
	"github.com/docker/docker/pkg/symlink"
 | 
			
		||||
	"github.com/docker/docker/pkg/ulimit"
 | 
			
		||||
	"github.com/docker/docker/runconfig"
 | 
			
		||||
	"github.com/docker/docker/utils"
 | 
			
		||||
)
 | 
			
		||||
| 
						 | 
				
			
			@ -276,11 +277,34 @@ func populateCommand(c *Container, env []string) error {
 | 
			
		|||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var rlimits []*ulimit.Rlimit
 | 
			
		||||
	ulimits := c.hostConfig.Ulimits
 | 
			
		||||
 | 
			
		||||
	// Merge ulimits with daemon defaults
 | 
			
		||||
	ulIdx := make(map[string]*ulimit.Ulimit)
 | 
			
		||||
	for _, ul := range ulimits {
 | 
			
		||||
		ulIdx[ul.Name] = ul
 | 
			
		||||
	}
 | 
			
		||||
	for name, ul := range c.daemon.config.Ulimits {
 | 
			
		||||
		if _, exists := ulIdx[name]; !exists {
 | 
			
		||||
			ulimits = append(ulimits, ul)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, limit := range ulimits {
 | 
			
		||||
		rl, err := limit.GetRlimit()
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
		rlimits = append(rlimits, rl)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	resources := &execdriver.Resources{
 | 
			
		||||
		Memory:     c.Config.Memory,
 | 
			
		||||
		MemorySwap: c.Config.MemorySwap,
 | 
			
		||||
		CpuShares:  c.Config.CpuShares,
 | 
			
		||||
		Cpuset:     c.Config.Cpuset,
 | 
			
		||||
		Rlimits:    rlimits,
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	processConfig := execdriver.ProcessConfig{
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -2,14 +2,16 @@ package execdriver
 | 
			
		|||
 | 
			
		||||
import (
 | 
			
		||||
	"errors"
 | 
			
		||||
	"github.com/docker/docker/daemon/execdriver/native/template"
 | 
			
		||||
	"github.com/docker/libcontainer"
 | 
			
		||||
	"github.com/docker/libcontainer/devices"
 | 
			
		||||
	"io"
 | 
			
		||||
	"os"
 | 
			
		||||
	"os/exec"
 | 
			
		||||
	"strings"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"github.com/docker/docker/daemon/execdriver/native/template"
 | 
			
		||||
	"github.com/docker/docker/pkg/ulimit"
 | 
			
		||||
	"github.com/docker/libcontainer"
 | 
			
		||||
	"github.com/docker/libcontainer/devices"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Context is a generic key value pair that allows
 | 
			
		||||
| 
						 | 
				
			
			@ -99,10 +101,11 @@ type NetworkInterface struct {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
type Resources struct {
 | 
			
		||||
	Memory     int64  `json:"memory"`
 | 
			
		||||
	MemorySwap int64  `json:"memory_swap"`
 | 
			
		||||
	CpuShares  int64  `json:"cpu_shares"`
 | 
			
		||||
	Cpuset     string `json:"cpuset"`
 | 
			
		||||
	Memory     int64            `json:"memory"`
 | 
			
		||||
	MemorySwap int64            `json:"memory_swap"`
 | 
			
		||||
	CpuShares  int64            `json:"cpu_shares"`
 | 
			
		||||
	Cpuset     string           `json:"cpuset"`
 | 
			
		||||
	Rlimits    []*ulimit.Rlimit `json:"rlimits"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type ResourceStats struct {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -58,6 +58,8 @@ func (d *driver) createContainer(c *execdriver.Command) (*libcontainer.Config, e
 | 
			
		|||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	d.setupRlimits(container, c)
 | 
			
		||||
 | 
			
		||||
	cmds := make(map[string]*exec.Cmd)
 | 
			
		||||
	d.Lock()
 | 
			
		||||
	for k, v := range d.activeContainers {
 | 
			
		||||
| 
						 | 
				
			
			@ -172,6 +174,16 @@ func (d *driver) setCapabilities(container *libcontainer.Config, c *execdriver.C
 | 
			
		|||
	return err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (d *driver) setupRlimits(container *libcontainer.Config, c *execdriver.Command) {
 | 
			
		||||
	if c.Resources == nil {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, rlimit := range c.Resources.Rlimits {
 | 
			
		||||
		container.Rlimits = append(container.Rlimits, libcontainer.Rlimit((*rlimit)))
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (d *driver) setupMounts(container *libcontainer.Config, c *execdriver.Command) error {
 | 
			
		||||
	for _, m := range c.Mounts {
 | 
			
		||||
		container.MountConfig.Mounts = append(container.MountConfig.Mounts, &mount.Mount{
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -74,6 +74,7 @@ func (daemon *Daemon) setHostConfig(container *Container, hostConfig *runconfig.
 | 
			
		|||
	if err := daemon.RegisterLinks(container, hostConfig); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	container.hostConfig = hostConfig
 | 
			
		||||
	container.toDisk()
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -51,6 +51,12 @@ You can still call an old version of the API using
 | 
			
		|||
**New!**
 | 
			
		||||
This endpoint now returns `Os`, `Arch` and `KernelVersion`.
 | 
			
		||||
 | 
			
		||||
`POST /containers/create`
 | 
			
		||||
`POST /containers/(id)/start`
 | 
			
		||||
 | 
			
		||||
**New!**
 | 
			
		||||
You can set ulimit settings to be used within the container.
 | 
			
		||||
 | 
			
		||||
## v1.17
 | 
			
		||||
 | 
			
		||||
### Full Documentation
 | 
			
		||||
| 
						 | 
				
			
			@ -86,7 +92,6 @@ root filesystem as read only.
 | 
			
		|||
**New!**
 | 
			
		||||
This endpoint returns a live stream of a container's resource usage statistics.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
## v1.16
 | 
			
		||||
 | 
			
		||||
### Full Documentation
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -155,7 +155,8 @@ Create a container
 | 
			
		|||
               "CapDrop": ["MKNOD"],
 | 
			
		||||
               "RestartPolicy": { "Name": "", "MaximumRetryCount": 0 },
 | 
			
		||||
               "NetworkMode": "bridge",
 | 
			
		||||
               "Devices": []
 | 
			
		||||
               "Devices": [],
 | 
			
		||||
               "Ulimits": [{}]
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -244,6 +245,9 @@ Json Parameters:
 | 
			
		|||
  -   **Devices** - A list of devices to add to the container specified in the
 | 
			
		||||
        form
 | 
			
		||||
        `{ "PathOnHost": "/dev/deviceName", "PathInContainer": "/dev/deviceName", "CgroupPermissions": "mrw"}`
 | 
			
		||||
  -   **Ulimits** - A list of ulimits to be set in the container, specified as
 | 
			
		||||
        `{ "Name": <name>, "Soft": <soft limit>, "Hard": <hard limit> }`, for example:
 | 
			
		||||
        `Ulimits: { "Name": "nofile", "Soft": 1024, "Hard", 2048 }}`
 | 
			
		||||
 | 
			
		||||
Query Parameters:
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -337,7 +341,8 @@ Return low-level information on the container `id`
 | 
			
		|||
				"Name": "on-failure"
 | 
			
		||||
			},
 | 
			
		||||
			"SecurityOpt": null,
 | 
			
		||||
			"VolumesFrom": null
 | 
			
		||||
			"VolumesFrom": null,
 | 
			
		||||
			"Ulimits": [{}]
 | 
			
		||||
		},
 | 
			
		||||
		"HostnamePath": "/var/lib/docker/containers/ba033ac4401106a3b513bc9d639eee123ad78ca3616b921167cd74b20e25ed39/hostname",
 | 
			
		||||
		"HostsPath": "/var/lib/docker/containers/ba033ac4401106a3b513bc9d639eee123ad78ca3616b921167cd74b20e25ed39/hosts",
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -109,6 +109,7 @@ expect an integer, and they can only be specified once.
 | 
			
		|||
      --tlskey="~/.docker/key.pem"           Path to TLS key file
 | 
			
		||||
      --tlsverify=false                      Use TLS and verify the remote
 | 
			
		||||
      -v, --version=false                    Print version information and quit
 | 
			
		||||
      --default-ulimit=[]                    Set default ulimit settings for containers.
 | 
			
		||||
 | 
			
		||||
Options with [] may be specified multiple times.
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -404,6 +405,14 @@ This will only add the proxy and authentication to the Docker daemon's requests
 | 
			
		|||
your `docker build`s and running containers will need extra configuration to use
 | 
			
		||||
the proxy
 | 
			
		||||
 | 
			
		||||
### Default Ulimits
 | 
			
		||||
 | 
			
		||||
`--default-ulimit` allows you to set the default `ulimit` options to use for all
 | 
			
		||||
containers. It takes the same options as `--ulimit` for `docker run`. If these
 | 
			
		||||
defaults are not set, `ulimit` settings will be inheritted, if not set on
 | 
			
		||||
`docker run`, from the Docker daemon. Any `--ulimit` options passed to
 | 
			
		||||
`docker run` will overwrite these defaults.
 | 
			
		||||
 | 
			
		||||
### Miscellaneous options
 | 
			
		||||
 | 
			
		||||
IP masquerading uses address translation to allow containers without a public IP to talk
 | 
			
		||||
| 
						 | 
				
			
			@ -1974,6 +1983,23 @@ You can add other hosts into a container's `/etc/hosts` file by using one or mor
 | 
			
		|||
>      $ alias hostip="ip route show 0.0.0.0/0 | grep -Eo 'via \S+' | awk '{ print \$2 }'"
 | 
			
		||||
>      $ docker run  --add-host=docker:$(hostip) --rm -it debian
 | 
			
		||||
 | 
			
		||||
### Setting ulimits in a container
 | 
			
		||||
 | 
			
		||||
Since setting `ulimit` settings in a container requires extra privileges not
 | 
			
		||||
available in the default container, you can set these using the `--ulimit` flag.
 | 
			
		||||
`--ulimit` is specified with a soft and hard limit as such:
 | 
			
		||||
`<type>=<soft limit>[:<hard limit>]`, for example:
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
    $ docker run --ulimit nofile=1024:1024 --rm debian ulimit -n
 | 
			
		||||
    1024
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
>**Note:**
 | 
			
		||||
> If you do not provide a `hard limit`, the `soft limit` will be used for both
 | 
			
		||||
values. If no `ulimits` are set, they will be inherited from the default `ulimits`
 | 
			
		||||
set on the daemon.
 | 
			
		||||
 | 
			
		||||
## save
 | 
			
		||||
 | 
			
		||||
    Usage: docker save [OPTIONS] IMAGE [IMAGE...]
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -480,3 +480,56 @@ func TestDaemonUpgradeWithVolumes(t *testing.T) {
 | 
			
		|||
 | 
			
		||||
	logDone("daemon - volumes from old(pre 1.3) daemon work")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestDaemonUlimitDefaults(t *testing.T) {
 | 
			
		||||
	d := NewDaemon(t)
 | 
			
		||||
 | 
			
		||||
	if err := d.StartWithBusybox("--default-ulimit", "nofile=42:42", "--default-ulimit", "nproc=1024:1024"); err != nil {
 | 
			
		||||
		t.Fatal(err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	out, err := d.Cmd("run", "--ulimit", "nproc=2048", "--name=test", "busybox", "/bin/sh", "-c", "echo $(ulimit -n); echo $(ulimit -p)")
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Fatal(out, err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	outArr := strings.Split(out, "\n")
 | 
			
		||||
	if len(outArr) < 2 {
 | 
			
		||||
		t.Fatal("got unexpected output: %s", out)
 | 
			
		||||
	}
 | 
			
		||||
	nofile := strings.TrimSpace(outArr[0])
 | 
			
		||||
	nproc := strings.TrimSpace(outArr[1])
 | 
			
		||||
 | 
			
		||||
	if nofile != "42" {
 | 
			
		||||
		t.Fatalf("expected `ulimit -n` to be `42`, got: %s", nofile)
 | 
			
		||||
	}
 | 
			
		||||
	if nproc != "2048" {
 | 
			
		||||
		t.Fatalf("exepcted `ulimit -p` to be 2048, got: %s", nproc)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Now restart daemon with a new default
 | 
			
		||||
	if err := d.Restart("--default-ulimit", "nofile=43"); err != nil {
 | 
			
		||||
		t.Fatal(err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	out, err = d.Cmd("start", "-a", "test")
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Fatal(err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	outArr = strings.Split(out, "\n")
 | 
			
		||||
	if len(outArr) < 2 {
 | 
			
		||||
		t.Fatal("got unexpected output: %s", out)
 | 
			
		||||
	}
 | 
			
		||||
	nofile = strings.TrimSpace(outArr[0])
 | 
			
		||||
	nproc = strings.TrimSpace(outArr[1])
 | 
			
		||||
 | 
			
		||||
	if nofile != "43" {
 | 
			
		||||
		t.Fatalf("expected `ulimit -n` to be `43`, got: %s", nofile)
 | 
			
		||||
	}
 | 
			
		||||
	if nproc != "2048" {
 | 
			
		||||
		t.Fatalf("exepcted `ulimit -p` to be 2048, got: %s", nproc)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	logDone("daemon - default ulimits are applied")
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -91,3 +91,18 @@ func TestRunWithVolumesIsRecursive(t *testing.T) {
 | 
			
		|||
 | 
			
		||||
	logDone("run - volumes are bind mounted recursively")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestRunWithUlimits(t *testing.T) {
 | 
			
		||||
	defer deleteAllContainers()
 | 
			
		||||
	out, _, err := runCommandWithOutput(exec.Command(dockerBinary, "run", "--name=testulimits", "--ulimit", "nofile=42", "busybox", "/bin/sh", "-c", "ulimit -n"))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Fatal(err, out)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ul := strings.TrimSpace(out)
 | 
			
		||||
	if ul != "42" {
 | 
			
		||||
		t.Fatalf("expected `ulimit -n` to be 42, got %s", ul)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	logDone("run - ulimits are set")
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -11,6 +11,7 @@ import (
 | 
			
		|||
	"github.com/docker/docker/api"
 | 
			
		||||
	flag "github.com/docker/docker/pkg/mflag"
 | 
			
		||||
	"github.com/docker/docker/pkg/parsers"
 | 
			
		||||
	"github.com/docker/docker/pkg/ulimit"
 | 
			
		||||
	"github.com/docker/docker/utils"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -43,6 +44,10 @@ func LabelListVar(values *[]string, names []string, usage string) {
 | 
			
		|||
	flag.Var(newListOptsRef(values, ValidateLabel), names, usage)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func UlimitMapVar(values map[string]*ulimit.Ulimit, names []string, usage string) {
 | 
			
		||||
	flag.Var(NewUlimitOpt(values), names, usage)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ListOpts type
 | 
			
		||||
type ListOpts struct {
 | 
			
		||||
	values    *[]string
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										44
									
								
								opts/ulimit.go
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										44
									
								
								opts/ulimit.go
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,44 @@
 | 
			
		|||
package opts
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
 | 
			
		||||
	"github.com/docker/docker/pkg/ulimit"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type UlimitOpt struct {
 | 
			
		||||
	values map[string]*ulimit.Ulimit
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func NewUlimitOpt(ref map[string]*ulimit.Ulimit) *UlimitOpt {
 | 
			
		||||
	return &UlimitOpt{ref}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (o *UlimitOpt) Set(val string) error {
 | 
			
		||||
	l, err := ulimit.Parse(val)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	o.values[l.Name] = l
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (o *UlimitOpt) String() string {
 | 
			
		||||
	var out []string
 | 
			
		||||
	for _, v := range o.values {
 | 
			
		||||
		out = append(out, v.String())
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return fmt.Sprintf("%v", out)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (o *UlimitOpt) GetList() []*ulimit.Ulimit {
 | 
			
		||||
	var ulimits []*ulimit.Ulimit
 | 
			
		||||
	for _, v := range o.values {
 | 
			
		||||
		ulimits = append(ulimits, v)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return ulimits
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										106
									
								
								pkg/ulimit/ulimit.go
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										106
									
								
								pkg/ulimit/ulimit.go
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,106 @@
 | 
			
		|||
package ulimit
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"strconv"
 | 
			
		||||
	"strings"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Human friendly version of Rlimit
 | 
			
		||||
type Ulimit struct {
 | 
			
		||||
	Name string
 | 
			
		||||
	Hard int64
 | 
			
		||||
	Soft int64
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type Rlimit struct {
 | 
			
		||||
	Type int    `json:"type,omitempty"`
 | 
			
		||||
	Hard uint64 `json:"hard,omitempty"`
 | 
			
		||||
	Soft uint64 `json:"soft,omitempty"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	// magic numbers for making the syscall
 | 
			
		||||
	// some of these are defined in the syscall package, but not all.
 | 
			
		||||
	// Also since Windows client doesn't get access to the syscall package, need to
 | 
			
		||||
	//	define these here
 | 
			
		||||
	RLIMIT_AS         = 9
 | 
			
		||||
	RLIMIT_CORE       = 4
 | 
			
		||||
	RLIMIT_CPU        = 0
 | 
			
		||||
	RLIMIT_DATA       = 2
 | 
			
		||||
	RLIMIT_FSIZE      = 1
 | 
			
		||||
	RLIMIT_LOCKS      = 10
 | 
			
		||||
	RLIMIT_MEMLOCK    = 8
 | 
			
		||||
	RLIMIT_MSGQUEUE   = 12
 | 
			
		||||
	RLIMIT_NICE       = 13
 | 
			
		||||
	RLIMIT_NOFILE     = 7
 | 
			
		||||
	RLIMIT_NPROC      = 6
 | 
			
		||||
	RLIMIT_RSS        = 5
 | 
			
		||||
	RLIMIT_RTPRIO     = 14
 | 
			
		||||
	RLIMIT_RTTIME     = 15
 | 
			
		||||
	RLIMIT_SIGPENDING = 11
 | 
			
		||||
	RLIMIT_STACK      = 3
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var ulimitNameMapping = map[string]int{
 | 
			
		||||
	//"as":         RLIMIT_AS, // Disbaled since this doesn't seem usable with the way Docker inits a container.
 | 
			
		||||
	"core":       RLIMIT_CORE,
 | 
			
		||||
	"cpu":        RLIMIT_CPU,
 | 
			
		||||
	"data":       RLIMIT_DATA,
 | 
			
		||||
	"fsize":      RLIMIT_FSIZE,
 | 
			
		||||
	"locks":      RLIMIT_LOCKS,
 | 
			
		||||
	"memlock":    RLIMIT_MEMLOCK,
 | 
			
		||||
	"msgqueue":   RLIMIT_MSGQUEUE,
 | 
			
		||||
	"nice":       RLIMIT_NICE,
 | 
			
		||||
	"nofile":     RLIMIT_NOFILE,
 | 
			
		||||
	"nproc":      RLIMIT_NPROC,
 | 
			
		||||
	"rss":        RLIMIT_RSS,
 | 
			
		||||
	"rtprio":     RLIMIT_RTPRIO,
 | 
			
		||||
	"rttime":     RLIMIT_RTTIME,
 | 
			
		||||
	"sigpending": RLIMIT_SIGPENDING,
 | 
			
		||||
	"stack":      RLIMIT_STACK,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func Parse(val string) (*Ulimit, error) {
 | 
			
		||||
	parts := strings.SplitN(val, "=", 2)
 | 
			
		||||
	if len(parts) != 2 {
 | 
			
		||||
		return nil, fmt.Errorf("invalid ulimit argument: %s", val)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if _, exists := ulimitNameMapping[parts[0]]; !exists {
 | 
			
		||||
		return nil, fmt.Errorf("invalid ulimit type: %s", parts[0])
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	limitVals := strings.SplitN(parts[1], ":", 2)
 | 
			
		||||
	if len(limitVals) > 2 {
 | 
			
		||||
		return nil, fmt.Errorf("too many limit value arguments - %s, can only have up to two, `soft[:hard]`", parts[1])
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	soft, err := strconv.ParseInt(limitVals[0], 10, 64)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	hard := soft // in case no hard was set
 | 
			
		||||
	if len(limitVals) == 2 {
 | 
			
		||||
		hard, err = strconv.ParseInt(limitVals[1], 10, 64)
 | 
			
		||||
	}
 | 
			
		||||
	if soft > hard {
 | 
			
		||||
		return nil, fmt.Errorf("ulimit soft limit must be less than or equal to hard limit: %d > %d", soft, hard)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return &Ulimit{Name: parts[0], Soft: soft, Hard: hard}, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (u *Ulimit) GetRlimit() (*Rlimit, error) {
 | 
			
		||||
	t, exists := ulimitNameMapping[u.Name]
 | 
			
		||||
	if !exists {
 | 
			
		||||
		return nil, fmt.Errorf("invalid ulimit name %s", u.Name)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return &Rlimit{Type: t, Soft: uint64(u.Soft), Hard: uint64(u.Hard)}, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (u *Ulimit) String() string {
 | 
			
		||||
	return fmt.Sprintf("%s=%s:%s", u.Name, u.Soft, u.Hard)
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										41
									
								
								pkg/ulimit/ulimit_test.go
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								pkg/ulimit/ulimit_test.go
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,41 @@
 | 
			
		|||
package ulimit
 | 
			
		||||
 | 
			
		||||
import "testing"
 | 
			
		||||
 | 
			
		||||
func TestParseInvalidLimitType(t *testing.T) {
 | 
			
		||||
	if _, err := Parse("notarealtype=1024:1024"); err == nil {
 | 
			
		||||
		t.Fatalf("expected error on invalid ulimit type")
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestParseBadFormat(t *testing.T) {
 | 
			
		||||
	if _, err := Parse("nofile:1024:1024"); err == nil {
 | 
			
		||||
		t.Fatal("expected error on bad syntax")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if _, err := Parse("nofile"); err == nil {
 | 
			
		||||
		t.Fatal("expected error on bad syntax")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if _, err := Parse("nofile="); err == nil {
 | 
			
		||||
		t.Fatal("expected error on bad syntax")
 | 
			
		||||
	}
 | 
			
		||||
	if _, err := Parse("nofile=:"); err == nil {
 | 
			
		||||
		t.Fatal("expected error on bad syntax")
 | 
			
		||||
	}
 | 
			
		||||
	if _, err := Parse("nofile=:1024"); err == nil {
 | 
			
		||||
		t.Fatal("expected error on bad syntax")
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestParseHardLessThanSoft(t *testing.T) {
 | 
			
		||||
	if _, err := Parse("nofile:1024:1"); err == nil {
 | 
			
		||||
		t.Fatal("expected error on hard limit less than soft limit")
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestParseInvalidValueType(t *testing.T) {
 | 
			
		||||
	if _, err := Parse("nofile:asdf"); err == nil {
 | 
			
		||||
		t.Fatal("expected error on bad value type")
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -5,6 +5,7 @@ import (
 | 
			
		|||
 | 
			
		||||
	"github.com/docker/docker/engine"
 | 
			
		||||
	"github.com/docker/docker/nat"
 | 
			
		||||
	"github.com/docker/docker/pkg/ulimit"
 | 
			
		||||
	"github.com/docker/docker/utils"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -119,6 +120,7 @@ type HostConfig struct {
 | 
			
		|||
	RestartPolicy   RestartPolicy
 | 
			
		||||
	SecurityOpt     []string
 | 
			
		||||
	ReadonlyRootfs  bool
 | 
			
		||||
	Ulimits         []*ulimit.Ulimit
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// This is used by the create command when you want to set both the
 | 
			
		||||
| 
						 | 
				
			
			@ -156,6 +158,9 @@ func ContainerHostConfigFromJob(job *engine.Job) *HostConfig {
 | 
			
		|||
	job.GetenvJson("PortBindings", &hostConfig.PortBindings)
 | 
			
		||||
	job.GetenvJson("Devices", &hostConfig.Devices)
 | 
			
		||||
	job.GetenvJson("RestartPolicy", &hostConfig.RestartPolicy)
 | 
			
		||||
 | 
			
		||||
	job.GetenvJson("Ulimits", &hostConfig.Ulimits)
 | 
			
		||||
 | 
			
		||||
	hostConfig.SecurityOpt = job.GetenvList("SecurityOpt")
 | 
			
		||||
	if Binds := job.GetenvList("Binds"); Binds != nil {
 | 
			
		||||
		hostConfig.Binds = Binds
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -10,6 +10,7 @@ import (
 | 
			
		|||
	"github.com/docker/docker/opts"
 | 
			
		||||
	flag "github.com/docker/docker/pkg/mflag"
 | 
			
		||||
	"github.com/docker/docker/pkg/parsers"
 | 
			
		||||
	"github.com/docker/docker/pkg/ulimit"
 | 
			
		||||
	"github.com/docker/docker/pkg/units"
 | 
			
		||||
	"github.com/docker/docker/utils"
 | 
			
		||||
)
 | 
			
		||||
| 
						 | 
				
			
			@ -32,6 +33,9 @@ func Parse(cmd *flag.FlagSet, args []string) (*Config, *HostConfig, *flag.FlagSe
 | 
			
		|||
		flEnv     = opts.NewListOpts(opts.ValidateEnv)
 | 
			
		||||
		flDevices = opts.NewListOpts(opts.ValidatePath)
 | 
			
		||||
 | 
			
		||||
		ulimits   = make(map[string]*ulimit.Ulimit)
 | 
			
		||||
		flUlimits = opts.NewUlimitOpt(ulimits)
 | 
			
		||||
 | 
			
		||||
		flPublish     = opts.NewListOpts(nil)
 | 
			
		||||
		flExpose      = opts.NewListOpts(nil)
 | 
			
		||||
		flDns         = opts.NewListOpts(opts.ValidateIPAddress)
 | 
			
		||||
| 
						 | 
				
			
			@ -82,6 +86,7 @@ func Parse(cmd *flag.FlagSet, args []string) (*Config, *HostConfig, *flag.FlagSe
 | 
			
		|||
	cmd.Var(&flCapAdd, []string{"-cap-add"}, "Add Linux capabilities")
 | 
			
		||||
	cmd.Var(&flCapDrop, []string{"-cap-drop"}, "Drop Linux capabilities")
 | 
			
		||||
	cmd.Var(&flSecurityOpt, []string{"-security-opt"}, "Security Options")
 | 
			
		||||
	cmd.Var(flUlimits, []string{"-ulimit"}, "Ulimit options")
 | 
			
		||||
 | 
			
		||||
	cmd.Require(flag.Min, 1)
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -309,6 +314,7 @@ func Parse(cmd *flag.FlagSet, args []string) (*Config, *HostConfig, *flag.FlagSe
 | 
			
		|||
		RestartPolicy:   restartPolicy,
 | 
			
		||||
		SecurityOpt:     flSecurityOpt.GetAll(),
 | 
			
		||||
		ReadonlyRootfs:  *flReadonlyRootfs,
 | 
			
		||||
		Ulimits:         flUlimits.GetList(),
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// When allocating stdin in attached mode, close stdin at client disconnect
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue