From c1e3f6196124f8c757a7017ae2bba7f8c05fde20 Mon Sep 17 00:00:00 2001 From: Derek McGowan Date: Wed, 28 Jan 2015 16:30:00 -0800 Subject: [PATCH 001/204] Add distribution maintainers to maintainers files Signed-off-by: Derek McGowan (github: dmcgowan) --- MAINTAINERS | 30 +++++++++++++++++++++++++++++- graph/MAINTAINERS | 2 ++ registry/MAINTAINERS | 4 +++- 3 files changed, 34 insertions(+), 2 deletions(-) diff --git a/MAINTAINERS b/MAINTAINERS index da7a2c851f..706a7d1cc9 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -339,7 +339,10 @@ made through a pull request. "dmp42", "vbatts", "joffrey", - "samalba" + "samalba", + "sday", + "jlhawn", + "dmcg" ] [Org.Subsystems."build tools"] @@ -399,6 +402,16 @@ made through a pull request. Email = "ben@firshman.co.uk" Github = "bfirsh" + [people.dmcg] + Name = "Derek McGowan" + Email = "derek@docker.com" + Github = "dmcgowan" + + [people.dmp42] + Name = "Olivier Gambier" + Email = "olivier@docker.com" + Github = "dmp42" + [people.ehazlett] Name = "Evan Hazlett" Email = "ejhazlett@gmail.com" @@ -424,11 +437,26 @@ made through a pull request. Email = "jess@docker.com" Github = "jfrazelle" + [people.jlhawn] + Name = "Josh Hawn" + Email = "josh.hawn@docker.com" + Github = "jlhawn" + + [people.joffrey] + Name = "Joffrey Fuhrer" + Email = "joffrey@docker.com" + Github = "shin-" + [people.lk4d4] Name = "Alexander Morozov" Email = "lk4d4@docker.com" Github = "lk4d4" + [people.sday] + Name = "Stephen Day" + Email = "stephen.day@docker.com" + Github = "stevvooe" + [people.shykes] Name = "Solomon Hykes" Email = "solomon@docker.com" diff --git a/graph/MAINTAINERS b/graph/MAINTAINERS index e409454b5e..3d685b7453 100644 --- a/graph/MAINTAINERS +++ b/graph/MAINTAINERS @@ -3,3 +3,5 @@ Victor Vieux (@vieux) Michael Crosby (@crosbymichael) Cristian Staretu (@unclejack) Tibor Vass (@tiborvass) +Josh Hawn (@jlhawn) +Derek McGowan (@dmcgowan) diff --git a/registry/MAINTAINERS b/registry/MAINTAINERS index fdb03ed573..a75e15b4ef 100644 --- a/registry/MAINTAINERS +++ b/registry/MAINTAINERS @@ -1,5 +1,7 @@ Sam Alba (@samalba) Joffrey Fuhrer (@shin-) -Ken Cochrane (@kencochrane) Vincent Batts (@vbatts) Olivier Gambier (@dmp42) +Josh Hawn (@jlhawn) +Derek McGowan (@dmcgowan) +Stephen Day (@stevvooe) From 0fadc9bd90c14acfd14a8c5e87d80e7a72438e2a Mon Sep 17 00:00:00 2001 From: "Andrew C. Bodine" Date: Fri, 6 Feb 2015 09:51:01 -0800 Subject: [PATCH 002/204] adds notify user of login credential persistence for registry Signed-off-by: Andrew C. Bodine --- api/client/commands.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/api/client/commands.go b/api/client/commands.go index 7d1f6e7002..eb1d36b59c 100644 --- a/api/client/commands.go +++ b/api/client/commands.go @@ -393,6 +393,8 @@ func (cli *DockerCli) CmdLogin(args ...string) error { return err } registry.SaveConfig(cli.configFile) + fmt.Fprintf(cli.out, "WARNING: login credentials saved in %s.\n", path.Join(homedir.Get(), registry.CONFIGFILE)) + if out2.Get("Status") != "" { fmt.Fprintf(cli.out, "%s\n", out2.Get("Status")) } From d15f8feaa86453147161db6e1b2ec2538e11ecfd Mon Sep 17 00:00:00 2001 From: Sven Dowideit Date: Mon, 16 Feb 2015 10:02:56 +1000 Subject: [PATCH 003/204] Add some information about the docker build return code Signed-off-by: Sven Dowideit --- docs/sources/reference/commandline/cli.md | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/docs/sources/reference/commandline/cli.md b/docs/sources/reference/commandline/cli.md index 1d784d72e1..a0136fe02c 100644 --- a/docs/sources/reference/commandline/cli.md +++ b/docs/sources/reference/commandline/cli.md @@ -525,6 +525,29 @@ on your local host, and then this is sent to the Docker daemon as the context. This way, your local user credentials and VPN's etc can be used to access private repositories. +### Return code + +On a successful build, a return code of success `0` will be returned. +When the build fails, a non-zero failure code will be returned. + +There should be informational output of the reason for failure output +to `STDERR`: + +``` +$ docker build -t fail . +Sending build context to Docker daemon 2.048 kB +Sending build context to Docker daemon +Step 0 : FROM busybox + ---> 4986bf8c1536 +Step 1 : RUN exit 13 + ---> Running in e26670ec7a0a +INFO[0000] The command [/bin/sh -c exit 13] returned a non-zero code: 13 +$ echo $? +1 +``` + +### .dockerignore file + If a file named `.dockerignore` exists in the root of `PATH` then it is interpreted as a newline-separated list of exclusion patterns. Exclusion patterns match files or directories relative to `PATH` that From b422d8da8f1d1e51da6cfff4890e6a08252da7c5 Mon Sep 17 00:00:00 2001 From: Nghia Tran Date: Fri, 20 Feb 2015 19:18:30 -0800 Subject: [PATCH 004/204] engine.Tail() to ignore trailing whitespaces. In its current form, if an error message has two trailing "\n" instead of one, an empty line is resulted (see engine/job.go for an example of such usages). Skipping all trailing whitespaces will give a better error message. Signed-off-by: Nghia Tran --- engine/streams.go | 20 +++++++++++--------- engine/streams_test.go | 5 +++++ 2 files changed, 16 insertions(+), 9 deletions(-) diff --git a/engine/streams.go b/engine/streams.go index ec703c96fa..216fb8980a 100644 --- a/engine/streams.go +++ b/engine/streams.go @@ -5,7 +5,9 @@ import ( "fmt" "io" "io/ioutil" + "strings" "sync" + "unicode" ) type Output struct { @@ -16,25 +18,25 @@ type Output struct { } // Tail returns the n last lines of a buffer -// stripped out of the last \n, if any +// stripped out of trailing white spaces, if any. +// // if n <= 0, returns an empty string func Tail(buffer *bytes.Buffer, n int) string { if n <= 0 { return "" } - bytes := buffer.Bytes() - if len(bytes) > 0 && bytes[len(bytes)-1] == '\n' { - bytes = bytes[:len(bytes)-1] - } - for i := buffer.Len() - 2; i >= 0; i-- { - if bytes[i] == '\n' { + s := strings.TrimRightFunc(buffer.String(), unicode.IsSpace) + i := len(s) - 1 + for ; i >= 0 && n > 0; i-- { + if s[i] == '\n' { n-- if n == 0 { - return string(bytes[i+1:]) + break } } } - return string(bytes) + // when i == -1, return the whole string which is s[0:] + return s[i+1:] } // NewOutput returns a new Output object with no destinations attached. diff --git a/engine/streams_test.go b/engine/streams_test.go index 5cfd5d0e6c..476a721baf 100644 --- a/engine/streams_test.go +++ b/engine/streams_test.go @@ -111,6 +111,11 @@ func TestTail(t *testing.T) { "Two\nThree", "One\nTwo\nThree", } + tests["One\nTwo\n\n\n"] = []string{ + "", + "Two", + "One\nTwo", + } for input, outputs := range tests { for n, expectedOutput := range outputs { output := Tail(bytes.NewBufferString(input), n) From 8c19e43dff7d46e6213eac512d44bb2582d1c959 Mon Sep 17 00:00:00 2001 From: Daniel YC Lin Date: Mon, 9 Feb 2015 16:25:42 +0800 Subject: [PATCH 005/204] Update sample systemd for container 1. Docker require to run before redis container run. 2. 'start' command can not accept more options like "run -e xx ..." 3. Remove wrong command 'Author=' Signed-off-by: Daniel YC Lin --- docs/sources/articles/host_integration.md | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/docs/sources/articles/host_integration.md b/docs/sources/articles/host_integration.md index 89fd2a1f7a..cda86688e8 100644 --- a/docs/sources/articles/host_integration.md +++ b/docs/sources/articles/host_integration.md @@ -59,18 +59,27 @@ a new service that will be started after the docker daemon service has started. /usr/bin/docker start -a redis_server end script - ### systemd [Unit] Description=Redis container - Author=Me + Requires=docker.service After=docker.service [Service] Restart=always ExecStart=/usr/bin/docker start -a redis_server + # for more options, use 'run' instead of 'start', but not suggested + # ExecStart=/usr/bin/docker run redis_server ExecStop=/usr/bin/docker stop -t 2 redis_server [Install] WantedBy=local.target + +if you need to pass options to the redis container (such as '--env'), +then you'll need to use 'docker run' rather than 'docker start'. + + [Service] + ... + ExecStart=/usr/bin/docker run --env foo=bar redis_server + ... From 814916457b50082e4299691ae92b4ed368ddb076 Mon Sep 17 00:00:00 2001 From: Sven Dowideit Date: Thu, 12 Feb 2015 13:02:54 +1000 Subject: [PATCH 006/204] tweak the prose a little Signed-off-by: Sven Dowideit --- docs/sources/articles/host_integration.md | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/docs/sources/articles/host_integration.md b/docs/sources/articles/host_integration.md index cda86688e8..cbcb21a357 100644 --- a/docs/sources/articles/host_integration.md +++ b/docs/sources/articles/host_integration.md @@ -69,17 +69,18 @@ a new service that will be started after the docker daemon service has started. [Service] Restart=always ExecStart=/usr/bin/docker start -a redis_server - # for more options, use 'run' instead of 'start', but not suggested - # ExecStart=/usr/bin/docker run redis_server ExecStop=/usr/bin/docker stop -t 2 redis_server [Install] WantedBy=local.target -if you need to pass options to the redis container (such as '--env'), -then you'll need to use 'docker run' rather than 'docker start'. +If you need to pass options to the redis container (such as `--env`), +then you'll need to use `docker run` rather than `docker start`. This will +create a new container every time the service is started, which will be stopped +and removed when the service is stopped. [Service] ... - ExecStart=/usr/bin/docker run --env foo=bar redis_server + ExecStart=/usr/bin/docker run --env foo=bar --name redis_server redis + ExecStop=/usr/bin/docker stop -t 2 redis_server ; /usr/bin/docker rm -f redis_server ... From ed3fe85ba3b30d2586108a707fe20bba6c667e91 Mon Sep 17 00:00:00 2001 From: Jason Stangroome Date: Thu, 26 Feb 2015 14:18:19 +1100 Subject: [PATCH 007/204] Correct IP/MAC address generation docs The MAC address is generated from the IP address, not the other way. Signed-off-by: Jason Stangroome --- docs/sources/articles/networking.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/docs/sources/articles/networking.md b/docs/sources/articles/networking.md index 5c3e885e17..ec061bdec9 100644 --- a/docs/sources/articles/networking.md +++ b/docs/sources/articles/networking.md @@ -772,10 +772,11 @@ The steps with which Docker configures a container are: 5. Give the container's `eth0` a new IP address from within the bridge's range of network addresses, and set its default route to - the IP address that the Docker host owns on the bridge. If available - the IP address is generated from the MAC address. This prevents ARP - cache invalidation problems, when a new container comes up with an - IP used in the past by another container with another MAC. + the IP address that the Docker host owns on the bridge. The MAC + address is generated from the IP address unless otherwise specified. + This prevents ARP cache invalidation problems, when a new container + comes up with an IP used in the past by another container with another + MAC. With these steps complete, the container now possesses an `eth0` (virtual) network card and will find itself able to communicate with From 351074edcd22e4ca587713feda541268a66cdb86 Mon Sep 17 00:00:00 2001 From: Tianon Gravi Date: Fri, 27 Feb 2015 22:53:36 -0700 Subject: [PATCH 008/204] Download busybox from the Hub instead of GitHub This downloads a specific image ID of `busybox:latest` from the Hub directly (within the `Dockerfile`, ready for `docker load`) instead of grabbing the source from GitHub and doing a `docker build` at daemon start time. This ensures the test suite runs more consistently. Signed-off-by: Andrew "Tianon" Page --- Dockerfile | 7 +-- contrib/download-frozen-image.sh | 90 ++++++++++++++++++++++++++++++++ project/make/.ensure-busybox | 15 +++++- 3 files changed, 107 insertions(+), 5 deletions(-) create mode 100755 contrib/download-frozen-image.sh diff --git a/Dockerfile b/Dockerfile index c3d1c246bf..a7cbf468d6 100644 --- a/Dockerfile +++ b/Dockerfile @@ -107,9 +107,6 @@ RUN go get golang.org/x/tools/cmd/cover # TODO replace FPM with some very minimal debhelper stuff RUN gem install --no-rdoc --no-ri fpm --version 1.3.2 -# Get the "busybox" image source so we can build locally instead of pulling -RUN git clone -b buildroot-2014.02 https://github.com/jpetazzo/docker-busybox.git /docker-busybox - # Install registry ENV REGISTRY_COMMIT c448e0416925a9876d5576e412703c9b8b865e19 RUN set -x \ @@ -145,6 +142,10 @@ ENV DOCKER_BUILDTAGS apparmor selinux btrfs_noversion # Let us use a .bashrc file RUN ln -sfv $PWD/.bashrc ~/.bashrc +# Get the "busybox" image so we can "docker load" locally instead of pulling +COPY contrib/download-frozen-image.sh /go/src/github.com/docker/docker/contrib/ +RUN ./contrib/download-frozen-image.sh /docker-busybox busybox@4986bf8c15363d1c5d15512d5266f8777bfba4974ac56e3270e7760f6f0a8125 + # Install man page generator COPY vendor /go/src/github.com/docker/docker/vendor # (copy vendor/ because go-md2man needs golang.org/x/net) diff --git a/contrib/download-frozen-image.sh b/contrib/download-frozen-image.sh new file mode 100755 index 0000000000..2e3eb98e54 --- /dev/null +++ b/contrib/download-frozen-image.sh @@ -0,0 +1,90 @@ +#!/bin/bash +set -e + +# hello-world latest ef872312fe1b 3 months ago 910 B +# hello-world latest ef872312fe1bbc5e05aae626791a47ee9b032efa8f3bda39cc0be7b56bfe59b9 3 months ago 910 B + +# debian latest f6fab3b798be 10 weeks ago 85.1 MB +# debian latest f6fab3b798be3174f45aa1eb731f8182705555f89c9026d8c1ef230cbf8301dd 10 weeks ago 85.1 MB + +usage() { + echo "usage: $0 dir image[:tag][@image-id] ..." + echo " ie: $0 /tmp/hello-world hello-world" + echo " $0 /tmp/debian-jessie debian:jessie" + echo " $0 /tmp/old-hello-world hello-world@ef872312fe1bbc5e05aae626791a47ee9b032efa8f3bda39cc0be7b56bfe59b9" + echo " $0 /tmp/old-debian debian:latest@f6fab3b798be3174f45aa1eb731f8182705555f89c9026d8c1ef230cbf8301dd" + [ -z "$1" ] || exit "$1" +} + +dir="$1" # dir for building tar in +shift || usage 1 >&2 + +[ $# -gt 0 -a "$dir" ] || usage 2 >&2 +mkdir -p "$dir" + +declare -A repositories=() + +while [ $# -gt 0 ]; do + imageTag="$1" + shift + image="${imageTag%%[:@]*}" + tag="${imageTag#*:}" + imageId="${tag##*@}" + [ "$imageId" != "$tag" ] || imageId= + [ "$tag" != "$imageTag" ] || tag='latest' + tag="${tag%@*}" + + token="$(curl -sSL -o /dev/null -D- -H 'X-Docker-Token: true' "https://index.docker.io/v1/repositories/$image/images" | tr -d '\r' | awk -F ': *' '$1 == "X-Docker-Token" { print $2 }')" + + if [ -z "$imageId" ]; then + imageId="$(curl -sSL -H "Authorization: Token $token" "https://registry-1.docker.io/v1/repositories/$image/tags/$tag")" + imageId="${imageId//\"/}" + fi + + ancestryJson="$(curl -sSL -H "Authorization: Token $token" "https://registry-1.docker.io/v1/images/$imageId/ancestry")" + if [ "${ancestryJson:0:1}" != '[' ]; then + echo >&2 "error: /v1/images/$imageId/ancestry returned something unexpected:" + echo >&2 " $ancestryJson" + exit 1 + fi + + IFS=',' + ancestry=( ${ancestryJson//[\[\] \"]/} ) + unset IFS + + [ -z "${repositories[$image]}" ] || repositories[$image]+=', ' + repositories[$image]+='"'"$tag"'": "'"$imageId"'"' + + echo "Downloading '$imageTag' (${#ancestry[@]} layers)..." + for imageId in "${ancestry[@]}"; do + mkdir -p "$dir/$imageId" + echo '1.0' > "$dir/$imageId/VERSION" + + curl -sSL -H "Authorization: Token $token" "https://registry-1.docker.io/v1/images/$imageId/json" -o "$dir/$imageId/json" -C - + + # TODO figure out why "-C -" doesn't work here + # "curl: (33) HTTP server doesn't seem to support byte ranges. Cannot resume." + # "HTTP/1.1 416 Requested Range Not Satisfiable" + if [ -f "$dir/$imageId/layer.tar" ]; then + # TODO hackpatch for no -C support :'( + echo "skipping existing ${imageId:0:12}" + continue + fi + curl -SL --progress -H "Authorization: Token $token" "https://registry-1.docker.io/v1/images/$imageId/layer" -o "$dir/$imageId/layer.tar" # -C - + done + echo +done + +echo -n '{' > "$dir/repositories" +firstImage=1 +for image in "${!repositories[@]}"; do + [ "$firstImage" ] || echo -n ',' >> "$dir/repositories" + firstImage= + echo -n $'\n\t' >> "$dir/repositories" + echo -n '"'"$image"'": { '"${repositories[$image]}"' }' >> "$dir/repositories" +done +echo -n $'\n}\n' >> "$dir/repositories" + +echo "Download of images into '$dir' complete." +echo "Use something like the following to load the result into a Docker daemon:" +echo " tar -cC '$dir' . | docker load" diff --git a/project/make/.ensure-busybox b/project/make/.ensure-busybox index 24ba3052db..fef9a0135c 100644 --- a/project/make/.ensure-busybox +++ b/project/make/.ensure-busybox @@ -2,8 +2,19 @@ set -e if ! docker inspect busybox &> /dev/null; then - if [ -d /docker-busybox ]; then - ( set -x; docker build -t busybox /docker-busybox ) + hardCodedDir='/docker-busybox' + if [ -d "$hardCodedDir" ]; then + ( set -x; tar -cC "$hardCodedDir" . | docker load ) + elif [ -e Dockerfile ] && command -v curl > /dev/null; then + # testing for "curl" because "download-frozen-image.sh" is built around curl + dir="$DEST/busybox" + # extract the exact "download-frozen-image.sh" line from the Dockerfile itself for consistency + awk '$1 == "RUN" && $2 == "./contrib/download-frozen-image.sh" && /busybox@/ { + for (i = 2; i < NF; i++) + printf ( $i == "'"$hardCodedDir"'" ? "'"$dir"'" : $i ) " "; + print $NF; + }' Dockerfile | sh -x + ( set -x; tar -cC "$dir" . | docker load ) else ( set -x; docker pull busybox ) fi From 5118f1431c9cae757d5e7c193ed85fc8fca3ae85 Mon Sep 17 00:00:00 2001 From: Tianon Gravi Date: Fri, 27 Feb 2015 22:37:25 -0700 Subject: [PATCH 009/204] Add secondary "simple" Dockerfile This is the absolute bare minimum necessary to compile and test Docker -- this is going to be especially useful for testing and verifying assumptions. With this, we can setup a Jenkins job that tests to ensure that all the work we do to make sure our build scripts and tests don't contain assumptions is not effort spent in vain. This is important because this is the kind of bare-bones stock environment our packagers build in. Additionally, this verifies that our scripts will work reasonably on other platforms (such as Darwin and Windows) as well. Assumptions existing tests make that currently fail: - `registry-v2` exists as a binary in `$PATH` (FIXED IN #11005 :tada:) - `unprivilegeduser` exists as a user in `/etc/passwd` Signed-off-by: Andrew "Tianon" Page --- Dockerfile.simple | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 Dockerfile.simple diff --git a/Dockerfile.simple b/Dockerfile.simple new file mode 100644 index 0000000000..b6a7edbee7 --- /dev/null +++ b/Dockerfile.simple @@ -0,0 +1,32 @@ +# docker build -t docker:simple -f Dockerfile.simple . +# docker run --rm docker:simple hack/make.sh dynbinary +# docker run --rm --privileged docker:simple hack/make.sh test-unit +# docker run --rm --privileged docker:simple hack/dind hack/make.sh dynbinary test-integration-cli + +# This represents the bare minimum required to build and test Docker. + +FROM debian:jessie + +# compile and runtime deps +# https://github.com/docker/docker/blob/master/project/PACKAGERS.md#build-dependencies +# https://github.com/docker/docker/blob/master/project/PACKAGERS.md#runtime-dependencies +RUN apt-get update && apt-get install -y --no-install-recommends \ + btrfs-tools \ + gcc \ + git \ + golang \ + libdevmapper-dev \ + libsqlite3-dev \ + \ + ca-certificates \ + e2fsprogs \ + iptables \ + procps \ + xz-utils \ + \ + lxc \ + && rm -rf /var/lib/apt/lists/* + +ENV AUTO_GOPATH 1 +WORKDIR /usr/src/docker +COPY . /usr/src/docker From dec67f7f573855cd1760ed180efc006f5a900c2d Mon Sep 17 00:00:00 2001 From: Tianon Gravi Date: Mon, 2 Mar 2015 18:33:38 -0700 Subject: [PATCH 010/204] Update contrib/mkimage/debootstrap whitespace for consistency Signed-off-by: Andrew "Tianon" Page --- contrib/mkimage/debootstrap | 46 ++++++++++++++++++------------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/contrib/mkimage/debootstrap b/contrib/mkimage/debootstrap index c7a2b66830..0438ff2b29 100755 --- a/contrib/mkimage/debootstrap +++ b/contrib/mkimage/debootstrap @@ -28,13 +28,13 @@ shift # prevent init scripts from running during install/update echo >&2 "+ echo exit 101 > '$rootfsDir/usr/sbin/policy-rc.d'" cat > "$rootfsDir/usr/sbin/policy-rc.d" <<'EOF' -#!/bin/sh + #!/bin/sh -# For most Docker users, "apt-get install" only happens during "docker build", -# where starting services doesn't work and often fails in humorous ways. This -# prevents those failures by stopping the services from attempting to start. + # For most Docker users, "apt-get install" only happens during "docker build", + # where starting services doesn't work and often fails in humorous ways. This + # prevents those failures by stopping the services from attempting to start. -exit 101 + exit 101 EOF chmod +x "$rootfsDir/usr/sbin/policy-rc.d" @@ -59,12 +59,12 @@ if strings "$rootfsDir/usr/bin/dpkg" | grep -q unsafe-io; then # force dpkg not to call sync() after package extraction (speeding up installs) echo >&2 "+ echo force-unsafe-io > '$rootfsDir/etc/dpkg/dpkg.cfg.d/docker-apt-speedup'" cat > "$rootfsDir/etc/dpkg/dpkg.cfg.d/docker-apt-speedup" <<-'EOF' - # For most Docker users, package installs happen during "docker build", which - # doesn't survive power loss and gets restarted clean afterwards anyhow, so - # this minor tweak gives us a nice speedup (much nicer on spinning disks, - # obviously). + # For most Docker users, package installs happen during "docker build", which + # doesn't survive power loss and gets restarted clean afterwards anyhow, so + # this minor tweak gives us a nice speedup (much nicer on spinning disks, + # obviously). - force-unsafe-io + force-unsafe-io EOF fi @@ -97,26 +97,26 @@ if [ -d "$rootfsDir/etc/apt/apt.conf.d" ]; then # remove apt-cache translations for fast "apt-get update" echo >&2 "+ echo Acquire::Languages 'none' > '$rootfsDir/etc/apt/apt.conf.d/docker-no-languages'" cat > "$rootfsDir/etc/apt/apt.conf.d/docker-no-languages" <<-'EOF' - # In Docker, we don't often need the "Translations" files, so we're just wasting - # time and space by downloading them, and this inhibits that. For users that do - # need them, it's a simple matter to delete this file and "apt-get update". :) + # In Docker, we don't often need the "Translations" files, so we're just wasting + # time and space by downloading them, and this inhibits that. For users that do + # need them, it's a simple matter to delete this file and "apt-get update". :) - Acquire::Languages "none"; + Acquire::Languages "none"; EOF echo >&2 "+ echo Acquire::GzipIndexes 'true' > '$rootfsDir/etc/apt/apt.conf.d/docker-gzip-indexes'" cat > "$rootfsDir/etc/apt/apt.conf.d/docker-gzip-indexes" <<-'EOF' - # Since Docker users using "RUN apt-get update && apt-get install -y ..." in - # their Dockerfiles don't go delete the lists files afterwards, we want them to - # be as small as possible on-disk, so we explicitly request "gz" versions and - # tell Apt to keep them gzipped on-disk. + # Since Docker users using "RUN apt-get update && apt-get install -y ..." in + # their Dockerfiles don't go delete the lists files afterwards, we want them to + # be as small as possible on-disk, so we explicitly request "gz" versions and + # tell Apt to keep them gzipped on-disk. - # For comparison, an "apt-get update" layer without this on a pristine - # "debian:wheezy" base image was "29.88 MB", where with this it was only - # "8.273 MB". + # For comparison, an "apt-get update" layer without this on a pristine + # "debian:wheezy" base image was "29.88 MB", where with this it was only + # "8.273 MB". - Acquire::GzipIndexes "true"; - Acquire::CompressionTypes::Order:: "gz"; + Acquire::GzipIndexes "true"; + Acquire::CompressionTypes::Order:: "gz"; EOF fi From 8625a281c66180af867f59a041560bf5035ae404 Mon Sep 17 00:00:00 2001 From: Tianon Gravi Date: Tue, 3 Mar 2015 12:41:26 -0700 Subject: [PATCH 011/204] Rename .dockerversion to .go-autogen so it's clear that all autogenerated code goes here Signed-off-by: Andrew "Tianon" Page --- project/make.sh | 2 +- project/make/.dockerinit | 2 +- project/make/{.dockerversion => .go-autogen} | 8 +------- project/make/binary | 2 +- 4 files changed, 4 insertions(+), 10 deletions(-) rename project/make/{.dockerversion => .go-autogen} (75%) diff --git a/project/make.sh b/project/make.sh index ec880df39c..0db70a750e 100755 --- a/project/make.sh +++ b/project/make.sh @@ -101,7 +101,7 @@ fi # Use these flags when compiling the tests and final binary IAMSTATIC='true' -source "$(dirname "$BASH_SOURCE")/make/.dockerversion" +source "$(dirname "$BASH_SOURCE")/make/.go-autogen" LDFLAGS='-w' LDFLAGS_STATIC='-linkmode external' diff --git a/project/make/.dockerinit b/project/make/.dockerinit index 0f51fce0ac..f98158d0b9 100644 --- a/project/make/.dockerinit +++ b/project/make/.dockerinit @@ -2,7 +2,7 @@ set -e IAMSTATIC="true" -source "$(dirname "$BASH_SOURCE")/.dockerversion" +source "$(dirname "$BASH_SOURCE")/.go-autogen" # dockerinit still needs to be a static binary, even if docker is dynamic go build \ diff --git a/project/make/.dockerversion b/project/make/.go-autogen similarity index 75% rename from project/make/.dockerversion rename to project/make/.go-autogen index c96ff065bd..1b48f611ef 100644 --- a/project/make/.dockerversion +++ b/project/make/.go-autogen @@ -1,6 +1,7 @@ #!/bin/bash rm -rf autogen + mkdir -p autogen/dockerversion cat > autogen/dockerversion/dockerversion.go < autogen/dockerversion/static.go < Date: Mon, 9 Feb 2015 21:02:53 +0000 Subject: [PATCH 012/204] Allow use of just image name without the tag Addresses #10645 Signed-off-by: Srini Brahmaroutu --- events/events.go | 7 ++++ integration-cli/docker_cli_events_test.go | 44 +++++++++++++++++++++++ 2 files changed, 51 insertions(+) diff --git a/events/events.go b/events/events.go index 0951f7099d..91babd1356 100644 --- a/events/events.go +++ b/events/events.go @@ -2,6 +2,7 @@ package events import ( "encoding/json" + "strings" "sync" "time" @@ -112,6 +113,12 @@ func writeEvent(job *engine.Job, event *utils.JSONMessage, eventFilters filters. if v == field { return false } + if strings.Contains(field, ":") { + image := strings.Split(field, ":") + if image[0] == v { + return false + } + } } return true } diff --git a/integration-cli/docker_cli_events_test.go b/integration-cli/docker_cli_events_test.go index fd542baf25..312580761c 100644 --- a/integration-cli/docker_cli_events_test.go +++ b/integration-cli/docker_cli_events_test.go @@ -245,3 +245,47 @@ func TestEventsFilters(t *testing.T) { logDone("events - filters") } + +func TestEventsFilterImageName(t *testing.T) { + since := time.Now().Unix() + defer deleteAllContainers() + + out, _, err := runCommandWithOutput(exec.Command(dockerBinary, "run", "--name", "container_1", "-d", "busybox", "true")) + if err != nil { + t.Fatal(out, err) + } + container1 := stripTrailingCharacters(out) + out, _, err = runCommandWithOutput(exec.Command(dockerBinary, "run", "--name", "container_2", "-d", "busybox", "true")) + if err != nil { + t.Fatal(out, err) + } + container2 := stripTrailingCharacters(out) + + for _, s := range []string{"busybox", "busybox:latest"} { + eventsCmd := exec.Command(dockerBinary, "events", fmt.Sprintf("--since=%d", since), fmt.Sprintf("--until=%d", time.Now().Unix()), "--filter", fmt.Sprintf("image=%s", s)) + out, _, err := runCommandWithOutput(eventsCmd) + if err != nil { + t.Fatalf("Failed to get events, error: %s(%s)", err, out) + } + events := strings.Split(out, "\n") + events = events[:len(events)-1] + if len(events) == 0 { + t.Fatalf("Expected events but found none for the image busybox:latest") + } + count1 := 0 + count2 := 0 + for _, e := range events { + if strings.Contains(e, container1) { + count1++ + } else if strings.Contains(e, container2) { + count2++ + } + } + if count1 == 0 || count2 == 0 { + t.Fatalf("Expected events from each container but got %d from %s and %d from %s", count1, container1, count2, container2) + } + } + + logDone("events - filters using image") + +} From e2b8933d213b283578babc1d86538950295e4fc7 Mon Sep 17 00:00:00 2001 From: Martijn Dwars Date: Fri, 27 Feb 2015 19:50:55 +0100 Subject: [PATCH 013/204] Move directory size calculation to pkg/ (fixes #10970) Signed-off-by: Martijn Dwars --- daemon/container.go | 3 +- daemon/graphdriver/aufs/aufs.go | 4 +- pkg/directory/directory.go | 39 +++++++++++ pkg/directory/directory_test.go | 120 ++++++++++++++++++++++++++++++++ utils/utils_daemon.go | 31 --------- 5 files changed, 163 insertions(+), 34 deletions(-) create mode 100644 pkg/directory/directory.go create mode 100644 pkg/directory/directory_test.go diff --git a/daemon/container.go b/daemon/container.go index 676674bc2d..964567cccc 100644 --- a/daemon/container.go +++ b/daemon/container.go @@ -26,6 +26,7 @@ import ( "github.com/docker/docker/pkg/archive" "github.com/docker/docker/pkg/broadcastwriter" "github.com/docker/docker/pkg/common" + "github.com/docker/docker/pkg/directory" "github.com/docker/docker/pkg/ioutils" "github.com/docker/docker/pkg/networkfs/etchosts" "github.com/docker/docker/pkg/networkfs/resolvconf" @@ -885,7 +886,7 @@ func (container *Container) GetSize() (int64, int64) { } if _, err = os.Stat(container.basefs); err != nil { - if sizeRootfs, err = utils.TreeSize(container.basefs); err != nil { + if sizeRootfs, err = directory.Size(container.basefs); err != nil { sizeRootfs = -1 } } diff --git a/daemon/graphdriver/aufs/aufs.go b/daemon/graphdriver/aufs/aufs.go index 103a568e21..22579b8b63 100644 --- a/daemon/graphdriver/aufs/aufs.go +++ b/daemon/graphdriver/aufs/aufs.go @@ -35,8 +35,8 @@ import ( "github.com/docker/docker/pkg/archive" "github.com/docker/docker/pkg/chrootarchive" "github.com/docker/docker/pkg/common" + "github.com/docker/docker/pkg/directory" mountpk "github.com/docker/docker/pkg/mount" - "github.com/docker/docker/utils" "github.com/docker/libcontainer/label" ) @@ -320,7 +320,7 @@ func (a *Driver) applyDiff(id string, diff archive.ArchiveReader) error { // relative to its base filesystem directory. func (a *Driver) DiffSize(id, parent string) (size int64, err error) { // AUFS doesn't need the parent layer to calculate the diff size. - return utils.TreeSize(path.Join(a.rootPath(), "diff", id)) + return directory.Size(path.Join(a.rootPath(), "diff", id)) } // ApplyDiff extracts the changeset from the given diff into the diff --git a/pkg/directory/directory.go b/pkg/directory/directory.go new file mode 100644 index 0000000000..80fb9a8332 --- /dev/null +++ b/pkg/directory/directory.go @@ -0,0 +1,39 @@ +// +build linux + +package directory + +import ( + "os" + "path/filepath" + "syscall" +) + +// Size walks a directory tree and returns its total size in bytes. +func Size(dir string) (size int64, err error) { + data := make(map[uint64]struct{}) + err = filepath.Walk(dir, func(d string, fileInfo os.FileInfo, e error) error { + // Ignore directory sizes + if fileInfo == nil { + return nil + } + + s := fileInfo.Size() + if fileInfo.IsDir() || s == 0 { + return nil + } + + // Check inode to handle hard links correctly + inode := fileInfo.Sys().(*syscall.Stat_t).Ino + // inode is not a uint64 on all platforms. Cast it to avoid issues. + if _, exists := data[uint64(inode)]; exists { + return nil + } + // inode is not a uint64 on all platforms. Cast it to avoid issues. + data[uint64(inode)] = struct{}{} + + size += s + + return nil + }) + return +} diff --git a/pkg/directory/directory_test.go b/pkg/directory/directory_test.go new file mode 100644 index 0000000000..a137c59098 --- /dev/null +++ b/pkg/directory/directory_test.go @@ -0,0 +1,120 @@ +package directory + +import ( + "os" + "testing" +) + +// Size of an empty directory should be 0 +func TestSizeEmpty(t *testing.T) { + var err error + if err = os.Mkdir("/tmp/testSizeEmptyDirectory", 0777); err != nil { + t.Fatalf("failed to create directory: %s", err) + } + + var size int64 + if size, _ = Size("/tmp/testSizeEmptyDirectory"); size != 0 { + t.Fatalf("empty directory has size: %d", size) + } +} + +// Size of a directory with one empty file should be 0 +func TestSizeEmptyFile(t *testing.T) { + var err error + if err = os.Mkdir("/tmp/testSizeEmptyFile", 0777); err != nil { + t.Fatalf("failed to create directory: %s", err) + } + + if _, err = os.Create("/tmp/testSizeEmptyFile/file"); err != nil { + t.Fatalf("failed to create file: %s", err) + } + + var size int64 + if size, _ = Size("/tmp/testSizeEmptyFile"); size != 0 { + t.Fatalf("directory with one file has size: %d", size) + } +} + +// Size of a directory with one 5-byte file should be 5 +func TestSizeNonemptyFile(t *testing.T) { + var err error + if err = os.Mkdir("/tmp/testSizeNonemptyFile", 0777); err != nil { + t.Fatalf("failed to create directory: %s", err) + } + + var file *os.File + if file, err = os.Create("/tmp/testSizeNonemptyFile/file"); err != nil { + t.Fatalf("failed to create file: %s", err) + } + + d := []byte{97, 98, 99, 100, 101} + file.Write(d) + + var size int64 + if size, _ = Size("/tmp/testSizeNonemptyFile"); size != 5 { + t.Fatalf("directory with one 5-byte file has size: %d", size) + } +} + +// Size of a directory with one empty directory should be 0 +func TestSizeNestedDirectoryEmpty(t *testing.T) { + var err error + if err = os.MkdirAll("/tmp/testSizeNestedDirectoryEmpty/nested", 0777); err != nil { + t.Fatalf("failed to create directory: %s", err) + } + + var size int64 + if size, _ = Size("/tmp/testSizeNestedDirectoryEmpty"); size != 0 { + t.Fatalf("directory with one empty directory has size: %d", size) + } +} + +// Test directory with 1 file and 1 empty directory +func TestSizeFileAndNestedDirectoryEmpty(t *testing.T) { + var err error + if err = os.MkdirAll("/tmp/testSizeFileAndNestedDirectoryEmpty/nested", 0777); err != nil { + t.Fatalf("failed to create directory: %s", err) + } + + var file *os.File + if file, err = os.Create("/tmp/testSizeFileAndNestedDirectoryEmpty/file"); err != nil { + t.Fatalf("failed to create file: %s", err) + } + + d := []byte{100, 111, 99, 107, 101, 114} + file.Write(d) + + var size int64 + if size, _ = Size("/tmp/testSizeFileAndNestedDirectoryEmpty"); size != 6 { + t.Fatalf("directory with 6-byte file and empty directory has size: %d", size) + } +} + +// Test directory with 1 file and 1 non-empty directory +func TestSizeFileAndNestedDirectoryNonempty(t *testing.T) { + var err error + if err = os.MkdirAll("/tmp/testSizeFileAndNestedDirectoryEmpty/nested", 0777); err != nil { + t.Fatalf("failed to create directory: %s", err) + } + + var file *os.File + if file, err = os.Create("/tmp/testSizeFileAndNestedDirectoryEmpty/file"); err != nil { + t.Fatalf("failed to create file: %s", err) + } + + data := []byte{100, 111, 99, 107, 101, 114} + file.Write(data) + + var nestedFile *os.File + if nestedFile, err = os.Create("/tmp/testSizeFileAndNestedDirectoryEmpty/nested/file"); err != nil { + t.Fatalf("failed to create file: %s", err) + } + + nestedData := []byte{100, 111, 99, 107, 101, 114} + nestedFile.Write(nestedData) + + var size int64 + if size, _ = Size("/tmp/testSizeFileAndNestedDirectoryEmpty"); size != 12 { + t.Fatalf("directory with 6-byte file and empty directory has size: %d", size) + } +} diff --git a/utils/utils_daemon.go b/utils/utils_daemon.go index 9989f05e31..9aee485fef 100644 --- a/utils/utils_daemon.go +++ b/utils/utils_daemon.go @@ -4,40 +4,9 @@ package utils import ( "os" - "path/filepath" "syscall" ) -// TreeSize walks a directory tree and returns its total size in bytes. -func TreeSize(dir string) (size int64, err error) { - data := make(map[uint64]struct{}) - err = filepath.Walk(dir, func(d string, fileInfo os.FileInfo, e error) error { - // Ignore directory sizes - if fileInfo == nil { - return nil - } - - s := fileInfo.Size() - if fileInfo.IsDir() || s == 0 { - return nil - } - - // Check inode to handle hard links correctly - inode := fileInfo.Sys().(*syscall.Stat_t).Ino - // inode is not a uint64 on all platforms. Cast it to avoid issues. - if _, exists := data[uint64(inode)]; exists { - return nil - } - // inode is not a uint64 on all platforms. Cast it to avoid issues. - data[uint64(inode)] = struct{}{} - - size += s - - return nil - }) - return -} - // IsFileOwner checks whether the current user is the owner of the given file. func IsFileOwner(f string) bool { if fileInfo, err := os.Stat(f); err == nil && fileInfo != nil { From e8dc07dabc962e02876cadfe5f7b508a2453bec1 Mon Sep 17 00:00:00 2001 From: Qiang Huang Date: Tue, 3 Mar 2015 11:15:34 +0800 Subject: [PATCH 014/204] docs: fix cpu.shares part in man pages The original description has some mistakes and lack of many useful information, I rewrite them to make it accurate and complete. Signed-off-by: Qiang Huang --- docs/man/docker-run.1.md | 43 ++++++++++++++++++++++------------- docs/sources/reference/run.md | 43 ++++++++++++++++++++++------------- 2 files changed, 54 insertions(+), 32 deletions(-) diff --git a/docs/man/docker-run.1.md b/docs/man/docker-run.1.md index 7dd69841f1..b3f056effb 100644 --- a/docs/man/docker-run.1.md +++ b/docs/man/docker-run.1.md @@ -82,24 +82,35 @@ option can be set multiple times. **-c**, **--cpu-shares**=0 CPU shares (relative weight) - You can increase the priority of a container -with the -c option. By default, all containers run at the same priority and get -the same proportion of CPU cycles, but you can tell the kernel to give more -shares of CPU time to one or more containers when you start them via **docker -run**. + You can modify the priority of a container +with the `-c` option. By default, all containers run at the same priority +and get the same proportion of CPU cycles, the value specified must be 2 +or higher, if you are not setting `-c` or `--cpu-shares`, the default +shares of CPU time would be 1024. -The flag `-c` or `--cpu-shares` with value 0 indicates that the running -container has access to all 1024 (default) CPU shares. However, this value -can be modified to run a container with a different priority or different -proportion of CPU cycles. +CPU shares is kind of CPU bandwidth weight, the proportion will only +reflect when CPU-intensive processes are running. When tasks in one +container are idle, other containers are allowed to borrow the left-over +CPU time. -E.g., If we start three {C0, C1, C2} containers with default values -(`-c` OR `--cpu-shares` = 0) and one {C3} with (`-c` or `--cpu-shares`=512) -then C0, C1, and C2 would have access to 100% CPU shares (1024) and C3 would -only have access to 50% CPU shares (512). In the context of a time-sliced OS -with time quantum set as 100 milliseconds, containers C0, C1, and C2 will run -for full-time quantum, and container C3 will run for half-time quantum i.e 50 -milliseconds. +The actual amount of CPU time can very depending on the number of containers +running on the system. If a container have a share of 1024 and two other +containers have share of 512, when processes in all containers attempt to +use 100% of CPU, the first container would receive 50% of all CPU time, if +another container with share of 1024 is added, the first container would +only get 33% of the CPU (the rest receive 16.5%, 16.5% and 33% of CPU). + +Note that shares of CPU time are distributed per all CPU cores on multi-core +systems. Even if a container is limited to less than 100% of CPU time, it +may use 100% of each individual CPU core. E.g., if we start {C0} container +with (`-c` = 512) and {C1} with (`-c` = 1024), we start three CPU-intensive +processes (one in {C0} and two in {C1}) on a system with more than three +cores, might results in the following division of CPU shares: + +PID container CPU CPU share +100 {C0} 0 100% of CPU0 +101 {C1} 1 100% of CPU1 +102 {C1} 2 100% of CPU2 **--cap-add**=[] Add Linux capabilities diff --git a/docs/sources/reference/run.md b/docs/sources/reference/run.md index 8faf9ad77c..a9c2181c0f 100644 --- a/docs/sources/reference/run.md +++ b/docs/sources/reference/run.md @@ -329,24 +329,35 @@ We have four ways to set memory usage: It is not allowed to use more than L bytes of memory, swap *plus* memory usage is limited by S. -The operator can increase the priority of this container with -the `-c` option. By default, all containers run at the same priority and -get the same proportion of CPU cycles, but you can tell the kernel to -give more shares of CPU time to one or more containers when you start -them via Docker. +The operator can modify the priority of this container with +the `-c` option. By default, all containers run at the same priority +and get the same proportion of CPU cycles, the value specified must be 2 +or higher, if you are not setting `-c` or `--cpu-shares`, the default +shares of CPU time would be 1024. -The flag `-c` or `--cpu-shares` with value 0 indicates that the running -container has access to all 1024 (default) CPU shares. However, this value -can be modified to run a container with a different priority or different -proportion of CPU cycles. +CPU shares is kind of CPU bandwidth weight, the proportion will only +reflect when CPU-intensive processes are running. When tasks in one +container are idle, other containers are allowed to borrow the left-over +CPU time. -E.g., If we start three {C0, C1, C2} containers with default values -(`-c` OR `--cpu-shares` = 0) and one {C3} with (`-c` or `--cpu-shares`=512) -then C0, C1, and C2 would have access to 100% CPU shares (1024) and C3 would -only have access to 50% CPU shares (512). In the context of a time-sliced OS -with time quantum set as 100 milliseconds, containers C0, C1, and C2 will run -for full-time quantum, and container C3 will run for half-time quantum i.e 50 -milliseconds. +The actual amount of CPU time can very depending on the number of containers +running on the system. If a container have a share of 1024 and two other +containers have share of 512, when processes in all containers attempt to +use 100% of CPU, the first container would receive 50% of all CPU time, if +another container with share of 1024 is added, the first container would +only get 33% of the CPU (the rest receive 16.5%, 16.5% and 33% of CPU). + +Note that shares of CPU time are distributed per all CPU cores on multi-core +systems. Even if a container is limited to less than 100% of CPU time, it +may use 100% of each individual CPU core. E.g., if we start {C0} container +with (`-c` = 512) and {C1} with (`-c` = 1024), we start three CPU-intensive +processes (one in {C0} and two in {C1}) on a system with more than three +cores, might results in the following division of CPU shares: + +PID container CPU CPU share +100 {C0} 0 100% of CPU0 +101 {C1} 1 100% of CPU1 +102 {C1} 2 100% of CPU2 ## Runtime privilege, Linux capabilities, and LXC configuration From 26e5a9d76a802662bf287ce9de1f98988749636a Mon Sep 17 00:00:00 2001 From: Sven Dowideit Date: Tue, 3 Mar 2015 18:17:46 -0800 Subject: [PATCH 015/204] fix to cpu.shares documentation by @hqhq Signed-off-by: Sven Dowideit --- docs/man/docker-run.1.md | 53 ++++++++++---------- docs/sources/reference/commandline/cli.md | 3 +- docs/sources/reference/run.md | 59 +++++++++++++---------- 3 files changed, 63 insertions(+), 52 deletions(-) diff --git a/docs/man/docker-run.1.md b/docs/man/docker-run.1.md index b3f056effb..9262b91e39 100644 --- a/docs/man/docker-run.1.md +++ b/docs/man/docker-run.1.md @@ -82,35 +82,38 @@ option can be set multiple times. **-c**, **--cpu-shares**=0 CPU shares (relative weight) - You can modify the priority of a container -with the `-c` option. By default, all containers run at the same priority -and get the same proportion of CPU cycles, the value specified must be 2 -or higher, if you are not setting `-c` or `--cpu-shares`, the default -shares of CPU time would be 1024. + By default, all containers get the same proportion of CPU cycles. This proportion +can be modified by changing the container's CPU share weighting relative +to the weighting of all other running containers. -CPU shares is kind of CPU bandwidth weight, the proportion will only -reflect when CPU-intensive processes are running. When tasks in one -container are idle, other containers are allowed to borrow the left-over -CPU time. +To modify the proportion from the default of 1024, use the **-c** or **--cpu-shares** +flag to set the weighting to 2 or higher. -The actual amount of CPU time can very depending on the number of containers -running on the system. If a container have a share of 1024 and two other -containers have share of 512, when processes in all containers attempt to -use 100% of CPU, the first container would receive 50% of all CPU time, if -another container with share of 1024 is added, the first container would -only get 33% of the CPU (the rest receive 16.5%, 16.5% and 33% of CPU). +The proportion will only apply when CPU-intensive processes are running. +When tasks in one container are idle, other containers can use the +left-over CPU time. The actual amount of CPU time will vary depending on +the number of containers running on the system. -Note that shares of CPU time are distributed per all CPU cores on multi-core -systems. Even if a container is limited to less than 100% of CPU time, it -may use 100% of each individual CPU core. E.g., if we start {C0} container -with (`-c` = 512) and {C1} with (`-c` = 1024), we start three CPU-intensive -processes (one in {C0} and two in {C1}) on a system with more than three -cores, might results in the following division of CPU shares: +For example, consider three containers, one has a cpu-share of 1024 and +two others have a cpu-share setting of 512. When processes in all three +containers attempt to use 100% of CPU, the first container would receive +50% of the total CPU time. If you add a fouth container with a cpu-share +of 1024, the first container only gets 33% of the CPU. The remaining containers +receive 16.5%, 16.5% and 33% of the CPU. -PID container CPU CPU share -100 {C0} 0 100% of CPU0 -101 {C1} 1 100% of CPU1 -102 {C1} 2 100% of CPU2 +On a multi-core system, the shares of CPU time are distributed over all CPU +cores. Even if a container is limited to less than 100% of CPU time, it can +use 100% of each individual CPU core. + +For example, consider a system with more than three cores. If you start one +container **{C0}** with **-c=512** running one process, and another container +**{C1}** with **-c=1024** running two processes, this can result in the following +division of CPU shares: + + PID container CPU CPU share + 100 {C0} 0 100% of CPU0 + 101 {C1} 1 100% of CPU1 + 102 {C1} 2 100% of CPU2 **--cap-add**=[] Add Linux capabilities diff --git a/docs/sources/reference/commandline/cli.md b/docs/sources/reference/commandline/cli.md index eb61872dae..9e026d086c 100644 --- a/docs/sources/reference/commandline/cli.md +++ b/docs/sources/reference/commandline/cli.md @@ -817,7 +817,8 @@ container at any point. This is useful when you want to set up a container configuration ahead of time so that it is ready to start when you need it. -Please see the [run command](#run) section for more details. +Please see the [run command](#run) section and the [Docker run reference]( +/reference/run/) for more details. #### Examples diff --git a/docs/sources/reference/run.md b/docs/sources/reference/run.md index a9c2181c0f..9108a86ad1 100644 --- a/docs/sources/reference/run.md +++ b/docs/sources/reference/run.md @@ -311,7 +311,9 @@ container: -m="": Memory limit (format: , where unit = b, k, m or g) -memory-swap="": Total memory limit (memory + swap, format: , where unit = b, k, m or g) - -c=0 : CPU shares (relative weight) + -c, --cpu-shares=0 CPU shares (relative weight) + +### Memory constraints We have four ways to set memory usage: - memory=inf, memory-swap=inf (not specify any of them) @@ -329,35 +331,40 @@ We have four ways to set memory usage: It is not allowed to use more than L bytes of memory, swap *plus* memory usage is limited by S. -The operator can modify the priority of this container with -the `-c` option. By default, all containers run at the same priority -and get the same proportion of CPU cycles, the value specified must be 2 -or higher, if you are not setting `-c` or `--cpu-shares`, the default -shares of CPU time would be 1024. +### CPU share constraint -CPU shares is kind of CPU bandwidth weight, the proportion will only -reflect when CPU-intensive processes are running. When tasks in one -container are idle, other containers are allowed to borrow the left-over -CPU time. +By default, all containers get the same proportion of CPU cycles. This proportion +can be modified by changing the container's CPU share weighting relative +to the weighting of all other running containers. -The actual amount of CPU time can very depending on the number of containers -running on the system. If a container have a share of 1024 and two other -containers have share of 512, when processes in all containers attempt to -use 100% of CPU, the first container would receive 50% of all CPU time, if -another container with share of 1024 is added, the first container would -only get 33% of the CPU (the rest receive 16.5%, 16.5% and 33% of CPU). +To modify the proportion from the default of 1024, use the `-c` or `--cpu-shares` +flag to set the weighting to 2 or higher. -Note that shares of CPU time are distributed per all CPU cores on multi-core -systems. Even if a container is limited to less than 100% of CPU time, it -may use 100% of each individual CPU core. E.g., if we start {C0} container -with (`-c` = 512) and {C1} with (`-c` = 1024), we start three CPU-intensive -processes (one in {C0} and two in {C1}) on a system with more than three -cores, might results in the following division of CPU shares: +The proportion will only apply when CPU-intensive processes are running. +When tasks in one container are idle, other containers can use the +left-over CPU time. The actual amount of CPU time will vary depending on +the number of containers running on the system. -PID container CPU CPU share -100 {C0} 0 100% of CPU0 -101 {C1} 1 100% of CPU1 -102 {C1} 2 100% of CPU2 +For example, consider three containers, one has a cpu-share of 1024 and +two others have a cpu-share setting of 512. When processes in all three +containers attempt to use 100% of CPU, the first container would receive +50% of the total CPU time. If you add a fouth container with a cpu-share +of 1024, the first container only gets 33% of the CPU. The remaining containers +receive 16.5%, 16.5% and 33% of the CPU. + +On a multi-core system, the shares of CPU time are distributed over all CPU +cores. Even if a container is limited to less than 100% of CPU time, it can +use 100% of each individual CPU core. + +For example, consider a system with more than three cores. If you start one +container `{C0}` with `-c=512` running one process, and another container +`{C1}` with `-c=1024` running two processes, this can result in the following +division of CPU shares: + + PID container CPU CPU share + 100 {C0} 0 100% of CPU0 + 101 {C1} 1 100% of CPU1 + 102 {C1} 2 100% of CPU2 ## Runtime privilege, Linux capabilities, and LXC configuration From 5c05b5c992417cd7c6650b7dc785a06ca51ae228 Mon Sep 17 00:00:00 2001 From: Doug Davis Date: Wed, 4 Mar 2015 09:14:58 -0800 Subject: [PATCH 016/204] Add an HTTP Last-Modified header testcase Make sure ADD uses the Last-Modified as the mtime of the file. Signed-off-by: Doug Davis --- integration-cli/docker_cli_build_test.go | 67 ++++++++++++++++++++++++ 1 file changed, 67 insertions(+) diff --git a/integration-cli/docker_cli_build_test.go b/integration-cli/docker_cli_build_test.go index ac1ac14775..3b1730a462 100644 --- a/integration-cli/docker_cli_build_test.go +++ b/integration-cli/docker_cli_build_test.go @@ -669,6 +669,73 @@ func TestBuildCacheADD(t *testing.T) { logDone("build - build two images with remote ADD") } +func TestBuildLastModified(t *testing.T) { + name := "testbuildlastmodified" + defer deleteImages(name) + + server, err := fakeStorage(map[string]string{ + "file": "hello", + }) + if err != nil { + t.Fatal(err) + } + defer server.Close() + + var out, out2 string + + dFmt := `FROM busybox +ADD %s/file / +RUN ls -le /file` + + dockerfile := fmt.Sprintf(dFmt, server.URL) + + if _, out, err = buildImageWithOut(name, dockerfile, false); err != nil { + t.Fatal(err) + } + + originMTime := regexp.MustCompile(`root.*/file.*\n`).FindString(out) + // Make sure our regexp is correct + if strings.Index(originMTime, "/file") < 0 { + t.Fatalf("Missing ls info on 'file':\n%s", out) + } + + // Build it again and make sure the mtime of the file didn't change. + // Wait a few seconds to make sure the time changed enough to notice + time.Sleep(2 * time.Second) + + if _, out2, err = buildImageWithOut(name, dockerfile, false); err != nil { + t.Fatal(err) + } + + newMTime := regexp.MustCompile(`root.*/file.*\n`).FindString(out2) + if newMTime != originMTime { + t.Fatalf("MTime changed:\nOrigin:%s\nNew:%s", originMTime, newMTime) + } + + // Now 'touch' the file and make sure the timestamp DID change this time + // Create a new fakeStorage instead of just using Add() to help windows + server, err = fakeStorage(map[string]string{ + "file": "hello", + }) + if err != nil { + t.Fatal(err) + } + defer server.Close() + + dockerfile = fmt.Sprintf(dFmt, server.URL) + + if _, out2, err = buildImageWithOut(name, dockerfile, false); err != nil { + t.Fatal(err) + } + + newMTime = regexp.MustCompile(`root.*/file.*\n`).FindString(out2) + if newMTime == originMTime { + t.Fatalf("MTime didn't change:\nOrigin:%s\nNew:%s", originMTime, newMTime) + } + + logDone("build - use Last-Modified header") +} + func TestBuildSixtySteps(t *testing.T) { name := "foobuildsixtysteps" defer deleteImages(name) From 1a22418f9f1573ca8521831d52c8e0562cb3ef8f Mon Sep 17 00:00:00 2001 From: Ahmet Alp Balkan Date: Tue, 3 Mar 2015 18:40:16 -0800 Subject: [PATCH 017/204] pkg/archive: adjust chmod bits on windows This change modifies the chmod bits of build context archives built on windows to preserve the execute bit and remove the r/w bits from grp/others. Also adjusted integ-cli tests to verify permissions based on the platform the tests are running. Fixes #11047. Signed-off-by: Ahmet Alp Balkan --- integration-cli/docker_cli_build_test.go | 24 ++++++++++++------------ integration-cli/test_vars_unix.go | 2 ++ integration-cli/test_vars_windows.go | 3 +++ pkg/archive/archive.go | 3 +++ pkg/archive/archive_unix.go | 8 ++++++++ pkg/archive/archive_unix_test.go | 18 ++++++++++++++++++ pkg/archive/archive_windows.go | 12 ++++++++++++ pkg/archive/archive_windows_test.go | 18 ++++++++++++++++++ 8 files changed, 76 insertions(+), 12 deletions(-) diff --git a/integration-cli/docker_cli_build_test.go b/integration-cli/docker_cli_build_test.go index 00183c0671..cd653ddcef 100644 --- a/integration-cli/docker_cli_build_test.go +++ b/integration-cli/docker_cli_build_test.go @@ -690,15 +690,15 @@ func TestBuildSixtySteps(t *testing.T) { func TestBuildAddSingleFileToRoot(t *testing.T) { name := "testaddimg" defer deleteImages(name) - ctx, err := fakeContext(`FROM busybox + ctx, err := fakeContext(fmt.Sprintf(`FROM busybox RUN echo 'dockerio:x:1001:1001::/bin:/bin/false' >> /etc/passwd RUN echo 'dockerio:x:1001:' >> /etc/group RUN touch /exists RUN chown dockerio.dockerio /exists ADD test_file / RUN [ $(ls -l /test_file | awk '{print $3":"$4}') = 'root:root' ] -RUN [ $(ls -l /test_file | awk '{print $1}') = '-rw-r--r--' ] -RUN [ $(ls -l /exists | awk '{print $3":"$4}') = 'dockerio:dockerio' ]`, +RUN [ $(ls -l /test_file | awk '{print $1}') = '%s' ] +RUN [ $(ls -l /exists | awk '{print $3":"$4}') = 'dockerio:dockerio' ]`, expectedFileChmod), map[string]string{ "test_file": "test1", }) @@ -1263,7 +1263,7 @@ RUN [ $(ls -l /exists/test_file | awk '{print $3":"$4}') = 'root:root' ]`, func TestBuildAddWholeDirToRoot(t *testing.T) { name := "testaddwholedirtoroot" defer deleteImages(name) - ctx, err := fakeContext(`FROM busybox + ctx, err := fakeContext(fmt.Sprintf(`FROM busybox RUN echo 'dockerio:x:1001:1001::/bin:/bin/false' >> /etc/passwd RUN echo 'dockerio:x:1001:' >> /etc/group RUN touch /exists @@ -1272,8 +1272,8 @@ ADD test_dir /test_dir RUN [ $(ls -l / | grep test_dir | awk '{print $3":"$4}') = 'root:root' ] RUN [ $(ls -l / | grep test_dir | awk '{print $1}') = 'drwxr-xr-x' ] RUN [ $(ls -l /test_dir/test_file | awk '{print $3":"$4}') = 'root:root' ] -RUN [ $(ls -l /test_dir/test_file | awk '{print $1}') = '-rw-r--r--' ] -RUN [ $(ls -l /exists | awk '{print $3":"$4}') = 'dockerio:dockerio' ]`, +RUN [ $(ls -l /test_dir/test_file | awk '{print $1}') = '%s' ] +RUN [ $(ls -l /exists | awk '{print $3":"$4}') = 'dockerio:dockerio' ]`, expectedFileChmod), map[string]string{ "test_dir/test_file": "test1", }) @@ -1336,15 +1336,15 @@ RUN [ $(ls -l /usr/bin/suidbin | awk '{print $1}') = '-rwsr-xr-x' ]`, func TestBuildCopySingleFileToRoot(t *testing.T) { name := "testcopysinglefiletoroot" defer deleteImages(name) - ctx, err := fakeContext(`FROM busybox + ctx, err := fakeContext(fmt.Sprintf(`FROM busybox RUN echo 'dockerio:x:1001:1001::/bin:/bin/false' >> /etc/passwd RUN echo 'dockerio:x:1001:' >> /etc/group RUN touch /exists RUN chown dockerio.dockerio /exists COPY test_file / RUN [ $(ls -l /test_file | awk '{print $3":"$4}') = 'root:root' ] -RUN [ $(ls -l /test_file | awk '{print $1}') = '-rw-r--r--' ] -RUN [ $(ls -l /exists | awk '{print $3":"$4}') = 'dockerio:dockerio' ]`, +RUN [ $(ls -l /test_file | awk '{print $1}') = '%s' ] +RUN [ $(ls -l /exists | awk '{print $3":"$4}') = 'dockerio:dockerio' ]`, expectedFileChmod), map[string]string{ "test_file": "test1", }) @@ -1496,7 +1496,7 @@ RUN [ $(ls -l /exists/test_file | awk '{print $3":"$4}') = 'root:root' ]`, func TestBuildCopyWholeDirToRoot(t *testing.T) { name := "testcopywholedirtoroot" defer deleteImages(name) - ctx, err := fakeContext(`FROM busybox + ctx, err := fakeContext(fmt.Sprintf(`FROM busybox RUN echo 'dockerio:x:1001:1001::/bin:/bin/false' >> /etc/passwd RUN echo 'dockerio:x:1001:' >> /etc/group RUN touch /exists @@ -1505,8 +1505,8 @@ COPY test_dir /test_dir RUN [ $(ls -l / | grep test_dir | awk '{print $3":"$4}') = 'root:root' ] RUN [ $(ls -l / | grep test_dir | awk '{print $1}') = 'drwxr-xr-x' ] RUN [ $(ls -l /test_dir/test_file | awk '{print $3":"$4}') = 'root:root' ] -RUN [ $(ls -l /test_dir/test_file | awk '{print $1}') = '-rw-r--r--' ] -RUN [ $(ls -l /exists | awk '{print $3":"$4}') = 'dockerio:dockerio' ]`, +RUN [ $(ls -l /test_dir/test_file | awk '{print $1}') = '%s' ] +RUN [ $(ls -l /exists | awk '{print $3":"$4}') = 'dockerio:dockerio' ]`, expectedFileChmod), map[string]string{ "test_dir/test_file": "test1", }) diff --git a/integration-cli/test_vars_unix.go b/integration-cli/test_vars_unix.go index 988d3c4721..1ab8a5ca48 100644 --- a/integration-cli/test_vars_unix.go +++ b/integration-cli/test_vars_unix.go @@ -5,4 +5,6 @@ package main const ( // identifies if test suite is running on a unix platform isUnixCli = true + + expectedFileChmod = "-rw-r--r--" ) diff --git a/integration-cli/test_vars_windows.go b/integration-cli/test_vars_windows.go index f9ad163981..3cad4bceef 100644 --- a/integration-cli/test_vars_windows.go +++ b/integration-cli/test_vars_windows.go @@ -5,4 +5,7 @@ package main const ( // identifies if test suite is running on a unix platform isUnixCli = false + + // this is the expected file permission set on windows: gh#11047 + expectedFileChmod = "-rwx------" ) diff --git a/pkg/archive/archive.go b/pkg/archive/archive.go index bce66a505a..bfa6e18462 100644 --- a/pkg/archive/archive.go +++ b/pkg/archive/archive.go @@ -204,6 +204,7 @@ func (ta *tarAppender) addTarFile(path, name string) error { if err != nil { return err } + hdr.Mode = int64(chmodTarEntry(os.FileMode(hdr.Mode))) name, err = canonicalTarName(name, fi.IsDir()) if err != nil { @@ -696,6 +697,8 @@ func (archiver *Archiver) CopyFileWithTar(src, dst string) (err error) { return err } hdr.Name = filepath.Base(dst) + hdr.Mode = int64(chmodTarEntry(os.FileMode(hdr.Mode))) + tw := tar.NewWriter(w) defer tw.Close() if err := tw.WriteHeader(hdr); err != nil { diff --git a/pkg/archive/archive_unix.go b/pkg/archive/archive_unix.go index 8c7079f85a..cbce65e31d 100644 --- a/pkg/archive/archive_unix.go +++ b/pkg/archive/archive_unix.go @@ -4,6 +4,7 @@ package archive import ( "errors" + "os" "syscall" "github.com/docker/docker/vendor/src/code.google.com/p/go/src/pkg/archive/tar" @@ -16,6 +17,13 @@ func CanonicalTarNameForPath(p string) (string, error) { return p, nil // already unix-style } +// chmodTarEntry is used to adjust the file permissions used in tar header based +// on the platform the archival is done. + +func chmodTarEntry(perm os.FileMode) os.FileMode { + return perm // noop for unix as golang APIs provide perm bits correctly +} + func setHeaderForSpecialDevice(hdr *tar.Header, ta *tarAppender, name string, stat interface{}) (nlink uint32, inode uint64, err error) { s, ok := stat.(*syscall.Stat_t) diff --git a/pkg/archive/archive_unix_test.go b/pkg/archive/archive_unix_test.go index 52f28e20f0..18f45c480f 100644 --- a/pkg/archive/archive_unix_test.go +++ b/pkg/archive/archive_unix_test.go @@ -3,6 +3,7 @@ package archive import ( + "os" "testing" ) @@ -40,3 +41,20 @@ func TestCanonicalTarName(t *testing.T) { } } } + +func TestChmodTarEntry(t *testing.T) { + cases := []struct { + in, expected os.FileMode + }{ + {0000, 0000}, + {0777, 0777}, + {0644, 0644}, + {0755, 0755}, + {0444, 0444}, + } + for _, v := range cases { + if out := chmodTarEntry(v.in); out != v.expected { + t.Fatalf("wrong chmod. expected:%v got:%v", v.expected, out) + } + } +} diff --git a/pkg/archive/archive_windows.go b/pkg/archive/archive_windows.go index b95aa178d4..2904b38319 100644 --- a/pkg/archive/archive_windows.go +++ b/pkg/archive/archive_windows.go @@ -4,6 +4,7 @@ package archive import ( "fmt" + "os" "strings" "github.com/docker/docker/vendor/src/code.google.com/p/go/src/pkg/archive/tar" @@ -23,6 +24,17 @@ func CanonicalTarNameForPath(p string) (string, error) { return strings.Replace(p, "\\", "/", -1), nil } +// chmodTarEntry is used to adjust the file permissions used in tar header based +// on the platform the archival is done. +func chmodTarEntry(perm os.FileMode) os.FileMode { + // Clear r/w on grp/others: no precise equivalen of group/others on NTFS. + perm &= 0711 + // Add the x bit: make everything +x from windows + perm |= 0100 + + return perm +} + func setHeaderForSpecialDevice(hdr *tar.Header, ta *tarAppender, name string, stat interface{}) (nlink uint32, inode uint64, err error) { // do nothing. no notion of Rdev, Inode, Nlink in stat on Windows return diff --git a/pkg/archive/archive_windows_test.go b/pkg/archive/archive_windows_test.go index 2b7993c293..821a76a862 100644 --- a/pkg/archive/archive_windows_test.go +++ b/pkg/archive/archive_windows_test.go @@ -3,6 +3,7 @@ package archive import ( + "os" "testing" ) @@ -46,3 +47,20 @@ func TestCanonicalTarName(t *testing.T) { } } } + +func TestChmodTarEntry(t *testing.T) { + cases := []struct { + in, expected os.FileMode + }{ + {0000, 0100}, + {0777, 0711}, + {0644, 0700}, + {0755, 0711}, + {0444, 0500}, + } + for _, v := range cases { + if out := chmodTarEntry(v.in); out != v.expected { + t.Fatalf("wrong chmod. expected:%v got:%v", v.expected, out) + } + } +} From 8833779ae0384f3f9a995f94e42478451107a5ed Mon Sep 17 00:00:00 2001 From: Sebastiaan van Stijn Date: Sun, 1 Mar 2015 21:13:33 +0100 Subject: [PATCH 018/204] Docs: add restart-policies to 'Run reference' This moves some information on restart-policies from the "command line" page to "run reference". Also fixes some minor typos and adds a "NOTE" about --rm and --restart not allowed to be combined. Also removes inline CSS styles from tables, which will be styled by the stylesheet, and fixes some minor MarkDown errors (`<` -> <) depends on https://github.com/docker/docs-base/pull/1 resolves #11069 Signed-off-by: Sebastiaan van Stijn --- docs/sources/reference/commandline/cli.md | 75 +++++++----- docs/sources/reference/run.md | 132 ++++++++++++++++++---- 2 files changed, 157 insertions(+), 50 deletions(-) diff --git a/docs/sources/reference/commandline/cli.md b/docs/sources/reference/commandline/cli.md index f480f89cf3..0985bbb33e 100644 --- a/docs/sources/reference/commandline/cli.md +++ b/docs/sources/reference/commandline/cli.md @@ -1914,41 +1914,56 @@ application change: #### Restart Policies -Using the `--restart` flag on Docker run you can specify a restart policy for -how a container should or should not be restarted on exit. +Use Docker's `--restart` to specify a container's *restart policy*. A restart +policy controls whether the Docker daemon restarts a container after exit. +Docker supports the following restart policies: -An ever increasing delay (double the previous delay, starting at 100 milliseconds) -is added before each restart to prevent flooding the server. This means the daemaon -will wait for 100 mS, then 200 mS, 400, 800, 1600, and so on until either the -`on-failure` limit is hit, or when you `docker stop` or even `docker rm -f` -the container. - -When a restart policy is active on a container, it will be shown in `docker ps` -as either `Up` or `Restarting` in `docker ps`. It can also be useful to use -`docker events` to see the restart policy in effect. - -** no ** - Do not restart the container when it exits. - -** on-failure ** - Restart the container only if it exits with a non zero exit status. - -** always ** - Always restart the container regardless of the exit status. - -You can also specify the maximum amount of times Docker will try to -restart the container when using the ** on-failure ** policy. The -default is that Docker will try forever to restart the container. + + + + + + + + + + + + + + + + + + + + + +
PolicyResult
no + Do not automatically restart the container when it exits. This is the + default. +
+ + on-failure[:max-retries] + + + Restart only if the container exits with a non-zero exit status. + Optionally, limit the number of restart retries the Docker + daemon attempts. +
always + Always restart the container regardless of the exit status. + When you specify always, the Docker daemon will try to restart + the container indefinitely. +
$ sudo docker run --restart=always redis -This will run the `redis` container with a restart policy of ** always ** so that if -the container exits, Docker will restart it. +This will run the `redis` container with a restart policy of **always** +so that if the container exits, Docker will restart it. - $ sudo docker run --restart=on-failure:10 redis - -This will run the `redis` container with a restart policy of ** -on-failure ** and a maximum restart count of 10. If the `redis` -container exits with a non-zero exit status more than 10 times in a row -Docker will abort trying to restart the container. Providing a maximum -restart limit is only valid for the ** on-failure ** policy. +More detailed information on restart policies can be found in the +[Restart Policies (--restart)](/reference/run/#restart-policies-restart) section +of the Docker run reference page. ### Adding entries to a container hosts file diff --git a/docs/sources/reference/run.md b/docs/sources/reference/run.md index 8faf9ad77c..268ade05ee 100644 --- a/docs/sources/reference/run.md +++ b/docs/sources/reference/run.md @@ -52,6 +52,7 @@ following options. - [PID Equivalent](#pid-equivalent) - [IPC Settings](#ipc-settings) - [Network Settings](#network-settings) + - [Restart Policies
(--restart)](#restart-policies-restart) - [Clean Up (--rm)](#clean-up-rm) - [Runtime Constraints on CPU and Memory](#runtime-constraints-on-cpu-and-memory) - [Runtime Privilege, Linux Capabilities, and LXC Configuration](#runtime-privilege-linux-capabilities-and-lxc-configuration) @@ -256,6 +257,99 @@ container itself as well as `localhost` and a few other common things. The ::1 localhost ip6-localhost ip6-loopback 86.75.30.9 db-static +## Restart policies (--restart) + +Using the `--restart` flag on Docker run you can specify a restart policy for +how a container should or should not be restarted on exit. + +When a restart policy is active on a container, it will be shown as either `Up` +or `Restarting` in [`docker ps`](/reference/commandline/cli/#ps). It can also be +useful to use [`docker events`](/reference/commandline/cli/#events) to see the +restart policy in effect. + +Docker supports the following restart policies: + + + + + + + + + + + + + + + + + + + + + + +
PolicyResult
no + Do not automatically restart the container when it exits. This is the + default. +
+ + on-failure[:max-retries] + + + Restart only if the container exits with a non-zero exit status. + Optionally, limit the number of restart retries the Docker + daemon attempts. +
always + Always restart the container regardless of the exit status. + When you specify always, the Docker daemon will try to restart + the container indefinitely. +
+ +An ever increasing delay (double the previous delay, starting at 100 +milliseconds) is added before each restart to prevent flooding the server. +This means the daemon will wait for 100 ms, then 200 ms, 400, 800, 1600, +and so on until either the `on-failure` limit is hit, or when you `docker stop` +or `docker rm -f` the container. + +If a container is succesfully restarted (the container is started and runs +for at least 10 seconds), the delay is reset to its default value of 100 ms. + +You can specify the maximum amount of times Docker will try to restart the +container when using the **on-failure** policy. The default is that Docker +will try forever to restart the container. The number of (attempted) restarts +for a container can be obtained via [`docker inspect`]( +/reference/commandline/cli/#inspect). For example, to get the number of restarts +for container "my-container"; + + $ sudo docker inspect -f "{{ .RestartCount }}" my-container + # 2 + +Or, to get the last time the container was (re)started; + + $ docker inspect -f "{{ .State.StartedAt }}" my-container + # 2015-03-04T23:47:07.691840179Z + +You cannot set any restart policy in combination with +["clean up (--rm)"](#clean-up-rm). Setting both `--restart` and `--rm` +results in an error. + +###Examples + + $ sudo docker run --restart=always redis + +This will run the `redis` container with a restart policy of **always** +so that if the container exits, Docker will restart it. + + $ sudo docker run --restart=on-failure:10 redis + +This will run the `redis` container with a restart policy of **on-failure** +and a maximum restart count of 10. If the `redis` container exits with a +non-zero exit status more than 10 times in a row Docker will abort trying to +restart the container. Providing a maximum restart limit is only valid for the +**on-failure** policy. + ## Clean up (--rm) By default a container's file system persists even after the container @@ -317,15 +411,15 @@ We have four ways to set memory usage: - memory=inf, memory-swap=inf (not specify any of them) There is no memory limit, you can use as much as you want. - - memory=L - - Variable - Value + + + + - - + - - - + + - - + - - - + + +
VariableValue
HOME + HOME Set based on the value of USER
HOSTNAME +
HOSTNAME The hostname associated with the container
PATH + PATH Includes popular directories, such as :
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
TERM - xterm if the container is allocated a psuedo-TTY -
TERMxterm if the container is allocated a psuedo-TTY
@@ -619,7 +711,7 @@ container running Redis: # The redis-name container exposed port 6379 $ sudo docker ps - CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES + CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 4241164edf6f $ dockerfiles/redis:latest /redis-stable/src/re 5 seconds ago Up 4 seconds 6379/tcp redis-name # Note that there are no public ports exposed since we didn᾿t use -p or -P From 26e85b0db1b896e98071f121fb14b2c6d905ed4f Mon Sep 17 00:00:00 2001 From: Chen Hanxiao Date: Wed, 4 Mar 2015 21:09:50 -0500 Subject: [PATCH 019/204] dispatchers: warn user if try to use EXPOSE ip:hostPort:containerPort We could use EXPOSE ip:hostPort:containerPort, but actually it did as EXPOSE ::containerPort commit 2275c833 already warned user on daemon side. This patch will print warning message on client side. Signed-off-by: Chen Hanxiao --- builder/dispatchers.go | 10 ++++++++- integration-cli/docker_cli_build_test.go | 27 ++++++++++++++++++++++++ 2 files changed, 36 insertions(+), 1 deletion(-) diff --git a/builder/dispatchers.go b/builder/dispatchers.go index b00268ed67..959042e5ee 100644 --- a/builder/dispatchers.go +++ b/builder/dispatchers.go @@ -339,11 +339,19 @@ func expose(b *Builder, args []string, attributes map[string]bool, original stri b.Config.ExposedPorts = make(nat.PortSet) } - ports, _, err := nat.ParsePortSpecs(append(portsTab, b.Config.PortSpecs...)) + ports, bindingMap, err := nat.ParsePortSpecs(append(portsTab, b.Config.PortSpecs...)) if err != nil { return err } + for _, bindings := range bindingMap { + if bindings[0].HostIp != "" || bindings[0].HostPort != "" { + fmt.Fprintf(b.ErrStream, " ---> Using Dockerfile's EXPOSE instruction"+ + " to map host ports to container ports (ip:hostPort:containerPort) is deprecated.\n"+ + " Please use -p to publish the ports.\n") + } + } + // instead of using ports directly, we build a list of ports and sort it so // the order is consistent. This prevents cache burst where map ordering // changes between builds diff --git a/integration-cli/docker_cli_build_test.go b/integration-cli/docker_cli_build_test.go index ac1ac14775..b718d347a6 100644 --- a/integration-cli/docker_cli_build_test.go +++ b/integration-cli/docker_cli_build_test.go @@ -2343,6 +2343,33 @@ func TestBuildExposeUpperCaseProto(t *testing.T) { logDone("build - expose port with upper case proto") } +func TestBuildExposeHostPort(t *testing.T) { + // start building docker file with ip:hostPort:containerPort + name := "testbuildexpose" + expected := "map[5678/tcp:map[]]" + defer deleteImages(name) + _, out, err := buildImageWithOut(name, + `FROM scratch + EXPOSE 192.168.1.2:2375:5678`, + true) + if err != nil { + t.Fatal(err) + } + + if !strings.Contains(out, "to map host ports to container ports (ip:hostPort:containerPort) is deprecated.") { + t.Fatal("Missing warning message") + } + + res, err := inspectField(name, "Config.ExposedPorts") + if err != nil { + t.Fatal(err) + } + if res != expected { + t.Fatalf("Exposed ports %s, expected %s", res, expected) + } + logDone("build - ignore exposing host's port") +} + func TestBuildEmptyEntrypointInheritance(t *testing.T) { name := "testbuildentrypointinheritance" name2 := "testbuildentrypointinheritance2" From 43804baa33958466e2f487ce0b96cba475116ab1 Mon Sep 17 00:00:00 2001 From: Harald Albers Date: Thu, 19 Feb 2015 13:43:09 +0100 Subject: [PATCH 020/204] Add bash completion for docker events --filter and --until. Signed-off-by: Harald Albers --- contrib/completion/bash/docker | 28 ++++++++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/contrib/completion/bash/docker b/contrib/completion/bash/docker index cd09e4ba62..15f334c2bd 100755 --- a/contrib/completion/bash/docker +++ b/contrib/completion/bash/docker @@ -334,14 +334,38 @@ _docker_diff() { _docker_events() { case "$prev" in - --since) + --filter|-f) + COMPREPLY=( $( compgen -S = -W "container event image" -- "$cur" ) ) + compopt -o nospace + return + ;; + --since|--until) + return + ;; + esac + + # "=" gets parsed to a word and assigned to either $cur or $prev depending on whether + # it is the last character or not. So we search for "xxx=" in the the last two words. + case "${words[$cword-2]}$prev=" in + *container=*) + cur="${cur#=}" + __docker_containers_all + return + ;; + *event=*) + COMPREPLY=( $( compgen -W "create destroy die export kill pause restart start stop unpause" -- "${cur#=}" ) ) + return + ;; + *image=*) + cur="${cur#=}" + __docker_image_repos_and_tags_and_ids return ;; esac case "$cur" in -*) - COMPREPLY=( $( compgen -W "--since" -- "$cur" ) ) + COMPREPLY=( $( compgen -W "--filter -f --since --until" -- "$cur" ) ) ;; esac } From f3d96e81e9c2a738ec577b814092f8953932de3a Mon Sep 17 00:00:00 2001 From: Doug Davis Date: Thu, 5 Mar 2015 08:39:32 -0800 Subject: [PATCH 021/204] Add support for 'docker cp' to write to stdout Closes #10805 Signed-off-by: Doug Davis --- api/client/commands.go | 11 +++++++-- docs/man/docker-cp.1.md | 9 ++++---- docs/man/docker.1.md | 2 +- docs/sources/reference/commandline/cli.md | 11 +++++---- integration-cli/docker_cli_cp_test.go | 28 +++++++++++++++++++++++ 5 files changed, 50 insertions(+), 11 deletions(-) diff --git a/api/client/commands.go b/api/client/commands.go index a4835bbeb3..291390a53e 100644 --- a/api/client/commands.go +++ b/api/client/commands.go @@ -2415,7 +2415,7 @@ func (cli *DockerCli) CmdRun(args ...string) error { } func (cli *DockerCli) CmdCp(args ...string) error { - cmd := cli.Subcmd("cp", "CONTAINER:PATH HOSTPATH", "Copy files/folders from the PATH to the HOSTPATH", true) + cmd := cli.Subcmd("cp", "CONTAINER:PATH HOSTPATH|-", "Copy files/folders from the PATH to the HOSTPATH. Use '-' to write the data\nas a tar file to STDOUT.", true) cmd.Require(flag.Exact, 2) utils.ParseFlags(cmd, args, true) @@ -2442,7 +2442,14 @@ func (cli *DockerCli) CmdCp(args ...string) error { } if statusCode == 200 { - if err := archive.Untar(stream, copyData.Get("HostPath"), &archive.TarOptions{NoLchown: true}); err != nil { + dest := copyData.Get("HostPath") + + if dest == "-" { + _, err = io.Copy(cli.out, stream) + } else { + err = archive.Untar(stream, dest, &archive.TarOptions{NoLchown: true}) + } + if err != nil { return err } } diff --git a/docs/man/docker-cp.1.md b/docs/man/docker-cp.1.md index ac49a47a54..71d57e35c3 100644 --- a/docs/man/docker-cp.1.md +++ b/docs/man/docker-cp.1.md @@ -2,16 +2,17 @@ % Docker Community % JUNE 2014 # NAME -docker-cp - Copy files/folders from the PATH to the HOSTPATH +docker-cp - Copy files/folders from the PATH to the HOSTPATH, or STDOUT # SYNOPSIS **docker cp** [**--help**] -CONTAINER:PATH HOSTPATH +CONTAINER:PATH HOSTPATH|- # DESCRIPTION -Copy files/folders from a container's filesystem to the host -path. Paths are relative to the root of the filesystem. Files +Copy files/folders from a container's filesystem to the +path. Use '-' to write the data as a tar file to STDOUT. +Paths are relative to the root of the filesystem. Files can be copied from a running or stopped container. # OPTIONS diff --git a/docs/man/docker.1.md b/docs/man/docker.1.md index 4a6d0cf152..1b5288bbb5 100644 --- a/docs/man/docker.1.md +++ b/docs/man/docker.1.md @@ -117,7 +117,7 @@ unix://[/path/to/socket] to use. Create a new image from a container's changes **docker-cp(1)** - Copy files/folders from a container's filesystem to the host at path + Copy files/folders from a container's filesystem to the host **docker-create(1)** Create a new container diff --git a/docs/sources/reference/commandline/cli.md b/docs/sources/reference/commandline/cli.md index eb61872dae..25a4be44bf 100644 --- a/docs/sources/reference/commandline/cli.md +++ b/docs/sources/reference/commandline/cli.md @@ -757,12 +757,15 @@ Supported `Dockerfile` instructions: `CMD`, `ENTRYPOINT`, `ENV`, `EXPOSE`, ## cp -Copy files/folders from a container's filesystem to the host -path. Paths are relative to the root of the filesystem. +Copy files/folders from a container's filesystem to the +path. Use '-' to write the data as a tar file to STDOUT. +Paths are relative to the root of the filesystem. - Usage: docker cp CONTAINER:PATH HOSTPATH + Usage: docker cp CONTAINER:PATH HOSTPATH|- + + Copy files/folders from the PATH to the HOSTPATH. Use '-' to write the data + as a tar file to STDOUT. - Copy files/folders from the PATH to the HOSTPATH ## create diff --git a/integration-cli/docker_cli_cp_test.go b/integration-cli/docker_cli_cp_test.go index ecda53526a..db5f363882 100644 --- a/integration-cli/docker_cli_cp_test.go +++ b/integration-cli/docker_cli_cp_test.go @@ -8,6 +8,7 @@ import ( "os/exec" "path" "path/filepath" + "strings" "testing" ) @@ -528,3 +529,30 @@ func TestCpToDot(t *testing.T) { } logDone("cp - to dot path") } + +func TestCpToStdout(t *testing.T) { + out, exitCode, err := dockerCmd(t, "run", "-d", "busybox", "/bin/sh", "-c", "echo lololol > /test") + if err != nil || exitCode != 0 { + t.Fatalf("failed to create a container:%s\n%s", out, err) + } + + cID := stripTrailingCharacters(out) + defer deleteContainer(cID) + + out, _, err = dockerCmd(t, "wait", cID) + if err != nil || stripTrailingCharacters(out) != "0" { + t.Fatalf("failed to set up container:%s\n%s", out, err) + } + + out, _, err = runCommandPipelineWithOutput( + exec.Command(dockerBinary, "cp", cID+":/test", "-"), + exec.Command("tar", "-vtf", "-")) + if err != nil { + t.Fatalf("Failed to run commands: %s", err) + } + + if !strings.Contains(out, "test") || !strings.Contains(out, "-rw") { + t.Fatalf("Missing file from tar TOC:\n%s", out) + } + logDone("cp - to stdout") +} From d046f56e8826f597ddd1752b9289d93b9054fa9d Mon Sep 17 00:00:00 2001 From: Ahmet Alp Balkan Date: Thu, 5 Mar 2015 14:42:52 -0800 Subject: [PATCH 022/204] integ-cli: skip case-sensitive dockerfile tests on windows The tests end up overwriting the `dockerfile` with `Dockerfile` since windows filesystems are case-insensitive. The following methods are skipped: - TestBuildRenamedDockerfile - TestBuildFromMixedcaseDockerfile Signed-off-by: Ahmet Alp Balkan --- integration-cli/docker_cli_build_test.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/integration-cli/docker_cli_build_test.go b/integration-cli/docker_cli_build_test.go index ac1ac14775..a79ea590ac 100644 --- a/integration-cli/docker_cli_build_test.go +++ b/integration-cli/docker_cli_build_test.go @@ -4700,6 +4700,7 @@ func TestBuildRenamedDockerfile(t *testing.T) { } func TestBuildFromMixedcaseDockerfile(t *testing.T) { + testRequires(t, UnixCli) // Dockerfile overwrites dockerfile on windows defer deleteImages("test1") ctx, err := fakeContext(`FROM busybox @@ -4725,6 +4726,7 @@ func TestBuildFromMixedcaseDockerfile(t *testing.T) { } func TestBuildWithTwoDockerfiles(t *testing.T) { + testRequires(t, UnixCli) // Dockerfile overwrites dockerfile on windows defer deleteImages("test1") ctx, err := fakeContext(`FROM busybox From 06a40f0f281b4679855a406bc202e122d2875949 Mon Sep 17 00:00:00 2001 From: Zhang Wentao Date: Tue, 3 Mar 2015 11:15:17 +0800 Subject: [PATCH 023/204] docker info display http/https_proxy setting Signed-off-by: Zhang Wentao --- api/client/commands.go | 10 +++++++++- daemon/info.go | 10 ++++++++++ docs/sources/reference/api/docker_remote_api.md | 6 ++++++ docs/sources/reference/api/docker_remote_api_v1.18.md | 3 +++ docs/sources/reference/commandline/cli.md | 3 +++ 5 files changed, 31 insertions(+), 1 deletion(-) diff --git a/api/client/commands.go b/api/client/commands.go index a4835bbeb3..1eeb2fdf59 100644 --- a/api/client/commands.go +++ b/api/client/commands.go @@ -590,7 +590,15 @@ func (cli *DockerCli) CmdInfo(args ...string) error { fmt.Fprintf(cli.out, "Docker Root Dir: %s\n", root) } } - + if remoteInfo.Exists("HttpProxy") { + fmt.Fprintf(cli.out, "Http Proxy: %s\n", remoteInfo.Get("HttpProxy")) + } + if remoteInfo.Exists("HttpsProxy") { + fmt.Fprintf(cli.out, "Https Proxy: %s\n", remoteInfo.Get("HttpsProxy")) + } + if remoteInfo.Exists("NoProxy") { + fmt.Fprintf(cli.out, "No Proxy: %s\n", remoteInfo.Get("NoProxy")) + } if len(remoteInfo.GetList("IndexServerAddress")) != 0 { cli.LoadConfigFile() u := cli.configFile.Configs[remoteInfo.Get("IndexServerAddress")].Username diff --git a/daemon/info.go b/daemon/info.go index f0fc1241b5..67ac048acf 100644 --- a/daemon/info.go +++ b/daemon/info.go @@ -87,6 +87,16 @@ func (daemon *Daemon) CmdInfo(job *engine.Job) engine.Status { v.SetInt("NCPU", runtime.NumCPU()) v.SetInt64("MemTotal", meminfo.MemTotal) v.Set("DockerRootDir", daemon.Config().Root) + if http_proxy := os.Getenv("http_proxy"); http_proxy != "" { + v.Set("HttpProxy", http_proxy) + } + if https_proxy := os.Getenv("https_proxy"); https_proxy != "" { + v.Set("HttpsProxy", https_proxy) + } + if no_proxy := os.Getenv("no_proxy"); no_proxy != "" { + v.Set("NoProxy", no_proxy) + } + if hostname, err := os.Hostname(); err == nil { v.SetJson("Name", hostname) } diff --git a/docs/sources/reference/api/docker_remote_api.md b/docs/sources/reference/api/docker_remote_api.md index 4f844f4549..7cfdb468bd 100644 --- a/docs/sources/reference/api/docker_remote_api.md +++ b/docs/sources/reference/api/docker_remote_api.md @@ -57,6 +57,12 @@ This endpoint now returns `Os`, `Arch` and `KernelVersion`. **New!** You can set ulimit settings to be used within the container. +`Get /info` + +**New!** +Add return value `HttpProxy`,`HttpsProxy` and `NoProxy` to this entrypoint. + + ## v1.17 ### Full Documentation diff --git a/docs/sources/reference/api/docker_remote_api_v1.18.md b/docs/sources/reference/api/docker_remote_api_v1.18.md index d5d39fb6f2..ea0176c184 100644 --- a/docs/sources/reference/api/docker_remote_api_v1.18.md +++ b/docs/sources/reference/api/docker_remote_api_v1.18.md @@ -1455,6 +1455,9 @@ Display system-wide information "IPv4Forwarding":true, "Labels":["storage=ssd"], "DockerRootDir": "/var/lib/docker", + "HttpProxy": "http://test:test@localhost:8080" + "HttpsProxy": "https://test:test@localhost:8080" + "NoProxy": "9.81.1.160" "OperatingSystem": "Boot2Docker", } diff --git a/docs/sources/reference/commandline/cli.md b/docs/sources/reference/commandline/cli.md index eb61872dae..8ffaa5adc3 100644 --- a/docs/sources/reference/commandline/cli.md +++ b/docs/sources/reference/commandline/cli.md @@ -1241,6 +1241,9 @@ For example: EventsListeners: 0 Init Path: /usr/bin/docker Docker Root Dir: /var/lib/docker + Http Proxy: http://test:test@localhost:8080 + Https Proxy: https://test:test@localhost:8080 + No Proxy: 9.81.1.160 Username: svendowideit Registry: [https://index.docker.io/v1/] Labels: From 8ba64b383dee8827c70f9c0a0d71aa61d0f0c595 Mon Sep 17 00:00:00 2001 From: Ahmet Alp Balkan Date: Thu, 5 Mar 2015 17:15:11 -0800 Subject: [PATCH 024/204] pkg/archive_windows: make use of os.PathSeparator cc: @jhowardmsft Signed-off-by: Ahmet Alp Balkan --- pkg/archive/archive_windows.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pkg/archive/archive_windows.go b/pkg/archive/archive_windows.go index b95aa178d4..926d480217 100644 --- a/pkg/archive/archive_windows.go +++ b/pkg/archive/archive_windows.go @@ -4,6 +4,7 @@ package archive import ( "fmt" + "os" "strings" "github.com/docker/docker/vendor/src/code.google.com/p/go/src/pkg/archive/tar" @@ -20,7 +21,8 @@ func CanonicalTarNameForPath(p string) (string, error) { if strings.Contains(p, "/") { return "", fmt.Errorf("windows path contains forward slash: %s", p) } - return strings.Replace(p, "\\", "/", -1), nil + return strings.Replace(p, string(os.PathSeparator), "/", -1), nil + } func setHeaderForSpecialDevice(hdr *tar.Header, ta *tarAppender, name string, stat interface{}) (nlink uint32, inode uint64, err error) { From 957bc8ac50ee4e2498143fca7848b4ccaab6147a Mon Sep 17 00:00:00 2001 From: Ahmet Alp Balkan Date: Thu, 5 Mar 2015 19:36:56 -0800 Subject: [PATCH 025/204] remote api: explain Kind (ChangeType) Signed-off-by: Ahmet Alp Balkan --- docs/sources/reference/api/docker_remote_api_v1.18.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/docs/sources/reference/api/docker_remote_api_v1.18.md b/docs/sources/reference/api/docker_remote_api_v1.18.md index d5d39fb6f2..d1675ac1ef 100644 --- a/docs/sources/reference/api/docker_remote_api_v1.18.md +++ b/docs/sources/reference/api/docker_remote_api_v1.18.md @@ -495,6 +495,12 @@ Inspect changes on container `id`'s filesystem } ] +Values for `Kind`: + +- `0`: Modify +- `1`: Add +- `2`: Delete + Status Codes: - **200** – no error From 00d3727106ef4d1280175a135b36b80c8bfc6a7b Mon Sep 17 00:00:00 2001 From: Lei Jitang Date: Fri, 6 Mar 2015 15:13:27 +0800 Subject: [PATCH 026/204] Fix docker build and docker run bash completion Signed-off-by: Lei Jitang --- contrib/completion/bash/docker | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/contrib/completion/bash/docker b/contrib/completion/bash/docker index cd09e4ba62..01140d8c7d 100755 --- a/contrib/completion/bash/docker +++ b/contrib/completion/bash/docker @@ -255,11 +255,15 @@ _docker_build() { __docker_image_repos_and_tags return ;; + --file|-f) + _filedir + return + ;; esac case "$cur" in -*) - COMPREPLY=( $( compgen -W "--force-rm --no-cache --quiet -q --rm --tag -t" -- "$cur" ) ) + COMPREPLY=( $( compgen -W "--file -f --force-rm --no-cache --quiet -q --rm --tag -t" -- "$cur" ) ) ;; *) local counter="$(__docker_pos_first_nonflag '--tag|-t')" @@ -623,8 +627,10 @@ _docker_run() { --lxc-conf --mac-address --memory -m + --memory-swap --name --net + --pid --publish -p --restart --security-opt @@ -635,9 +641,11 @@ _docker_run() { " local all_options="$options_with_args + --help --interactive -i --privileged --publish-all -P + --read-only --tty -t " From b3769f0ca6f70303e7fa4a3ec1cc47bab91de34b Mon Sep 17 00:00:00 2001 From: Dan Walsh Date: Fri, 6 Mar 2015 08:41:41 -0500 Subject: [PATCH 027/204] We had some testers who found a hard to diagnose bug in Dockerfile They used single-quotes (') in the exec-form of onbuild run command and things blew up. They asked to fix the man page to explain why. Docker-DCO-1.1-Signed-off-by: Dan Walsh (github: rhatdan) --- docs/man/Dockerfile.5.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/docs/man/Dockerfile.5.md b/docs/man/Dockerfile.5.md index d66bcad067..51e1390ceb 100644 --- a/docs/man/Dockerfile.5.md +++ b/docs/man/Dockerfile.5.md @@ -97,6 +97,9 @@ A Dockerfile is similar to a Makefile. exec form makes it possible to avoid shell string munging. The exec form makes it possible to **RUN** commands using a base image that does not contain `/bin/sh`. + Note that the exec form is parsed as a JSON array, which means that you must + use double-quotes (") around words not single-quotes ('). + **CMD** -- **CMD** has three forms: @@ -120,6 +123,9 @@ A Dockerfile is similar to a Makefile. be executed when running the image. If you use the shell form of the **CMD**, the `` executes in `/bin/sh -c`: + Note that the exec form is parsed as a JSON array, which means that you must + use double-quotes (") around words not single-quotes ('). + ``` FROM ubuntu CMD echo "This is a test." | wc - From 3a70f9d422e2d47dad0d8d5825832dd7f85f049a Mon Sep 17 00:00:00 2001 From: Ahmet Alp Balkan Date: Thu, 5 Mar 2015 22:01:49 -0800 Subject: [PATCH 028/204] integ-cli: fix test requiring scratch TestRunCidFileCleanupIfEmpty fails on windows/mac because the test runs the command `docker run scratch` and it gives the following error: Unable to find image 'scratch:latest' locally Pulling repository scratch 511136ea3c5a: Download complete FATA[0004] 'scratch' is a reserved name I am not entirely sure if this is a test issue or not but I had a quick workaround by creating another image using `FROM scratch` and using that. Signed-off-by: Ahmet Alp Balkan --- integration-cli/docker_cli_run_test.go | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/integration-cli/docker_cli_run_test.go b/integration-cli/docker_cli_run_test.go index 76ec09f16d..11520d29e5 100644 --- a/integration-cli/docker_cli_run_test.go +++ b/integration-cli/docker_cli_run_test.go @@ -2289,11 +2289,12 @@ func TestRunCidFileCleanupIfEmpty(t *testing.T) { } defer os.RemoveAll(tmpDir) tmpCidFile := path.Join(tmpDir, "cid") - cmd := exec.Command(dockerBinary, "run", "--cidfile", tmpCidFile, "scratch") + cmd := exec.Command(dockerBinary, "run", "--cidfile", tmpCidFile, "emptyfs") out, _, err := runCommandWithOutput(cmd) - t.Log(out) if err == nil { - t.Fatal("Run without command must fail") + t.Fatalf("Run without command must fail. out=%s", out) + } else if !strings.Contains(out, "No command specified") { + t.Fatalf("Run without command failed with wrong output. out=%s\nerr=%v", out, err) } if _, err := os.Stat(tmpCidFile); err == nil { From 518fdf972d874a9fd0e1af739ffc978c6b39e126 Mon Sep 17 00:00:00 2001 From: Lei Jitang Date: Fri, 6 Mar 2015 09:06:14 -0800 Subject: [PATCH 029/204] Fix start container failed hijack the stdin of terminal Signed-off-by: Lei Jitang --- api/client/commands.go | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/api/client/commands.go b/api/client/commands.go index 1eeb2fdf59..ccab770f58 100644 --- a/api/client/commands.go +++ b/api/client/commands.go @@ -712,6 +712,16 @@ func (cli *DockerCli) CmdStart(args ...string) error { utils.ParseFlags(cmd, args, true) hijacked := make(chan io.Closer) + // Block the return until the chan gets closed + defer func() { + log.Debugf("CmdStart() returned, defer waiting for hijack to finish.") + if _, ok := <-hijacked; ok { + log.Errorf("Hijack did not finish (chan still open)") + } + if *openStdin || *attach { + cli.in.Close() + } + }() if *attach || *openStdin { if cmd.NArg() > 1 { @@ -769,26 +779,19 @@ func (cli *DockerCli) CmdStart(args ...string) error { } } - var encounteredError error for _, name := range cmd.Args() { _, _, err := readBody(cli.call("POST", "/containers/"+name+"/start", nil, false)) if err != nil { if !*attach && !*openStdin { fmt.Fprintf(cli.err, "%s\n", err) } - encounteredError = fmt.Errorf("Error: failed to start one or more containers") + return fmt.Errorf("Error: failed to start one or more containers") } else { if !*attach && !*openStdin { fmt.Fprintf(cli.out, "%s\n", name) } } } - if encounteredError != nil { - if *openStdin || *attach { - cli.in.Close() - } - return encounteredError - } if *openStdin || *attach { if tty && cli.isTerminalOut { From 9be2ca2394112d8e988dc492f482cefd93a5b5fb Mon Sep 17 00:00:00 2001 From: Doug Davis Date: Fri, 6 Mar 2015 06:36:23 -0800 Subject: [PATCH 030/204] Tell users about how VOLUME initializes the new mount point/volume Signed-off-by: Doug Davis --- docs/man/docker-run.1.md | 22 +++++++++++--------- docs/sources/reference/builder.md | 20 +++++++++++++++--- docs/sources/userguide/dockervolumes.md | 27 ++++++++++++++++--------- 3 files changed, 47 insertions(+), 22 deletions(-) diff --git a/docs/man/docker-run.1.md b/docs/man/docker-run.1.md index 7dd69841f1..7e3875afcc 100644 --- a/docs/man/docker-run.1.md +++ b/docs/man/docker-run.1.md @@ -325,16 +325,20 @@ read-write. See examples. **--volumes-from**=[] Mount volumes from the specified container(s) - Will mount volumes from the specified container identified by container-id. -Once a volume is mounted in a one container it can be shared with other -containers using the **--volumes-from** option when running those other -containers. The volumes can be shared even if the original container with the -mount is not running. + Mounts already mounted volumes from a source container onto another + container. You must supply the source's container-id. To share + a volume, use the **--volumes-from** option when running + the target container. You can share volumes even if the source container + is not running. - The container ID may be optionally suffixed with :ro or -:rw to mount the volumes in read-only or read-write mode, respectively. By -default, the volumes are mounted in the same mode (read write or read only) as -the reference container. + By default, Docker mounts the volumes in the same mode (read-write or + read-only) as it is mounted in the source container. Optionally, you + can change this by suffixing the container-id with either the `:ro` or + `:rw ` keyword. + + If the location of the volume from the source container overlaps with + data residing on a target container, then the volume hides + that data on the target. **-w**, **--workdir**="" Working directory inside the container diff --git a/docs/sources/reference/builder.md b/docs/sources/reference/builder.md index 6ee41f5b76..3d34db38cc 100644 --- a/docs/sources/reference/builder.md +++ b/docs/sources/reference/builder.md @@ -777,14 +777,28 @@ If you then run `docker stop test`, the container will not exit cleanly - the VOLUME ["/data"] -The `VOLUME` instruction will create a mount point with the specified name -and mark it as holding externally mounted volumes from native host or other +The `VOLUME` instruction creates a mount point with the specified name +and marks it as holding externally mounted volumes from native host or other containers. The value can be a JSON array, `VOLUME ["/var/log/"]`, or a plain string with multiple arguments, such as `VOLUME /var/log` or `VOLUME /var/log /var/db`. For more information/examples and mounting instructions via the -Docker client, refer to [*Share Directories via Volumes*](/userguide/dockervolumes/#volume) +Docker client, refer to +[*Share Directories via Volumes*](/userguide/dockervolumes/#volume) documentation. +The `docker run` command initializes the newly created volume with any data +that exists at the specified location within the base image. For example, +consider the following Dockerfile snippet: + + FROM ubuntu + RUN mkdir /myvol + RUN echo "hello world" > /myvol/greating + VOLUME /myvol + +This Dockerfile results in an image that causes `docker run`, to +create a new mount point at `/myvol` and copy the `greating` file +into the newly created volume. + > **Note**: > The list is parsed as a JSON array, which means that > you must use double-quotes (") around words not single-quotes ('). diff --git a/docs/sources/userguide/dockervolumes.md b/docs/sources/userguide/dockervolumes.md index 5a75f5b366..fcf7c55943 100644 --- a/docs/sources/userguide/dockervolumes.md +++ b/docs/sources/userguide/dockervolumes.md @@ -21,19 +21,21 @@ Docker. A *data volume* is a specially-designated directory within one or more containers that bypasses the [*Union File -System*](/terms/layer/#union-file-system) to provide several useful features for -persistent or shared data: +System*](/terms/layer/#union-file-system). Data volumes provide several +useful features for persistent or shared data: -- Volumes are initialized when a container is created -- Data volumes can be shared and reused between containers -- Changes to a data volume are made directly -- Changes to a data volume will not be included when you update an image -- Data volumes persist even if the container itself is deleted +- Volumes are initialized when a container is created. If the container's + base image contains data at the specified mount point, that data is + copied into the new volume. +- Data volumes can be shared and reused among containers. +- Changes to a data volume are made directly. +- Changes to a data volume will not be included when you update an image. +- Data volumes persist even if the container itself is deleted. Data volumes are designed to persist data, independent of the container's life -cycle. Docker will therefore *never* automatically delete volumes when you remove -a container, nor will it "garbage collect" volumes that are no longer referenced -by a container. +cycle. Docker therefore *never* automatically delete volumes when you remove +a container, nor will it "garbage collect" volumes that are no longer +referenced by a container. ### Adding a data volume @@ -132,6 +134,11 @@ And another: $ sudo docker run -d --volumes-from dbdata --name db2 training/postgres +In this case, if the `postgres` image contained a directory called `/dbdata` +then mounting the volumes from the `dbdata` container hides the +`/dbdata` files from the `postgres` image. The result is only the files +from the `dbdata` container are visible. + You can use multiple `--volumes-from` parameters to bring together multiple data volumes from multiple containers. From 7910cb52435f6edbd30444114d12d9bfaa34b37c Mon Sep 17 00:00:00 2001 From: Alexander Morozov Date: Mon, 23 Feb 2015 10:58:20 -0800 Subject: [PATCH 031/204] update libcontainer to dd3cb8822352fd4acc0b8b426bd86e47e98f6853 Signed-off-by: Alexander Morozov --- project/vendor.sh | 3 +- .../github.com/docker/libcontainer/.drone.yml | 9 - .../github.com/docker/libcontainer/.gitignore | 1 + .../docker/libcontainer/MAINTAINERS | 1 + .../github.com/docker/libcontainer/Makefile | 7 + .../src/github.com/docker/libcontainer/NOTICE | 2 +- .../docker/libcontainer/PRINCIPLES.md | 2 +- .../github.com/docker/libcontainer/README.md | 167 +++++- .../docker/libcontainer/api_temp.go | 21 - .../docker/libcontainer/apparmor/apparmor.go | 1 - .../docker/libcontainer/capabilities_linux.go | 91 +++ .../docker/libcontainer/cgroups/cgroups.go | 55 +- .../libcontainer/cgroups/fs/apply_raw.go | 141 +++-- .../docker/libcontainer/cgroups/fs/blkio.go | 21 +- .../libcontainer/cgroups/fs/blkio_test.go | 30 + .../docker/libcontainer/cgroups/fs/cpu.go | 25 +- .../libcontainer/cgroups/fs/cpu_test.go | 70 +++ .../docker/libcontainer/cgroups/fs/cpuacct.go | 7 +- .../docker/libcontainer/cgroups/fs/cpuset.go | 38 +- .../libcontainer/cgroups/fs/cpuset_test.go | 63 ++ .../docker/libcontainer/cgroups/fs/devices.go | 24 +- .../libcontainer/cgroups/fs/devices_test.go | 48 ++ .../docker/libcontainer/cgroups/fs/freezer.go | 33 +- .../docker/libcontainer/cgroups/fs/memory.go | 60 +- .../libcontainer/cgroups/fs/memory_test.go | 68 +++ .../libcontainer/cgroups/fs/perf_event.go | 7 +- .../libcontainer/cgroups/fs/util_test.go | 6 +- .../docker/libcontainer/cgroups/fs/utils.go | 10 + .../cgroups/systemd/apply_nosystemd.go | 46 +- .../cgroups/systemd/apply_systemd.go | 176 ++++-- .../github.com/docker/libcontainer/config.go | 154 ----- .../docker/libcontainer/configs/cgroup.go | 57 ++ .../docker/libcontainer/configs/config.go | 145 +++++ .../libcontainer/{ => configs}/config_test.go | 133 ++++- .../docker/libcontainer/configs/device.go | 52 ++ .../libcontainer/configs/device_defaults.go | 137 +++++ .../docker/libcontainer/configs/mount.go | 21 + .../docker/libcontainer/configs/namespaces.go | 109 ++++ .../docker/libcontainer/configs/network.go | 66 +++ .../libcontainer/configs/validate/config.go | 93 +++ .../github.com/docker/libcontainer/console.go | 15 + .../docker/libcontainer/console/console.go | 128 ---- .../docker/libcontainer/console_linux.go | 147 +++++ .../docker/libcontainer/container.go | 142 +++-- .../docker/libcontainer/container_linux.go | 306 ++++++++++ .../libcontainer/container_linux_test.go | 200 +++++++ .../libcontainer/container_nouserns_linux.go | 13 + .../libcontainer/container_userns_linux.go | 26 + .../docker/libcontainer/devices/defaults.go | 159 ----- .../docker/libcontainer/devices/devices.go | 72 +-- .../libcontainer/devices/devices_test.go | 12 +- .../docker/libcontainer/devices/number.go | 4 - .../docker/libcontainer/docs/man/nsinit.1.md | 38 ++ .../github.com/docker/libcontainer/error.go | 39 +- .../docker/libcontainer/error_test.go | 20 + .../github.com/docker/libcontainer/factory.go | 30 +- .../docker/libcontainer/factory_linux.go | 262 +++++++++ .../docker/libcontainer/factory_linux_test.go | 125 ++++ .../docker/libcontainer/generic_error.go | 74 +++ .../docker/libcontainer/generic_error_test.go | 14 + .../docker/libcontainer/hack/validate.sh | 13 + .../docker/libcontainer/init_linux.go | 253 ++++++++ .../libcontainer/integration/exec_test.go | 325 ++++++++++- .../libcontainer/integration/execin_test.go | 356 +++++++++--- .../libcontainer/integration/init_test.go | 67 +-- .../libcontainer/integration/template_test.go | 79 ++- .../libcontainer/integration/utils_test.go | 82 ++- .../docker/libcontainer/mount/init.go | 209 ------- .../docker/libcontainer/mount/mount.go | 109 ---- .../docker/libcontainer/mount/mount_config.go | 28 - .../docker/libcontainer/mount/msmoveroot.go | 20 - .../docker/libcontainer/mount/nodes/nodes.go | 57 -- .../mount/nodes/nodes_unsupported.go | 13 - .../docker/libcontainer/mount/pivotroot.go | 34 -- .../docker/libcontainer/mount/ptmx.go | 30 - .../docker/libcontainer/mount/readonly.go | 11 - .../docker/libcontainer/mount/remount.go | 31 - .../docker/libcontainer/namespaces/create.go | 10 - .../docker/libcontainer/namespaces/exec.go | 229 -------- .../docker/libcontainer/namespaces/execin.go | 132 ----- .../docker/libcontainer/namespaces/init.go | 331 ----------- .../libcontainer/namespaces/nsenter/nsenter.c | 245 -------- .../namespaces/nsenter/nsenter.go | 10 - .../docker/libcontainer/namespaces/utils.go | 45 -- .../libcontainer/netlink/netlink_linux.go | 30 +- .../docker/libcontainer/network/loopback.go | 23 - .../docker/libcontainer/network/network.go | 117 ---- .../docker/libcontainer/network/stats.go | 74 --- .../docker/libcontainer/network/strategy.go | 34 -- .../docker/libcontainer/network/types.go | 50 -- .../docker/libcontainer/network/veth.go | 122 ---- .../docker/libcontainer/network/veth_test.go | 53 -- .../docker/libcontainer/network_linux.go | 208 +++++++ .../docker/libcontainer/notify_linux.go | 7 +- .../docker/libcontainer/notify_linux_test.go | 8 +- .../{namespaces => }/nsenter/README.md | 0 .../docker/libcontainer/nsenter/nsenter.go | 25 + .../{namespaces => }/nsenter/nsenter_test.go | 41 +- .../nsenter/nsenter_unsupported.go | 0 .../docker/libcontainer/nsenter/nsexec.c | 168 ++++++ .../docker/libcontainer/nsinit/Makefile | 2 + .../docker/libcontainer/nsinit/config.go | 265 ++++++++- .../docker/libcontainer/nsinit/exec.go | 261 +++------ .../docker/libcontainer/nsinit/init.go | 57 +- .../docker/libcontainer/nsinit/main.go | 60 +- .../docker/libcontainer/nsinit/nsenter.go | 84 --- .../docker/libcontainer/nsinit/oom.go | 38 +- .../docker/libcontainer/nsinit/pause.go | 64 +- .../docker/libcontainer/nsinit/state.go | 31 + .../docker/libcontainer/nsinit/stats.go | 48 +- .../docker/libcontainer/nsinit/tty.go | 65 +++ .../docker/libcontainer/nsinit/utils.go | 101 ++-- .../github.com/docker/libcontainer/process.go | 85 ++- .../docker/libcontainer/process_linux.go | 240 ++++++++ .../docker/libcontainer/rootfs_linux.go | 363 ++++++++++++ .../libcontainer/sample_configs/apparmor.json | 532 ++++++++++------- .../sample_configs/attach_to_bridge.json | 549 +++++++++++------- .../libcontainer/sample_configs/host-pid.json | 530 ++++++++++------- .../libcontainer/sample_configs/minimal.json | 537 ++++++++++------- .../route_source_address_selection.json | 209 ------- .../libcontainer/sample_configs/selinux.json | 533 ++++++++++------- .../libcontainer/sample_configs/userns.json | 376 ++++++++++++ .../security/capabilities/capabilities.go | 56 -- .../security/capabilities/types.go | 88 --- .../security/capabilities/types_test.go | 19 - .../security/restrict/restrict.go | 53 -- .../security/restrict/unsupported.go | 9 - .../docker/libcontainer/setns_init_linux.go | 35 ++ .../docker/libcontainer/stacktrace/capture.go | 23 + .../libcontainer/stacktrace/capture_test.go | 27 + .../docker/libcontainer/stacktrace/frame.go | 35 ++ .../libcontainer/stacktrace/frame_test.go | 20 + .../libcontainer/stacktrace/stacktrace.go | 5 + .../libcontainer/standard_init_linux.go | 94 +++ .../github.com/docker/libcontainer/state.go | 77 --- .../github.com/docker/libcontainer/stats.go | 22 + .../docker/libcontainer/system/linux.go | 29 +- .../github.com/docker/libcontainer/types.go | 11 - .../docker/libcontainer/update-vendor.sh | 5 +- .../docker/libcontainer/utils/utils.go | 13 + .../github.com/godbus/dbus/CONTRIBUTING.md | 50 ++ vendor/src/github.com/godbus/dbus/MAINTAINERS | 2 + vendor/src/github.com/godbus/dbus/conn.go | 26 +- .../godbus/dbus/introspect/introspect.go | 6 + .../godbus/dbus/introspect/introspectable.go | 5 +- .../src/github.com/godbus/dbus/prop/prop.go | 8 +- .../github.com/godbus/dbus/transport_unix.go | 6 + .../dbus/transport_unixcred_dragonfly.go | 95 +++ ...nixcred.go => transport_unixcred_linux.go} | 5 +- vendor/src/github.com/godbus/dbus/variant.go | 14 +- .../gocapability/capability/capability.go | 3 +- .../capability/capability_linux.go | 52 +- .../syndtr/gocapability/capability/enum.go | 89 +-- 153 files changed, 8501 insertions(+), 5216 deletions(-) delete mode 100755 vendor/src/github.com/docker/libcontainer/.drone.yml create mode 100644 vendor/src/github.com/docker/libcontainer/.gitignore delete mode 100644 vendor/src/github.com/docker/libcontainer/api_temp.go create mode 100644 vendor/src/github.com/docker/libcontainer/capabilities_linux.go create mode 100644 vendor/src/github.com/docker/libcontainer/cgroups/fs/cpuset_test.go create mode 100644 vendor/src/github.com/docker/libcontainer/cgroups/fs/devices_test.go delete mode 100644 vendor/src/github.com/docker/libcontainer/config.go create mode 100644 vendor/src/github.com/docker/libcontainer/configs/cgroup.go create mode 100644 vendor/src/github.com/docker/libcontainer/configs/config.go rename vendor/src/github.com/docker/libcontainer/{ => configs}/config_test.go (58%) create mode 100644 vendor/src/github.com/docker/libcontainer/configs/device.go create mode 100644 vendor/src/github.com/docker/libcontainer/configs/device_defaults.go create mode 100644 vendor/src/github.com/docker/libcontainer/configs/mount.go create mode 100644 vendor/src/github.com/docker/libcontainer/configs/namespaces.go create mode 100644 vendor/src/github.com/docker/libcontainer/configs/network.go create mode 100644 vendor/src/github.com/docker/libcontainer/configs/validate/config.go create mode 100644 vendor/src/github.com/docker/libcontainer/console.go delete mode 100644 vendor/src/github.com/docker/libcontainer/console/console.go create mode 100644 vendor/src/github.com/docker/libcontainer/console_linux.go create mode 100644 vendor/src/github.com/docker/libcontainer/container_linux.go create mode 100644 vendor/src/github.com/docker/libcontainer/container_linux_test.go create mode 100644 vendor/src/github.com/docker/libcontainer/container_nouserns_linux.go create mode 100644 vendor/src/github.com/docker/libcontainer/container_userns_linux.go delete mode 100644 vendor/src/github.com/docker/libcontainer/devices/defaults.go create mode 100644 vendor/src/github.com/docker/libcontainer/docs/man/nsinit.1.md create mode 100644 vendor/src/github.com/docker/libcontainer/error_test.go create mode 100644 vendor/src/github.com/docker/libcontainer/factory_linux.go create mode 100644 vendor/src/github.com/docker/libcontainer/factory_linux_test.go create mode 100644 vendor/src/github.com/docker/libcontainer/generic_error.go create mode 100644 vendor/src/github.com/docker/libcontainer/generic_error_test.go create mode 100755 vendor/src/github.com/docker/libcontainer/hack/validate.sh create mode 100644 vendor/src/github.com/docker/libcontainer/init_linux.go delete mode 100644 vendor/src/github.com/docker/libcontainer/mount/init.go delete mode 100644 vendor/src/github.com/docker/libcontainer/mount/mount.go delete mode 100644 vendor/src/github.com/docker/libcontainer/mount/mount_config.go delete mode 100644 vendor/src/github.com/docker/libcontainer/mount/msmoveroot.go delete mode 100644 vendor/src/github.com/docker/libcontainer/mount/nodes/nodes.go delete mode 100644 vendor/src/github.com/docker/libcontainer/mount/nodes/nodes_unsupported.go delete mode 100644 vendor/src/github.com/docker/libcontainer/mount/pivotroot.go delete mode 100644 vendor/src/github.com/docker/libcontainer/mount/ptmx.go delete mode 100644 vendor/src/github.com/docker/libcontainer/mount/readonly.go delete mode 100644 vendor/src/github.com/docker/libcontainer/mount/remount.go delete mode 100644 vendor/src/github.com/docker/libcontainer/namespaces/create.go delete mode 100644 vendor/src/github.com/docker/libcontainer/namespaces/exec.go delete mode 100644 vendor/src/github.com/docker/libcontainer/namespaces/execin.go delete mode 100644 vendor/src/github.com/docker/libcontainer/namespaces/init.go delete mode 100644 vendor/src/github.com/docker/libcontainer/namespaces/nsenter/nsenter.c delete mode 100644 vendor/src/github.com/docker/libcontainer/namespaces/nsenter/nsenter.go delete mode 100644 vendor/src/github.com/docker/libcontainer/namespaces/utils.go delete mode 100644 vendor/src/github.com/docker/libcontainer/network/loopback.go delete mode 100644 vendor/src/github.com/docker/libcontainer/network/network.go delete mode 100644 vendor/src/github.com/docker/libcontainer/network/stats.go delete mode 100644 vendor/src/github.com/docker/libcontainer/network/strategy.go delete mode 100644 vendor/src/github.com/docker/libcontainer/network/types.go delete mode 100644 vendor/src/github.com/docker/libcontainer/network/veth.go delete mode 100644 vendor/src/github.com/docker/libcontainer/network/veth_test.go create mode 100644 vendor/src/github.com/docker/libcontainer/network_linux.go rename vendor/src/github.com/docker/libcontainer/{namespaces => }/nsenter/README.md (100%) create mode 100644 vendor/src/github.com/docker/libcontainer/nsenter/nsenter.go rename vendor/src/github.com/docker/libcontainer/{namespaces => }/nsenter/nsenter_test.go (60%) rename vendor/src/github.com/docker/libcontainer/{namespaces => }/nsenter/nsenter_unsupported.go (100%) create mode 100644 vendor/src/github.com/docker/libcontainer/nsenter/nsexec.c create mode 100644 vendor/src/github.com/docker/libcontainer/nsinit/Makefile delete mode 100644 vendor/src/github.com/docker/libcontainer/nsinit/nsenter.go create mode 100644 vendor/src/github.com/docker/libcontainer/nsinit/state.go create mode 100644 vendor/src/github.com/docker/libcontainer/nsinit/tty.go create mode 100644 vendor/src/github.com/docker/libcontainer/process_linux.go create mode 100644 vendor/src/github.com/docker/libcontainer/rootfs_linux.go delete mode 100644 vendor/src/github.com/docker/libcontainer/sample_configs/route_source_address_selection.json create mode 100644 vendor/src/github.com/docker/libcontainer/sample_configs/userns.json delete mode 100644 vendor/src/github.com/docker/libcontainer/security/capabilities/capabilities.go delete mode 100644 vendor/src/github.com/docker/libcontainer/security/capabilities/types.go delete mode 100644 vendor/src/github.com/docker/libcontainer/security/capabilities/types_test.go delete mode 100644 vendor/src/github.com/docker/libcontainer/security/restrict/restrict.go delete mode 100644 vendor/src/github.com/docker/libcontainer/security/restrict/unsupported.go create mode 100644 vendor/src/github.com/docker/libcontainer/setns_init_linux.go create mode 100644 vendor/src/github.com/docker/libcontainer/stacktrace/capture.go create mode 100644 vendor/src/github.com/docker/libcontainer/stacktrace/capture_test.go create mode 100644 vendor/src/github.com/docker/libcontainer/stacktrace/frame.go create mode 100644 vendor/src/github.com/docker/libcontainer/stacktrace/frame_test.go create mode 100644 vendor/src/github.com/docker/libcontainer/stacktrace/stacktrace.go create mode 100644 vendor/src/github.com/docker/libcontainer/standard_init_linux.go delete mode 100644 vendor/src/github.com/docker/libcontainer/state.go create mode 100644 vendor/src/github.com/docker/libcontainer/stats.go delete mode 100644 vendor/src/github.com/docker/libcontainer/types.go create mode 100644 vendor/src/github.com/godbus/dbus/CONTRIBUTING.md create mode 100644 vendor/src/github.com/godbus/dbus/MAINTAINERS create mode 100644 vendor/src/github.com/godbus/dbus/transport_unixcred_dragonfly.go rename vendor/src/github.com/godbus/dbus/{transport_unixcred.go => transport_unixcred_linux.go} (61%) diff --git a/project/vendor.sh b/project/vendor.sh index 634e17602c..0849bc00c2 100755 --- a/project/vendor.sh +++ b/project/vendor.sh @@ -68,8 +68,7 @@ if [ "$1" = '--go' ]; then mv tmp-tar src/code.google.com/p/go/src/pkg/archive/tar fi -# this commit is from docker_1.5 branch in libcontainer, pls delete that branch when you'll update libcontainer again -clone git github.com/docker/libcontainer 2d3b5af7486f1a4e80a5ed91859d309b4eebf80c +clone git github.com/docker/libcontainer dd3cb8822352fd4acc0b8b426bd86e47e98f6853 # see src/github.com/docker/libcontainer/update-vendor.sh which is the "source of truth" for libcontainer deps (just like this file) rm -rf src/github.com/docker/libcontainer/vendor eval "$(grep '^clone ' src/github.com/docker/libcontainer/update-vendor.sh | grep -v 'github.com/codegangsta/cli')" diff --git a/vendor/src/github.com/docker/libcontainer/.drone.yml b/vendor/src/github.com/docker/libcontainer/.drone.yml deleted file mode 100755 index 80d298f218..0000000000 --- a/vendor/src/github.com/docker/libcontainer/.drone.yml +++ /dev/null @@ -1,9 +0,0 @@ -image: dockercore/libcontainer -script: -# Setup the DockerInDocker environment. - - /dind - - sed -i 's!docker/docker!docker/libcontainer!' /go/src/github.com/docker/docker/hack/make/.validate - - bash /go/src/github.com/docker/docker/hack/make/validate-dco - - bash /go/src/github.com/docker/docker/hack/make/validate-gofmt - - export GOPATH="$GOPATH:/go:$(pwd)/vendor" # Drone mucks with our GOPATH - - make direct-test diff --git a/vendor/src/github.com/docker/libcontainer/.gitignore b/vendor/src/github.com/docker/libcontainer/.gitignore new file mode 100644 index 0000000000..4c2914fc7c --- /dev/null +++ b/vendor/src/github.com/docker/libcontainer/.gitignore @@ -0,0 +1 @@ +nsinit/nsinit diff --git a/vendor/src/github.com/docker/libcontainer/MAINTAINERS b/vendor/src/github.com/docker/libcontainer/MAINTAINERS index 5235131722..cea3500f2b 100644 --- a/vendor/src/github.com/docker/libcontainer/MAINTAINERS +++ b/vendor/src/github.com/docker/libcontainer/MAINTAINERS @@ -3,4 +3,5 @@ Rohit Jnagal (@rjnagal) Victor Marmol (@vmarmol) Mrunal Patel (@mrunalp) Alexandr Morozov (@LK4D4) +Daniel, Dao Quang Minh (@dqminh) update-vendor.sh: Tianon Gravi (@tianon) diff --git a/vendor/src/github.com/docker/libcontainer/Makefile b/vendor/src/github.com/docker/libcontainer/Makefile index f94171b0fc..c2c9a98d35 100644 --- a/vendor/src/github.com/docker/libcontainer/Makefile +++ b/vendor/src/github.com/docker/libcontainer/Makefile @@ -22,3 +22,10 @@ direct-build: direct-install: go install -v $(GO_PACKAGES) + +local: + go test -v + +validate: + hack/validate.sh + diff --git a/vendor/src/github.com/docker/libcontainer/NOTICE b/vendor/src/github.com/docker/libcontainer/NOTICE index ca1635f896..dc9129878c 100644 --- a/vendor/src/github.com/docker/libcontainer/NOTICE +++ b/vendor/src/github.com/docker/libcontainer/NOTICE @@ -1,5 +1,5 @@ libcontainer -Copyright 2012-2014 Docker, Inc. +Copyright 2012-2015 Docker, Inc. This product includes software developed at Docker, Inc. (http://www.docker.com). diff --git a/vendor/src/github.com/docker/libcontainer/PRINCIPLES.md b/vendor/src/github.com/docker/libcontainer/PRINCIPLES.md index 42396c0eec..0560642102 100644 --- a/vendor/src/github.com/docker/libcontainer/PRINCIPLES.md +++ b/vendor/src/github.com/docker/libcontainer/PRINCIPLES.md @@ -8,7 +8,7 @@ In the design and development of libcontainer we try to follow these principles: * Less code is better. * Fewer components are better. Do you really need to add one more class? * 50 lines of straightforward, readable code is better than 10 lines of magic that nobody can understand. -* Don't do later what you can do now. "//FIXME: refactor" is not acceptable in new code. +* Don't do later what you can do now. "//TODO: refactor" is not acceptable in new code. * When hesitating between two options, choose the one that is easier to reverse. * "No" is temporary; "Yes" is forever. If you're not sure about a new feature, say no. You can change your mind later. * Containers must be portable to the greatest possible number of machines. Be suspicious of any change which makes machines less interchangeable. diff --git a/vendor/src/github.com/docker/libcontainer/README.md b/vendor/src/github.com/docker/libcontainer/README.md index 37047e68c8..984f2c5238 100644 --- a/vendor/src/github.com/docker/libcontainer/README.md +++ b/vendor/src/github.com/docker/libcontainer/README.md @@ -1,48 +1,169 @@ -## libcontainer - reference implementation for containers [![Build Status](https://ci.dockerproject.com/github.com/docker/libcontainer/status.svg?branch=master)](https://ci.dockerproject.com/github.com/docker/libcontainer) +## libcontainer - reference implementation for containers [![Build Status](https://jenkins.dockerproject.com/buildStatus/icon?job=Libcontainer Master)](https://jenkins.dockerproject.com/job/Libcontainer%20Master/) -### Note on API changes: - -Please bear with us while we work on making the libcontainer API stable and something that we can support long term. We are currently discussing the API with the community, therefore, if you currently depend on libcontainer please pin your dependency at a specific tag or commit id. Please join the discussion and help shape the API. - -#### Background - -libcontainer specifies configuration options for what a container is. It provides a native Go implementation for using Linux namespaces with no external dependencies. libcontainer provides many convenience functions for working with namespaces, networking, and management. +Libcontainer provides a native Go implementation for creating containers +with namespaces, cgroups, capabilities, and filesystem access controls. +It allows you to manage the lifecycle of the container performing additional operations +after the container is created. #### Container -A container is a self contained execution environment that shares the kernel of the host system and which is (optionally) isolated from other containers in the system. +A container is a self contained execution environment that shares the kernel of the +host system and which is (optionally) isolated from other containers in the system. -libcontainer may be used to execute a process in a container. If a user tries to run a new process inside an existing container, the new process is added to the processes executing in the container. +#### Using libcontainer + +To create a container you first have to initialize an instance of a factory +that will handle the creation and initialization for a container. + +Because containers are spawned in a two step process you will need to provide +arguments to a binary that will be executed as the init process for the container. +To use the current binary that is spawning the containers and acting as the parent +you can use `os.Args[0]` and we have a command called `init` setup. + +```go +root, err := libcontainer.New("/var/lib/container", libcontainer.InitArgs(os.Args[0], "init")) +if err != nil { + log.Fatal(err) +} +``` + +Once you have an instance of the factory created we can create a configuration +struct describing how the container is to be created. A sample would look similar to this: + +```go +config := &configs.Config{ + Rootfs: rootfs, + Capabilities: []string{ + "CHOWN", + "DAC_OVERRIDE", + "FSETID", + "FOWNER", + "MKNOD", + "NET_RAW", + "SETGID", + "SETUID", + "SETFCAP", + "SETPCAP", + "NET_BIND_SERVICE", + "SYS_CHROOT", + "KILL", + "AUDIT_WRITE", + }, + Namespaces: configs.Namespaces([]configs.Namespace{ + {Type: configs.NEWNS}, + {Type: configs.NEWUTS}, + {Type: configs.NEWIPC}, + {Type: configs.NEWPID}, + {Type: configs.NEWNET}, + }), + Cgroups: &configs.Cgroup{ + Name: "test-container", + Parent: "system", + AllowAllDevices: false, + AllowedDevices: configs.DefaultAllowedDevices, + }, + + Devices: configs.DefaultAutoCreatedDevices, + Hostname: "testing", + Networks: []*configs.Network{ + { + Type: "loopback", + Address: "127.0.0.1/0", + Gateway: "localhost", + }, + }, + Rlimits: []configs.Rlimit{ + { + Type: syscall.RLIMIT_NOFILE, + Hard: uint64(1024), + Soft: uint64(1024), + }, + }, +} +``` + +Once you have the configuration populated you can create a container: + +```go +container, err := root.Create("container-id", config) +``` + +To spawn bash as the initial process inside the container and have the +processes pid returned in order to wait, signal, or kill the process: + +```go +process := &libcontainer.Process{ + Args: []string{"/bin/bash"}, + Env: []string{"PATH=/bin"}, + User: "daemon", + Stdin: os.Stdin, + Stdout: os.Stdout, + Stderr: os.Stderr, +} + +err := container.Start(process) +if err != nil { + log.Fatal(err) +} + +// wait for the process to finish. +status, err := process.Wait() +if err != nil { + log.Fatal(err) +} + +// destroy the container. +container.Destroy() +``` + +Additional ways to interact with a running container are: + +```go +// return all the pids for all processes running inside the container. +processes, err := container.Processes() + +// get detailed cpu, memory, io, and network statistics for the container and +// it's processes. +stats, err := container.Stats() -#### Root file system +// pause all processes inside the container. +container.Pause() -A container runs with a directory known as its *root file system*, or *rootfs*, mounted as the file system root. The rootfs is usually a full system tree. - - -#### Configuration - -A container is initially configured by supplying configuration data when the container is created. +// resume all paused processes. +container.Resume() +``` #### nsinit -`nsinit` is a cli application which demonstrates the use of libcontainer. It is able to spawn new containers or join existing containers, based on the current directory. +`nsinit` is a cli application which demonstrates the use of libcontainer. +It is able to spawn new containers or join existing containers. A root +filesystem must be provided for use along with a container configuration file. -To use `nsinit`, cd into a Linux rootfs and copy a `container.json` file into the directory with your specified configuration. Environment, networking, and different capabilities for the container are specified in this file. The configuration is used for each process executed inside the container. +To use `nsinit`, cd into a Linux rootfs and copy a `container.json` file into +the directory with your specified configuration. Environment, networking, +and different capabilities for the container are specified in this file. +The configuration is used for each process executed inside the container. See the `sample_configs` folder for examples of what the container configuration should look like. To execute `/bin/bash` in the current directory as a container just run the following **as root**: ```bash -nsinit exec /bin/bash +nsinit exec --tty /bin/bash ``` -If you wish to spawn another process inside the container while your current bash session is running, run the same command again to get another bash shell (or change the command). If the original process (PID 1) dies, all other processes spawned inside the container will be killed and the namespace will be removed. +If you wish to spawn another process inside the container while your +current bash session is running, run the same command again to +get another bash shell (or change the command). If the original +process (PID 1) dies, all other processes spawned inside the container +will be killed and the namespace will be removed. -You can identify if a process is running in a container by looking to see if `state.json` is in the root of the directory. +You can identify if a process is running in a container by +looking to see if `state.json` is in the root of the directory. -You may also specify an alternate root place where the `container.json` file is read and where the `state.json` file will be saved. +You may also specify an alternate root place where +the `container.json` file is read and where the `state.json` file will be saved. #### Future See the [roadmap](ROADMAP.md). diff --git a/vendor/src/github.com/docker/libcontainer/api_temp.go b/vendor/src/github.com/docker/libcontainer/api_temp.go deleted file mode 100644 index 5c682ee344..0000000000 --- a/vendor/src/github.com/docker/libcontainer/api_temp.go +++ /dev/null @@ -1,21 +0,0 @@ -/* -Temporary API endpoint for libcontainer while the full API is finalized (api.go). -*/ -package libcontainer - -import ( - "github.com/docker/libcontainer/cgroups/fs" - "github.com/docker/libcontainer/network" -) - -// TODO(vmarmol): Complete Stats() in final libcontainer API and move users to that. -// DEPRECATED: The below portions are only to be used during the transition to the official API. -// Returns all available stats for the given container. -func GetStats(container *Config, state *State) (stats *ContainerStats, err error) { - stats = &ContainerStats{} - if stats.CgroupStats, err = fs.GetStats(state.CgroupPaths); err != nil { - return stats, err - } - stats.NetworkStats, err = network.GetStats(&state.NetworkState) - return stats, err -} diff --git a/vendor/src/github.com/docker/libcontainer/apparmor/apparmor.go b/vendor/src/github.com/docker/libcontainer/apparmor/apparmor.go index fb1574dfc6..3be3294d85 100644 --- a/vendor/src/github.com/docker/libcontainer/apparmor/apparmor.go +++ b/vendor/src/github.com/docker/libcontainer/apparmor/apparmor.go @@ -24,7 +24,6 @@ func ApplyProfile(name string) error { if name == "" { return nil } - cName := C.CString(name) defer C.free(unsafe.Pointer(cName)) diff --git a/vendor/src/github.com/docker/libcontainer/capabilities_linux.go b/vendor/src/github.com/docker/libcontainer/capabilities_linux.go new file mode 100644 index 0000000000..6b8b465c56 --- /dev/null +++ b/vendor/src/github.com/docker/libcontainer/capabilities_linux.go @@ -0,0 +1,91 @@ +// +build linux + +package libcontainer + +import ( + "fmt" + "os" + + "github.com/syndtr/gocapability/capability" +) + +const allCapabilityTypes = capability.CAPS | capability.BOUNDS + +var capabilityList = map[string]capability.Cap{ + "SETPCAP": capability.CAP_SETPCAP, + "SYS_MODULE": capability.CAP_SYS_MODULE, + "SYS_RAWIO": capability.CAP_SYS_RAWIO, + "SYS_PACCT": capability.CAP_SYS_PACCT, + "SYS_ADMIN": capability.CAP_SYS_ADMIN, + "SYS_NICE": capability.CAP_SYS_NICE, + "SYS_RESOURCE": capability.CAP_SYS_RESOURCE, + "SYS_TIME": capability.CAP_SYS_TIME, + "SYS_TTY_CONFIG": capability.CAP_SYS_TTY_CONFIG, + "MKNOD": capability.CAP_MKNOD, + "AUDIT_WRITE": capability.CAP_AUDIT_WRITE, + "AUDIT_CONTROL": capability.CAP_AUDIT_CONTROL, + "MAC_OVERRIDE": capability.CAP_MAC_OVERRIDE, + "MAC_ADMIN": capability.CAP_MAC_ADMIN, + "NET_ADMIN": capability.CAP_NET_ADMIN, + "SYSLOG": capability.CAP_SYSLOG, + "CHOWN": capability.CAP_CHOWN, + "NET_RAW": capability.CAP_NET_RAW, + "DAC_OVERRIDE": capability.CAP_DAC_OVERRIDE, + "FOWNER": capability.CAP_FOWNER, + "DAC_READ_SEARCH": capability.CAP_DAC_READ_SEARCH, + "FSETID": capability.CAP_FSETID, + "KILL": capability.CAP_KILL, + "SETGID": capability.CAP_SETGID, + "SETUID": capability.CAP_SETUID, + "LINUX_IMMUTABLE": capability.CAP_LINUX_IMMUTABLE, + "NET_BIND_SERVICE": capability.CAP_NET_BIND_SERVICE, + "NET_BROADCAST": capability.CAP_NET_BROADCAST, + "IPC_LOCK": capability.CAP_IPC_LOCK, + "IPC_OWNER": capability.CAP_IPC_OWNER, + "SYS_CHROOT": capability.CAP_SYS_CHROOT, + "SYS_PTRACE": capability.CAP_SYS_PTRACE, + "SYS_BOOT": capability.CAP_SYS_BOOT, + "LEASE": capability.CAP_LEASE, + "SETFCAP": capability.CAP_SETFCAP, + "WAKE_ALARM": capability.CAP_WAKE_ALARM, + "BLOCK_SUSPEND": capability.CAP_BLOCK_SUSPEND, + "AUDIT_READ": capability.CAP_AUDIT_READ, +} + +func newCapWhitelist(caps []string) (*whitelist, error) { + l := []capability.Cap{} + for _, c := range caps { + v, ok := capabilityList[c] + if !ok { + return nil, fmt.Errorf("unknown capability %q", c) + } + l = append(l, v) + } + pid, err := capability.NewPid(os.Getpid()) + if err != nil { + return nil, err + } + return &whitelist{ + keep: l, + pid: pid, + }, nil +} + +type whitelist struct { + pid capability.Capabilities + keep []capability.Cap +} + +// dropBoundingSet drops the capability bounding set to those specified in the whitelist. +func (w *whitelist) dropBoundingSet() error { + w.pid.Clear(capability.BOUNDS) + w.pid.Set(capability.BOUNDS, w.keep...) + return w.pid.Apply(capability.BOUNDS) +} + +// drop drops all capabilities for the current process except those specified in the whitelist. +func (w *whitelist) drop() error { + w.pid.Clear(allCapabilityTypes) + w.pid.Set(allCapabilityTypes, w.keep...) + return w.pid.Apply(allCapabilityTypes) +} diff --git a/vendor/src/github.com/docker/libcontainer/cgroups/cgroups.go b/vendor/src/github.com/docker/libcontainer/cgroups/cgroups.go index 106698d18f..df7bfb3c71 100644 --- a/vendor/src/github.com/docker/libcontainer/cgroups/cgroups.go +++ b/vendor/src/github.com/docker/libcontainer/cgroups/cgroups.go @@ -3,16 +3,38 @@ package cgroups import ( "fmt" - "github.com/docker/libcontainer/devices" + "github.com/docker/libcontainer/configs" ) -type FreezerState string +type Manager interface { + // Apply cgroup configuration to the process with the specified pid + Apply(pid int) error -const ( - Undefined FreezerState = "" - Frozen FreezerState = "FROZEN" - Thawed FreezerState = "THAWED" -) + // Returns the PIDs inside the cgroup set + GetPids() ([]int, error) + + // Returns statistics for the cgroup set + GetStats() (*Stats, error) + + // Toggles the freezer cgroup according with specified state + Freeze(state configs.FreezerState) error + + // Destroys the cgroup set + Destroy() error + + // NewCgroupManager() and LoadCgroupManager() require following attributes: + // Paths map[string]string + // Cgroups *cgroups.Cgroup + // Paths maps cgroup subsystem to path at which it is mounted. + // Cgroups specifies specific cgroup settings for the various subsystems + + // Returns cgroup paths to save in a state file and to be able to + // restore the object later. + GetPaths() map[string]string + + // Set the cgroup as configured. + Set(container *configs.Config) error +} type NotFoundError struct { Subsystem string @@ -32,25 +54,6 @@ func IsNotFound(err error) bool { if err == nil { return false } - _, ok := err.(*NotFoundError) return ok } - -type Cgroup struct { - Name string `json:"name,omitempty"` - Parent string `json:"parent,omitempty"` // name of parent cgroup or slice - - AllowAllDevices bool `json:"allow_all_devices,omitempty"` // If this is true allow access to any kind of device within the container. If false, allow access only to devices explicitly listed in the allowed_devices list. - AllowedDevices []*devices.Device `json:"allowed_devices,omitempty"` - Memory int64 `json:"memory,omitempty"` // Memory limit (in bytes) - MemoryReservation int64 `json:"memory_reservation,omitempty"` // Memory reservation or soft_limit (in bytes) - MemorySwap int64 `json:"memory_swap,omitempty"` // Total memory usage (memory + swap); set `-1' to disable swap - CpuShares int64 `json:"cpu_shares,omitempty"` // CPU shares (relative weight vs. other containers) - CpuQuota int64 `json:"cpu_quota,omitempty"` // CPU hardcap limit (in usecs). Allowed cpu time in a given period. - CpuPeriod int64 `json:"cpu_period,omitempty"` // CPU period to be used for hardcapping (in usecs). 0 to use system default. - CpusetCpus string `json:"cpuset_cpus,omitempty"` // CPU to use - CpusetMems string `json:"cpuset_mems,omitempty"` // MEM to use - Freezer FreezerState `json:"freezer,omitempty"` // set the freeze value for the process - Slice string `json:"slice,omitempty"` // Parent slice to use for systemd -} diff --git a/vendor/src/github.com/docker/libcontainer/cgroups/fs/apply_raw.go b/vendor/src/github.com/docker/libcontainer/cgroups/fs/apply_raw.go index 58046b0ad7..4943b77529 100644 --- a/vendor/src/github.com/docker/libcontainer/cgroups/fs/apply_raw.go +++ b/vendor/src/github.com/docker/libcontainer/cgroups/fs/apply_raw.go @@ -1,13 +1,14 @@ package fs import ( - "fmt" "io/ioutil" "os" "path/filepath" "strconv" + "sync" "github.com/docker/libcontainer/cgroups" + "github.com/docker/libcontainer/configs" ) var ( @@ -24,43 +25,65 @@ var ( CgroupProcesses = "cgroup.procs" ) -// The absolute path to the root of the cgroup hierarchies. -var cgroupRoot string - -// TODO(vmarmol): Report error here, we'll probably need to wait for the new API. -func init() { - // we can pick any subsystem to find the root - cpuRoot, err := cgroups.FindCgroupMountpoint("cpu") - if err != nil { - return - } - cgroupRoot = filepath.Dir(cpuRoot) - - if _, err := os.Stat(cgroupRoot); err != nil { - return - } -} - type subsystem interface { // Returns the stats, as 'stats', corresponding to the cgroup under 'path'. GetStats(path string, stats *cgroups.Stats) error // Removes the cgroup represented by 'data'. Remove(*data) error // Creates and joins the cgroup represented by data. - Set(*data) error + Apply(*data) error + // Set the cgroup represented by cgroup. + Set(path string, cgroup *configs.Cgroup) error +} + +type Manager struct { + Cgroups *configs.Cgroup + Paths map[string]string +} + +// The absolute path to the root of the cgroup hierarchies. +var cgroupRootLock sync.Mutex +var cgroupRoot string + +// Gets the cgroupRoot. +func getCgroupRoot() (string, error) { + cgroupRootLock.Lock() + defer cgroupRootLock.Unlock() + + if cgroupRoot != "" { + return cgroupRoot, nil + } + + // we can pick any subsystem to find the root + cpuRoot, err := cgroups.FindCgroupMountpoint("cpu") + if err != nil { + return "", err + } + root := filepath.Dir(cpuRoot) + + if _, err := os.Stat(root); err != nil { + return "", err + } + + cgroupRoot = root + return cgroupRoot, nil } type data struct { root string cgroup string - c *cgroups.Cgroup + c *configs.Cgroup pid int } -func Apply(c *cgroups.Cgroup, pid int) (map[string]string, error) { - d, err := getCgroupData(c, pid) +func (m *Manager) Apply(pid int) error { + if m.Cgroups == nil { + return nil + } + + d, err := getCgroupData(m.Cgroups, pid) if err != nil { - return nil, err + return err } paths := make(map[string]string) @@ -70,10 +93,10 @@ func Apply(c *cgroups.Cgroup, pid int) (map[string]string, error) { } }() for name, sys := range subsystems { - if err := sys.Set(d); err != nil { - return nil, err + if err := sys.Apply(d); err != nil { + return err } - // FIXME: Apply should, ideally, be reentrant or be broken up into a separate + // TODO: Apply should, ideally, be reentrant or be broken up into a separate // create and join phase so that the cgroup hierarchy for a container can be // created then join consists of writing the process pids to cgroup.procs p, err := d.path(name) @@ -81,16 +104,26 @@ func Apply(c *cgroups.Cgroup, pid int) (map[string]string, error) { if cgroups.IsNotFound(err) { continue } - return nil, err + return err } paths[name] = p } - return paths, nil + m.Paths = paths + + return nil +} + +func (m *Manager) Destroy() error { + return cgroups.RemovePaths(m.Paths) +} + +func (m *Manager) GetPaths() map[string]string { + return m.Paths } // Symmetrical public function to update device based cgroups. Also available // in the systemd implementation. -func ApplyDevices(c *cgroups.Cgroup, pid int) error { +func ApplyDevices(c *configs.Cgroup, pid int) error { d, err := getCgroupData(c, pid) if err != nil { return err @@ -98,12 +131,12 @@ func ApplyDevices(c *cgroups.Cgroup, pid int) error { devices := subsystems["devices"] - return devices.Set(d) + return devices.Apply(d) } -func GetStats(systemPaths map[string]string) (*cgroups.Stats, error) { +func (m *Manager) GetStats() (*cgroups.Stats, error) { stats := cgroups.NewStats() - for name, path := range systemPaths { + for name, path := range m.Paths { sys, ok := subsystems[name] if !ok || !cgroups.PathExists(path) { continue @@ -116,29 +149,48 @@ func GetStats(systemPaths map[string]string) (*cgroups.Stats, error) { return stats, nil } +func (m *Manager) Set(container *configs.Config) error { + for name, path := range m.Paths { + sys, ok := subsystems[name] + if !ok || !cgroups.PathExists(path) { + continue + } + if err := sys.Set(path, container.Cgroups); err != nil { + return err + } + } + + return nil +} + // Freeze toggles the container's freezer cgroup depending on the state // provided -func Freeze(c *cgroups.Cgroup, state cgroups.FreezerState) error { - d, err := getCgroupData(c, 0) +func (m *Manager) Freeze(state configs.FreezerState) error { + d, err := getCgroupData(m.Cgroups, 0) if err != nil { return err } - prevState := c.Freezer - c.Freezer = state + dir, err := d.path("freezer") + if err != nil { + return err + } + + prevState := m.Cgroups.Freezer + m.Cgroups.Freezer = state freezer := subsystems["freezer"] - err = freezer.Set(d) + err = freezer.Set(dir, m.Cgroups) if err != nil { - c.Freezer = prevState + m.Cgroups.Freezer = prevState return err } return nil } -func GetPids(c *cgroups.Cgroup) ([]int, error) { - d, err := getCgroupData(c, 0) +func (m *Manager) GetPids() ([]int, error) { + d, err := getCgroupData(m.Cgroups, 0) if err != nil { return nil, err } @@ -151,9 +203,10 @@ func GetPids(c *cgroups.Cgroup) ([]int, error) { return cgroups.ReadProcsFile(dir) } -func getCgroupData(c *cgroups.Cgroup, pid int) (*data, error) { - if cgroupRoot == "" { - return nil, fmt.Errorf("failed to find the cgroup root") +func getCgroupData(c *configs.Cgroup, pid int) (*data, error) { + root, err := getCgroupRoot() + if err != nil { + return nil, err } cgroup := c.Name @@ -162,7 +215,7 @@ func getCgroupData(c *cgroups.Cgroup, pid int) (*data, error) { } return &data{ - root: cgroupRoot, + root: root, cgroup: cgroup, c: c, pid: pid, diff --git a/vendor/src/github.com/docker/libcontainer/cgroups/fs/blkio.go b/vendor/src/github.com/docker/libcontainer/cgroups/fs/blkio.go index ce824d56c2..8e132643bb 100644 --- a/vendor/src/github.com/docker/libcontainer/cgroups/fs/blkio.go +++ b/vendor/src/github.com/docker/libcontainer/cgroups/fs/blkio.go @@ -9,17 +9,32 @@ import ( "strings" "github.com/docker/libcontainer/cgroups" + "github.com/docker/libcontainer/configs" ) type BlkioGroup struct { } -func (s *BlkioGroup) Set(d *data) error { - // we just want to join this group even though we don't set anything - if _, err := d.join("blkio"); err != nil && !cgroups.IsNotFound(err) { +func (s *BlkioGroup) Apply(d *data) error { + dir, err := d.join("blkio") + if err != nil && !cgroups.IsNotFound(err) { return err } + if err := s.Set(dir, d.c); err != nil { + return err + } + + return nil +} + +func (s *BlkioGroup) Set(path string, cgroup *configs.Cgroup) error { + if cgroup.BlkioWeight != 0 { + if err := writeFile(path, "blkio.weight", strconv.FormatInt(cgroup.BlkioWeight, 10)); err != nil { + return err + } + } + return nil } diff --git a/vendor/src/github.com/docker/libcontainer/cgroups/fs/blkio_test.go b/vendor/src/github.com/docker/libcontainer/cgroups/fs/blkio_test.go index 6cd38cbaba..9ef93fcff2 100644 --- a/vendor/src/github.com/docker/libcontainer/cgroups/fs/blkio_test.go +++ b/vendor/src/github.com/docker/libcontainer/cgroups/fs/blkio_test.go @@ -1,6 +1,7 @@ package fs import ( + "strconv" "testing" "github.com/docker/libcontainer/cgroups" @@ -72,6 +73,35 @@ func appendBlkioStatEntry(blkioStatEntries *[]cgroups.BlkioStatEntry, major, min *blkioStatEntries = append(*blkioStatEntries, cgroups.BlkioStatEntry{Major: major, Minor: minor, Value: value, Op: op}) } +func TestBlkioSetWeight(t *testing.T) { + helper := NewCgroupTestUtil("blkio", t) + defer helper.cleanup() + + const ( + weightBefore = 100 + weightAfter = 200 + ) + + helper.writeFileContents(map[string]string{ + "blkio.weight": strconv.Itoa(weightBefore), + }) + + helper.CgroupData.c.BlkioWeight = weightAfter + blkio := &BlkioGroup{} + if err := blkio.Set(helper.CgroupPath, helper.CgroupData.c); err != nil { + t.Fatal(err) + } + + value, err := getCgroupParamUint(helper.CgroupPath, "blkio.weight") + if err != nil { + t.Fatalf("Failed to parse blkio.weight - %s", err) + } + + if value != weightAfter { + t.Fatal("Got the wrong value, set blkio.weight failed.") + } +} + func TestBlkioStats(t *testing.T) { helper := NewCgroupTestUtil("blkio", t) defer helper.cleanup() diff --git a/vendor/src/github.com/docker/libcontainer/cgroups/fs/cpu.go b/vendor/src/github.com/docker/libcontainer/cgroups/fs/cpu.go index efac9ed16a..1fbf7b1540 100644 --- a/vendor/src/github.com/docker/libcontainer/cgroups/fs/cpu.go +++ b/vendor/src/github.com/docker/libcontainer/cgroups/fs/cpu.go @@ -7,33 +7,44 @@ import ( "strconv" "github.com/docker/libcontainer/cgroups" + "github.com/docker/libcontainer/configs" ) type CpuGroup struct { } -func (s *CpuGroup) Set(d *data) error { +func (s *CpuGroup) Apply(d *data) error { // We always want to join the cpu group, to allow fair cpu scheduling // on a container basis dir, err := d.join("cpu") if err != nil { return err } - if d.c.CpuShares != 0 { - if err := writeFile(dir, "cpu.shares", strconv.FormatInt(d.c.CpuShares, 10)); err != nil { + + if err := s.Set(dir, d.c); err != nil { + return err + } + + return nil +} + +func (s *CpuGroup) Set(path string, cgroup *configs.Cgroup) error { + if cgroup.CpuShares != 0 { + if err := writeFile(path, "cpu.shares", strconv.FormatInt(cgroup.CpuShares, 10)); err != nil { return err } } - if d.c.CpuPeriod != 0 { - if err := writeFile(dir, "cpu.cfs_period_us", strconv.FormatInt(d.c.CpuPeriod, 10)); err != nil { + if cgroup.CpuPeriod != 0 { + if err := writeFile(path, "cpu.cfs_period_us", strconv.FormatInt(cgroup.CpuPeriod, 10)); err != nil { return err } } - if d.c.CpuQuota != 0 { - if err := writeFile(dir, "cpu.cfs_quota_us", strconv.FormatInt(d.c.CpuQuota, 10)); err != nil { + if cgroup.CpuQuota != 0 { + if err := writeFile(path, "cpu.cfs_quota_us", strconv.FormatInt(cgroup.CpuQuota, 10)); err != nil { return err } } + return nil } diff --git a/vendor/src/github.com/docker/libcontainer/cgroups/fs/cpu_test.go b/vendor/src/github.com/docker/libcontainer/cgroups/fs/cpu_test.go index 2470e68956..bcf4ac4e8a 100644 --- a/vendor/src/github.com/docker/libcontainer/cgroups/fs/cpu_test.go +++ b/vendor/src/github.com/docker/libcontainer/cgroups/fs/cpu_test.go @@ -2,11 +2,81 @@ package fs import ( "fmt" + "strconv" "testing" "github.com/docker/libcontainer/cgroups" ) +func TestCpuSetShares(t *testing.T) { + helper := NewCgroupTestUtil("cpu", t) + defer helper.cleanup() + + const ( + sharesBefore = 1024 + sharesAfter = 512 + ) + + helper.writeFileContents(map[string]string{ + "cpu.shares": strconv.Itoa(sharesBefore), + }) + + helper.CgroupData.c.CpuShares = sharesAfter + cpu := &CpuGroup{} + if err := cpu.Set(helper.CgroupPath, helper.CgroupData.c); err != nil { + t.Fatal(err) + } + + value, err := getCgroupParamUint(helper.CgroupPath, "cpu.shares") + if err != nil { + t.Fatalf("Failed to parse cpu.shares - %s", err) + } + + if value != sharesAfter { + t.Fatal("Got the wrong value, set cpu.shares failed.") + } +} + +func TestCpuSetBandWidth(t *testing.T) { + helper := NewCgroupTestUtil("cpu", t) + defer helper.cleanup() + + const ( + quotaBefore = 8000 + quotaAfter = 5000 + periodBefore = 10000 + periodAfter = 7000 + ) + + helper.writeFileContents(map[string]string{ + "cpu.cfs_quota_us": strconv.Itoa(quotaBefore), + "cpu.cfs_period_us": strconv.Itoa(periodBefore), + }) + + helper.CgroupData.c.CpuQuota = quotaAfter + helper.CgroupData.c.CpuPeriod = periodAfter + cpu := &CpuGroup{} + if err := cpu.Set(helper.CgroupPath, helper.CgroupData.c); err != nil { + t.Fatal(err) + } + + quota, err := getCgroupParamUint(helper.CgroupPath, "cpu.cfs_quota_us") + if err != nil { + t.Fatalf("Failed to parse cpu.cfs_quota_us - %s", err) + } + if quota != quotaAfter { + t.Fatal("Got the wrong value, set cpu.cfs_quota_us failed.") + } + + period, err := getCgroupParamUint(helper.CgroupPath, "cpu.cfs_period_us") + if err != nil { + t.Fatalf("Failed to parse cpu.cfs_period_us - %s", err) + } + if period != periodAfter { + t.Fatal("Got the wrong value, set cpu.cfs_period_us failed.") + } +} + func TestCpuStats(t *testing.T) { helper := NewCgroupTestUtil("cpu", t) defer helper.cleanup() diff --git a/vendor/src/github.com/docker/libcontainer/cgroups/fs/cpuacct.go b/vendor/src/github.com/docker/libcontainer/cgroups/fs/cpuacct.go index 14b55ccd4e..decee85094 100644 --- a/vendor/src/github.com/docker/libcontainer/cgroups/fs/cpuacct.go +++ b/vendor/src/github.com/docker/libcontainer/cgroups/fs/cpuacct.go @@ -8,6 +8,7 @@ import ( "strings" "github.com/docker/libcontainer/cgroups" + "github.com/docker/libcontainer/configs" "github.com/docker/libcontainer/system" ) @@ -21,7 +22,7 @@ var clockTicks = uint64(system.GetClockTicks()) type CpuacctGroup struct { } -func (s *CpuacctGroup) Set(d *data) error { +func (s *CpuacctGroup) Apply(d *data) error { // we just want to join this group even though we don't set anything if _, err := d.join("cpuacct"); err != nil && !cgroups.IsNotFound(err) { return err @@ -30,6 +31,10 @@ func (s *CpuacctGroup) Set(d *data) error { return nil } +func (s *CpuacctGroup) Set(path string, cgroup *configs.Cgroup) error { + return nil +} + func (s *CpuacctGroup) Remove(d *data) error { return removePath(d.path("cpuacct")) } diff --git a/vendor/src/github.com/docker/libcontainer/cgroups/fs/cpuset.go b/vendor/src/github.com/docker/libcontainer/cgroups/fs/cpuset.go index ff67a53e87..3ed2f67d59 100644 --- a/vendor/src/github.com/docker/libcontainer/cgroups/fs/cpuset.go +++ b/vendor/src/github.com/docker/libcontainer/cgroups/fs/cpuset.go @@ -8,17 +8,34 @@ import ( "strconv" "github.com/docker/libcontainer/cgroups" + "github.com/docker/libcontainer/configs" ) type CpusetGroup struct { } -func (s *CpusetGroup) Set(d *data) error { +func (s *CpusetGroup) Apply(d *data) error { dir, err := d.path("cpuset") if err != nil { return err } - return s.SetDir(dir, d.c.CpusetCpus, d.c.CpusetMems, d.pid) + return s.ApplyDir(dir, d.c, d.pid) +} + +func (s *CpusetGroup) Set(path string, cgroup *configs.Cgroup) error { + if cgroup.CpusetCpus != "" { + if err := writeFile(path, "cpuset.cpus", cgroup.CpusetCpus); err != nil { + return err + } + } + + if cgroup.CpusetMems != "" { + if err := writeFile(path, "cpuset.mems", cgroup.CpusetMems); err != nil { + return err + } + } + + return nil } func (s *CpusetGroup) Remove(d *data) error { @@ -29,7 +46,7 @@ func (s *CpusetGroup) GetStats(path string, stats *cgroups.Stats) error { return nil } -func (s *CpusetGroup) SetDir(dir, cpus string, mems string, pid int) error { +func (s *CpusetGroup) ApplyDir(dir string, cgroup *configs.Cgroup, pid int) error { if err := s.ensureParent(dir); err != nil { return err } @@ -40,17 +57,10 @@ func (s *CpusetGroup) SetDir(dir, cpus string, mems string, pid int) error { return err } - // If we don't use --cpuset-xxx, the default value inherit from parent cgroup - // is set in s.ensureParent, otherwise, use the value we set - if cpus != "" { - if err := writeFile(dir, "cpuset.cpus", cpus); err != nil { - return err - } - } - if mems != "" { - if err := writeFile(dir, "cpuset.mems", mems); err != nil { - return err - } + // the default values inherit from parent cgroup are already set in + // s.ensureParent, cover these if we have our own + if err := s.Set(dir, cgroup); err != nil { + return err } return nil diff --git a/vendor/src/github.com/docker/libcontainer/cgroups/fs/cpuset_test.go b/vendor/src/github.com/docker/libcontainer/cgroups/fs/cpuset_test.go new file mode 100644 index 0000000000..7449cdca17 --- /dev/null +++ b/vendor/src/github.com/docker/libcontainer/cgroups/fs/cpuset_test.go @@ -0,0 +1,63 @@ +package fs + +import ( + "testing" +) + +func TestCpusetSetCpus(t *testing.T) { + helper := NewCgroupTestUtil("cpuset", t) + defer helper.cleanup() + + const ( + cpusBefore = "0" + cpusAfter = "1-3" + ) + + helper.writeFileContents(map[string]string{ + "cpuset.cpus": cpusBefore, + }) + + helper.CgroupData.c.CpusetCpus = cpusAfter + cpuset := &CpusetGroup{} + if err := cpuset.Set(helper.CgroupPath, helper.CgroupData.c); err != nil { + t.Fatal(err) + } + + value, err := getCgroupParamString(helper.CgroupPath, "cpuset.cpus") + if err != nil { + t.Fatalf("Failed to parse cpuset.cpus - %s", err) + } + + if value != cpusAfter { + t.Fatal("Got the wrong value, set cpuset.cpus failed.") + } +} + +func TestCpusetSetMems(t *testing.T) { + helper := NewCgroupTestUtil("cpuset", t) + defer helper.cleanup() + + const ( + memsBefore = "0" + memsAfter = "1" + ) + + helper.writeFileContents(map[string]string{ + "cpuset.mems": memsBefore, + }) + + helper.CgroupData.c.CpusetMems = memsAfter + cpuset := &CpusetGroup{} + if err := cpuset.Set(helper.CgroupPath, helper.CgroupData.c); err != nil { + t.Fatal(err) + } + + value, err := getCgroupParamString(helper.CgroupPath, "cpuset.mems") + if err != nil { + t.Fatalf("Failed to parse cpuset.mems - %s", err) + } + + if value != memsAfter { + t.Fatal("Got the wrong value, set cpuset.mems failed.") + } +} diff --git a/vendor/src/github.com/docker/libcontainer/cgroups/fs/devices.go b/vendor/src/github.com/docker/libcontainer/cgroups/fs/devices.go index 98d5d2d7dd..16e00b1c73 100644 --- a/vendor/src/github.com/docker/libcontainer/cgroups/fs/devices.go +++ b/vendor/src/github.com/docker/libcontainer/cgroups/fs/devices.go @@ -1,27 +1,39 @@ package fs -import "github.com/docker/libcontainer/cgroups" +import ( + "github.com/docker/libcontainer/cgroups" + "github.com/docker/libcontainer/configs" +) type DevicesGroup struct { } -func (s *DevicesGroup) Set(d *data) error { +func (s *DevicesGroup) Apply(d *data) error { dir, err := d.join("devices") if err != nil { return err } - if !d.c.AllowAllDevices { - if err := writeFile(dir, "devices.deny", "a"); err != nil { + if err := s.Set(dir, d.c); err != nil { + return err + } + + return nil +} + +func (s *DevicesGroup) Set(path string, cgroup *configs.Cgroup) error { + if !cgroup.AllowAllDevices { + if err := writeFile(path, "devices.deny", "a"); err != nil { return err } - for _, dev := range d.c.AllowedDevices { - if err := writeFile(dir, "devices.allow", dev.GetCgroupAllowString()); err != nil { + for _, dev := range cgroup.AllowedDevices { + if err := writeFile(path, "devices.allow", dev.CgroupString()); err != nil { return err } } } + return nil } diff --git a/vendor/src/github.com/docker/libcontainer/cgroups/fs/devices_test.go b/vendor/src/github.com/docker/libcontainer/cgroups/fs/devices_test.go new file mode 100644 index 0000000000..d87d092421 --- /dev/null +++ b/vendor/src/github.com/docker/libcontainer/cgroups/fs/devices_test.go @@ -0,0 +1,48 @@ +package fs + +import ( + "testing" + + "github.com/docker/libcontainer/configs" +) + +var ( + allowedDevices = []*configs.Device{ + { + Path: "/dev/zero", + Type: 'c', + Major: 1, + Minor: 5, + Permissions: "rwm", + FileMode: 0666, + }, + } + allowedList = "c 1:5 rwm" +) + +func TestDevicesSetAllow(t *testing.T) { + helper := NewCgroupTestUtil("devices", t) + defer helper.cleanup() + + helper.writeFileContents(map[string]string{ + "device.deny": "a", + }) + + helper.CgroupData.c.AllowAllDevices = false + helper.CgroupData.c.AllowedDevices = allowedDevices + devices := &DevicesGroup{} + if err := devices.Set(helper.CgroupPath, helper.CgroupData.c); err != nil { + t.Fatal(err) + } + + // FIXME: this doesn't make sence, the file devices.allow under real cgroupfs + // is not allowed to read. Our test path don't have cgroupfs mounted. + value, err := getCgroupParamString(helper.CgroupPath, "devices.allow") + if err != nil { + t.Fatalf("Failed to parse devices.allow - %s", err) + } + + if value != allowedList { + t.Fatal("Got the wrong value, set devices.allow failed.") + } +} diff --git a/vendor/src/github.com/docker/libcontainer/cgroups/fs/freezer.go b/vendor/src/github.com/docker/libcontainer/cgroups/fs/freezer.go index c6b677fa95..fc8241d1bf 100644 --- a/vendor/src/github.com/docker/libcontainer/cgroups/fs/freezer.go +++ b/vendor/src/github.com/docker/libcontainer/cgroups/fs/freezer.go @@ -5,37 +5,42 @@ import ( "time" "github.com/docker/libcontainer/cgroups" + "github.com/docker/libcontainer/configs" ) type FreezerGroup struct { } -func (s *FreezerGroup) Set(d *data) error { - switch d.c.Freezer { - case cgroups.Frozen, cgroups.Thawed: - dir, err := d.path("freezer") - if err != nil { - return err - } +func (s *FreezerGroup) Apply(d *data) error { + dir, err := d.join("freezer") + if err != nil && !cgroups.IsNotFound(err) { + return err + } - if err := writeFile(dir, "freezer.state", string(d.c.Freezer)); err != nil { + if err := s.Set(dir, d.c); err != nil { + return err + } + + return nil +} + +func (s *FreezerGroup) Set(path string, cgroup *configs.Cgroup) error { + switch cgroup.Freezer { + case configs.Frozen, configs.Thawed: + if err := writeFile(path, "freezer.state", string(cgroup.Freezer)); err != nil { return err } for { - state, err := readFile(dir, "freezer.state") + state, err := readFile(path, "freezer.state") if err != nil { return err } - if strings.TrimSpace(state) == string(d.c.Freezer) { + if strings.TrimSpace(state) == string(cgroup.Freezer) { break } time.Sleep(1 * time.Millisecond) } - default: - if _, err := d.join("freezer"); err != nil && !cgroups.IsNotFound(err) { - return err - } } return nil diff --git a/vendor/src/github.com/docker/libcontainer/cgroups/fs/memory.go b/vendor/src/github.com/docker/libcontainer/cgroups/fs/memory.go index 01713fd790..b99f81687a 100644 --- a/vendor/src/github.com/docker/libcontainer/cgroups/fs/memory.go +++ b/vendor/src/github.com/docker/libcontainer/cgroups/fs/memory.go @@ -8,12 +8,13 @@ import ( "strconv" "github.com/docker/libcontainer/cgroups" + "github.com/docker/libcontainer/configs" ) type MemoryGroup struct { } -func (s *MemoryGroup) Set(d *data) error { +func (s *MemoryGroup) Apply(d *data) error { dir, err := d.join("memory") // only return an error for memory if it was specified if err != nil && (d.c.Memory != 0 || d.c.MemoryReservation != 0 || d.c.MemorySwap != 0) { @@ -25,31 +26,42 @@ func (s *MemoryGroup) Set(d *data) error { } }() - // Only set values if some config was specified. - if d.c.Memory != 0 || d.c.MemoryReservation != 0 || d.c.MemorySwap != 0 { - if d.c.Memory != 0 { - if err := writeFile(dir, "memory.limit_in_bytes", strconv.FormatInt(d.c.Memory, 10)); err != nil { - return err - } - } - if d.c.MemoryReservation != 0 { - if err := writeFile(dir, "memory.soft_limit_in_bytes", strconv.FormatInt(d.c.MemoryReservation, 10)); err != nil { - return err - } - } - // By default, MemorySwap is set to twice the size of RAM. - // If you want to omit MemorySwap, set it to '-1'. - if d.c.MemorySwap == 0 { - if err := writeFile(dir, "memory.memsw.limit_in_bytes", strconv.FormatInt(d.c.Memory*2, 10)); err != nil { - return err - } - } - if d.c.MemorySwap > 0 { - if err := writeFile(dir, "memory.memsw.limit_in_bytes", strconv.FormatInt(d.c.MemorySwap, 10)); err != nil { - return err - } + if err := s.Set(dir, d.c); err != nil { + return err + } + + return nil +} + +func (s *MemoryGroup) Set(path string, cgroup *configs.Cgroup) error { + if cgroup.Memory != 0 { + if err := writeFile(path, "memory.limit_in_bytes", strconv.FormatInt(cgroup.Memory, 10)); err != nil { + return err } } + if cgroup.MemoryReservation != 0 { + if err := writeFile(path, "memory.soft_limit_in_bytes", strconv.FormatInt(cgroup.MemoryReservation, 10)); err != nil { + return err + } + } + // By default, MemorySwap is set to twice the size of Memory. + if cgroup.MemorySwap == 0 && cgroup.Memory != 0 { + if err := writeFile(path, "memory.memsw.limit_in_bytes", strconv.FormatInt(cgroup.Memory*2, 10)); err != nil { + return err + } + } + if cgroup.MemorySwap > 0 { + if err := writeFile(path, "memory.memsw.limit_in_bytes", strconv.FormatInt(cgroup.MemorySwap, 10)); err != nil { + return err + } + } + + if cgroup.OomKillDisable { + if err := writeFile(path, "memory.oom_control", "1"); err != nil { + return err + } + } + return nil } diff --git a/vendor/src/github.com/docker/libcontainer/cgroups/fs/memory_test.go b/vendor/src/github.com/docker/libcontainer/cgroups/fs/memory_test.go index a21cec75c0..f1c7dda8d8 100644 --- a/vendor/src/github.com/docker/libcontainer/cgroups/fs/memory_test.go +++ b/vendor/src/github.com/docker/libcontainer/cgroups/fs/memory_test.go @@ -1,6 +1,7 @@ package fs import ( + "strconv" "testing" "github.com/docker/libcontainer/cgroups" @@ -14,6 +15,46 @@ rss 1024` memoryFailcnt = "100\n" ) +func TestMemorySetMemory(t *testing.T) { + helper := NewCgroupTestUtil("memory", t) + defer helper.cleanup() + + const ( + memoryBefore = 314572800 // 300M + memoryAfter = 524288000 // 500M + reservationBefore = 209715200 // 200M + reservationAfter = 314572800 // 300M + ) + + helper.writeFileContents(map[string]string{ + "memory.limit_in_bytes": strconv.Itoa(memoryBefore), + "memory.soft_limit_in_bytes": strconv.Itoa(reservationBefore), + }) + + helper.CgroupData.c.Memory = memoryAfter + helper.CgroupData.c.MemoryReservation = reservationAfter + memory := &MemoryGroup{} + if err := memory.Set(helper.CgroupPath, helper.CgroupData.c); err != nil { + t.Fatal(err) + } + + value, err := getCgroupParamUint(helper.CgroupPath, "memory.limit_in_bytes") + if err != nil { + t.Fatalf("Failed to parse memory.limit_in_bytes - %s", err) + } + if value != memoryAfter { + t.Fatal("Got the wrong value, set memory.limit_in_bytes failed.") + } + + value, err = getCgroupParamUint(helper.CgroupPath, "memory.soft_limit_in_bytes") + if err != nil { + t.Fatalf("Failed to parse memory.soft_limit_in_bytes - %s", err) + } + if value != reservationAfter { + t.Fatal("Got the wrong value, set memory.soft_limit_in_bytes failed.") + } +} + func TestMemoryStats(t *testing.T) { helper := NewCgroupTestUtil("memory", t) defer helper.cleanup() @@ -132,3 +173,30 @@ func TestMemoryStatsBadMaxUsageFile(t *testing.T) { t.Fatal("Expected failure") } } + +func TestMemorySetOomControl(t *testing.T) { + helper := NewCgroupTestUtil("memory", t) + defer helper.cleanup() + + const ( + oom_kill_disable = 1 // disable oom killer, default is 0 + ) + + helper.writeFileContents(map[string]string{ + "memory.oom_control": strconv.Itoa(oom_kill_disable), + }) + + memory := &MemoryGroup{} + if err := memory.Set(helper.CgroupPath, helper.CgroupData.c); err != nil { + t.Fatal(err) + } + + value, err := getCgroupParamUint(helper.CgroupPath, "memory.oom_control") + if err != nil { + t.Fatalf("Failed to parse memory.oom_control - %s", err) + } + + if value != oom_kill_disable { + t.Fatalf("Got the wrong value, set memory.oom_control failed.") + } +} diff --git a/vendor/src/github.com/docker/libcontainer/cgroups/fs/perf_event.go b/vendor/src/github.com/docker/libcontainer/cgroups/fs/perf_event.go index 813274d8cb..ca65f734a1 100644 --- a/vendor/src/github.com/docker/libcontainer/cgroups/fs/perf_event.go +++ b/vendor/src/github.com/docker/libcontainer/cgroups/fs/perf_event.go @@ -2,12 +2,13 @@ package fs import ( "github.com/docker/libcontainer/cgroups" + "github.com/docker/libcontainer/configs" ) type PerfEventGroup struct { } -func (s *PerfEventGroup) Set(d *data) error { +func (s *PerfEventGroup) Apply(d *data) error { // we just want to join this group even though we don't set anything if _, err := d.join("perf_event"); err != nil && !cgroups.IsNotFound(err) { return err @@ -15,6 +16,10 @@ func (s *PerfEventGroup) Set(d *data) error { return nil } +func (s *PerfEventGroup) Set(path string, cgroup *configs.Cgroup) error { + return nil +} + func (s *PerfEventGroup) Remove(d *data) error { return removePath(d.path("perf_event")) } diff --git a/vendor/src/github.com/docker/libcontainer/cgroups/fs/util_test.go b/vendor/src/github.com/docker/libcontainer/cgroups/fs/util_test.go index 548870a8a3..e0c1262db2 100644 --- a/vendor/src/github.com/docker/libcontainer/cgroups/fs/util_test.go +++ b/vendor/src/github.com/docker/libcontainer/cgroups/fs/util_test.go @@ -10,6 +10,8 @@ import ( "io/ioutil" "os" "testing" + + "github.com/docker/libcontainer/configs" ) type cgroupTestUtil struct { @@ -26,7 +28,9 @@ type cgroupTestUtil struct { // Creates a new test util for the specified subsystem func NewCgroupTestUtil(subsystem string, t *testing.T) *cgroupTestUtil { - d := &data{} + d := &data{ + c: &configs.Cgroup{}, + } tempDir, err := ioutil.TempDir("", fmt.Sprintf("%s_cgroup_test", subsystem)) if err != nil { t.Fatal(err) diff --git a/vendor/src/github.com/docker/libcontainer/cgroups/fs/utils.go b/vendor/src/github.com/docker/libcontainer/cgroups/fs/utils.go index f37a3a485a..c2f75c8e54 100644 --- a/vendor/src/github.com/docker/libcontainer/cgroups/fs/utils.go +++ b/vendor/src/github.com/docker/libcontainer/cgroups/fs/utils.go @@ -60,3 +60,13 @@ func getCgroupParamUint(cgroupPath, cgroupFile string) (uint64, error) { return parseUint(strings.TrimSpace(string(contents)), 10, 64) } + +// Gets a string value from the specified cgroup file +func getCgroupParamString(cgroupPath, cgroupFile string) (string, error) { + contents, err := ioutil.ReadFile(filepath.Join(cgroupPath, cgroupFile)) + if err != nil { + return "", err + } + + return strings.TrimSpace(string(contents)), nil +} diff --git a/vendor/src/github.com/docker/libcontainer/cgroups/systemd/apply_nosystemd.go b/vendor/src/github.com/docker/libcontainer/cgroups/systemd/apply_nosystemd.go index 4b9a2f5b74..95ed4ea7eb 100644 --- a/vendor/src/github.com/docker/libcontainer/cgroups/systemd/apply_nosystemd.go +++ b/vendor/src/github.com/docker/libcontainer/cgroups/systemd/apply_nosystemd.go @@ -6,24 +6,50 @@ import ( "fmt" "github.com/docker/libcontainer/cgroups" + "github.com/docker/libcontainer/configs" ) +type Manager struct { + Cgroups *configs.Cgroup + Paths map[string]string +} + func UseSystemd() bool { return false } -func Apply(c *cgroups.Cgroup, pid int) (map[string]string, error) { - return nil, fmt.Errorf("Systemd not supported") -} - -func GetPids(c *cgroups.Cgroup) ([]int, error) { - return nil, fmt.Errorf("Systemd not supported") -} - -func ApplyDevices(c *cgroups.Cgroup, pid int) error { +func (m *Manager) Apply(pid int) error { return fmt.Errorf("Systemd not supported") } -func Freeze(c *cgroups.Cgroup, state cgroups.FreezerState) error { +func (m *Manager) GetPids() ([]int, error) { + return nil, fmt.Errorf("Systemd not supported") +} + +func (m *Manager) Destroy() error { + return fmt.Errorf("Systemd not supported") +} + +func (m *Manager) GetPaths() map[string]string { + return nil +} + +func (m *Manager) GetStats() (*cgroups.Stats, error) { + return nil, fmt.Errorf("Systemd not supported") +} + +func (m *Manager) Set(container *configs.Config) error { + return nil, fmt.Errorf("Systemd not supported") +} + +func (m *Manager) Freeze(state configs.FreezerState) error { + return fmt.Errorf("Systemd not supported") +} + +func ApplyDevices(c *configs.Cgroup, pid int) error { + return fmt.Errorf("Systemd not supported") +} + +func Freeze(c *configs.Cgroup, state configs.FreezerState) error { return fmt.Errorf("Systemd not supported") } diff --git a/vendor/src/github.com/docker/libcontainer/cgroups/systemd/apply_systemd.go b/vendor/src/github.com/docker/libcontainer/cgroups/systemd/apply_systemd.go index 41dce3117d..f4358e1a64 100644 --- a/vendor/src/github.com/docker/libcontainer/cgroups/systemd/apply_systemd.go +++ b/vendor/src/github.com/docker/libcontainer/cgroups/systemd/apply_systemd.go @@ -16,21 +16,38 @@ import ( systemd "github.com/coreos/go-systemd/dbus" "github.com/docker/libcontainer/cgroups" "github.com/docker/libcontainer/cgroups/fs" + "github.com/docker/libcontainer/configs" "github.com/godbus/dbus" ) -type systemdCgroup struct { - cgroup *cgroups.Cgroup +type Manager struct { + Cgroups *configs.Cgroup + Paths map[string]string } type subsystem interface { - GetStats(string, *cgroups.Stats) error + // Returns the stats, as 'stats', corresponding to the cgroup under 'path'. + GetStats(path string, stats *cgroups.Stats) error + // Set the cgroup represented by cgroup. + Set(path string, cgroup *configs.Cgroup) error +} + +var subsystems = map[string]subsystem{ + "devices": &fs.DevicesGroup{}, + "memory": &fs.MemoryGroup{}, + "cpu": &fs.CpuGroup{}, + "cpuset": &fs.CpusetGroup{}, + "cpuacct": &fs.CpuacctGroup{}, + "blkio": &fs.BlkioGroup{}, + "perf_event": &fs.PerfEventGroup{}, + "freezer": &fs.FreezerGroup{}, } var ( - connLock sync.Mutex - theConn *systemd.Conn - hasStartTransientUnit bool + connLock sync.Mutex + theConn *systemd.Conn + hasStartTransientUnit bool + hasTransientDefaultDependencies bool ) func newProp(name string, units interface{}) systemd.Property { @@ -64,6 +81,18 @@ func UseSystemd() bool { if dbusError, ok := err.(dbus.Error); ok { if dbusError.Name == "org.freedesktop.DBus.Error.UnknownMethod" { hasStartTransientUnit = false + return hasStartTransientUnit + } + } + } + + // Assume StartTransientUnit on a scope allows DefaultDependencies + hasTransientDefaultDependencies = true + ddf := newProp("DefaultDependencies", false) + if _, err := theConn.StartTransientUnit("docker-systemd-test-default-dependencies.scope", "replace", ddf); err != nil { + if dbusError, ok := err.(dbus.Error); ok { + if dbusError.Name == "org.freedesktop.DBus.Error.PropertyReadOnly" { + hasTransientDefaultDependencies = false } } } @@ -81,16 +110,14 @@ func getIfaceForUnit(unitName string) string { return "Unit" } -func Apply(c *cgroups.Cgroup, pid int) (map[string]string, error) { +func (m *Manager) Apply(pid int) error { var ( + c = m.Cgroups unitName = getUnitName(c) slice = "system.slice" properties []systemd.Property - res = &systemdCgroup{} ) - res.cgroup = c - if c.Slice != "" { slice = c.Slice } @@ -108,6 +135,11 @@ func Apply(c *cgroups.Cgroup, pid int) (map[string]string, error) { newProp("CPUAccounting", true), newProp("BlockIOAccounting", true)) + if hasTransientDefaultDependencies { + properties = append(properties, + newProp("DefaultDependencies", false)) + } + if c.Memory != 0 { properties = append(properties, newProp("MemoryLimit", uint64(c.Memory))) @@ -119,20 +151,29 @@ func Apply(c *cgroups.Cgroup, pid int) (map[string]string, error) { newProp("CPUShares", uint64(c.CpuShares))) } - if _, err := theConn.StartTransientUnit(unitName, "replace", properties...); err != nil { - return nil, err + if c.BlkioWeight != 0 { + properties = append(properties, + newProp("BlockIOWeight", uint64(c.BlkioWeight))) } - if !c.AllowAllDevices { - if err := joinDevices(c, pid); err != nil { - return nil, err - } + if _, err := theConn.StartTransientUnit(unitName, "replace", properties...); err != nil { + return err + } + + if err := joinDevices(c, pid); err != nil { + return err + } + + // TODO: CpuQuota and CpuPeriod not available in systemd + // we need to manually join the cpu.cfs_quota_us and cpu.cfs_period_us + if err := joinCpu(c, pid); err != nil { + return err } // -1 disables memorySwap - if c.MemorySwap >= 0 && (c.Memory != 0 || c.MemorySwap > 0) { + if c.MemorySwap >= 0 && c.Memory != 0 { if err := joinMemory(c, pid); err != nil { - return nil, err + return err } } @@ -140,11 +181,11 @@ func Apply(c *cgroups.Cgroup, pid int) (map[string]string, error) { // we need to manually join the freezer and cpuset cgroup in systemd // because it does not currently support it via the dbus api. if err := joinFreezer(c, pid); err != nil { - return nil, err + return err } if err := joinCpuset(c, pid); err != nil { - return nil, err + return err } paths := make(map[string]string) @@ -158,24 +199,53 @@ func Apply(c *cgroups.Cgroup, pid int) (map[string]string, error) { "perf_event", "freezer", } { - subsystemPath, err := getSubsystemPath(res.cgroup, sysname) + subsystemPath, err := getSubsystemPath(m.Cgroups, sysname) if err != nil { // Don't fail if a cgroup hierarchy was not found, just skip this subsystem if cgroups.IsNotFound(err) { continue } - return nil, err + return err } paths[sysname] = subsystemPath } - return paths, nil + + m.Paths = paths + + return nil +} + +func (m *Manager) Destroy() error { + return cgroups.RemovePaths(m.Paths) +} + +func (m *Manager) GetPaths() map[string]string { + return m.Paths } func writeFile(dir, file, data string) error { return ioutil.WriteFile(filepath.Join(dir, file), []byte(data), 0700) } -func joinFreezer(c *cgroups.Cgroup, pid int) error { +func joinCpu(c *configs.Cgroup, pid int) error { + path, err := getSubsystemPath(c, "cpu") + if err != nil { + return err + } + if c.CpuQuota != 0 { + if err = ioutil.WriteFile(filepath.Join(path, "cpu.cfs_quota_us"), []byte(strconv.FormatInt(c.CpuQuota, 10)), 0700); err != nil { + return err + } + } + if c.CpuPeriod != 0 { + if err = ioutil.WriteFile(filepath.Join(path, "cpu.cfs_period_us"), []byte(strconv.FormatInt(c.CpuPeriod, 10)), 0700); err != nil { + return err + } + } + return nil +} + +func joinFreezer(c *configs.Cgroup, pid int) error { path, err := getSubsystemPath(c, "freezer") if err != nil { return err @@ -188,7 +258,7 @@ func joinFreezer(c *cgroups.Cgroup, pid int) error { return ioutil.WriteFile(filepath.Join(path, "cgroup.procs"), []byte(strconv.Itoa(pid)), 0700) } -func getSubsystemPath(c *cgroups.Cgroup, subsystem string) (string, error) { +func getSubsystemPath(c *configs.Cgroup, subsystem string) (string, error) { mountpoint, err := cgroups.FindCgroupMountpoint(subsystem) if err != nil { return "", err @@ -207,8 +277,8 @@ func getSubsystemPath(c *cgroups.Cgroup, subsystem string) (string, error) { return filepath.Join(mountpoint, initPath, slice, getUnitName(c)), nil } -func Freeze(c *cgroups.Cgroup, state cgroups.FreezerState) error { - path, err := getSubsystemPath(c, "freezer") +func (m *Manager) Freeze(state configs.FreezerState) error { + path, err := getSubsystemPath(m.Cgroups, "freezer") if err != nil { return err } @@ -226,11 +296,14 @@ func Freeze(c *cgroups.Cgroup, state cgroups.FreezerState) error { } time.Sleep(1 * time.Millisecond) } + + m.Cgroups.Freezer = state + return nil } -func GetPids(c *cgroups.Cgroup) ([]int, error) { - path, err := getSubsystemPath(c, "cpu") +func (m *Manager) GetPids() ([]int, error) { + path, err := getSubsystemPath(m.Cgroups, "cpu") if err != nil { return nil, err } @@ -238,7 +311,26 @@ func GetPids(c *cgroups.Cgroup) ([]int, error) { return cgroups.ReadProcsFile(path) } -func getUnitName(c *cgroups.Cgroup) string { +func (m *Manager) GetStats() (*cgroups.Stats, error) { + stats := cgroups.NewStats() + for name, path := range m.Paths { + sys, ok := subsystems[name] + if !ok || !cgroups.PathExists(path) { + continue + } + if err := sys.GetStats(path, stats); err != nil { + return nil, err + } + } + + return stats, nil +} + +func (m *Manager) Set(container *configs.Config) error { + panic("not implemented") +} + +func getUnitName(c *configs.Cgroup) string { return fmt.Sprintf("%s-%s.scope", c.Parent, c.Name) } @@ -253,7 +345,7 @@ func getUnitName(c *cgroups.Cgroup) string { // Note: we can't use systemd to set up the initial limits, and then change the cgroup // because systemd will re-write the device settings if it needs to re-apply the cgroup context. // This happens at least for v208 when any sibling unit is started. -func joinDevices(c *cgroups.Cgroup, pid int) error { +func joinDevices(c *configs.Cgroup, pid int) error { path, err := getSubsystemPath(c, "devices") if err != nil { return err @@ -267,26 +359,26 @@ func joinDevices(c *cgroups.Cgroup, pid int) error { return err } - if err := writeFile(path, "devices.deny", "a"); err != nil { - return err - } - - for _, dev := range c.AllowedDevices { - if err := writeFile(path, "devices.allow", dev.GetCgroupAllowString()); err != nil { + if !c.AllowAllDevices { + if err := writeFile(path, "devices.deny", "a"); err != nil { + return err + } + } + for _, dev := range c.AllowedDevices { + if err := writeFile(path, "devices.allow", dev.CgroupString()); err != nil { return err } } - return nil } // Symmetrical public function to update device based cgroups. Also available // in the fs implementation. -func ApplyDevices(c *cgroups.Cgroup, pid int) error { +func ApplyDevices(c *configs.Cgroup, pid int) error { return joinDevices(c, pid) } -func joinMemory(c *cgroups.Cgroup, pid int) error { +func joinMemory(c *configs.Cgroup, pid int) error { memorySwap := c.MemorySwap if memorySwap == 0 { @@ -305,7 +397,7 @@ func joinMemory(c *cgroups.Cgroup, pid int) error { // systemd does not atm set up the cpuset controller, so we must manually // join it. Additionally that is a very finicky controller where each // level must have a full setup as the default for a new directory is "no cpus" -func joinCpuset(c *cgroups.Cgroup, pid int) error { +func joinCpuset(c *configs.Cgroup, pid int) error { path, err := getSubsystemPath(c, "cpuset") if err != nil { return err @@ -313,5 +405,5 @@ func joinCpuset(c *cgroups.Cgroup, pid int) error { s := &fs.CpusetGroup{} - return s.SetDir(path, c.CpusetCpus, c.CpusetMems, pid) + return s.ApplyDir(path, c, pid) } diff --git a/vendor/src/github.com/docker/libcontainer/config.go b/vendor/src/github.com/docker/libcontainer/config.go deleted file mode 100644 index 643601adac..0000000000 --- a/vendor/src/github.com/docker/libcontainer/config.go +++ /dev/null @@ -1,154 +0,0 @@ -package libcontainer - -import ( - "github.com/docker/libcontainer/cgroups" - "github.com/docker/libcontainer/mount" - "github.com/docker/libcontainer/network" -) - -type MountConfig mount.MountConfig - -type Network network.Network - -type NamespaceType string - -const ( - NEWNET NamespaceType = "NEWNET" - NEWPID NamespaceType = "NEWPID" - NEWNS NamespaceType = "NEWNS" - NEWUTS NamespaceType = "NEWUTS" - NEWIPC NamespaceType = "NEWIPC" - NEWUSER NamespaceType = "NEWUSER" -) - -// Namespace defines configuration for each namespace. It specifies an -// alternate path that is able to be joined via setns. -type Namespace struct { - Type NamespaceType `json:"type"` - Path string `json:"path,omitempty"` -} - -type Namespaces []Namespace - -func (n *Namespaces) Remove(t NamespaceType) bool { - i := n.index(t) - if i == -1 { - return false - } - *n = append((*n)[:i], (*n)[i+1:]...) - return true -} - -func (n *Namespaces) Add(t NamespaceType, path string) { - i := n.index(t) - if i == -1 { - *n = append(*n, Namespace{Type: t, Path: path}) - return - } - (*n)[i].Path = path -} - -func (n *Namespaces) index(t NamespaceType) int { - for i, ns := range *n { - if ns.Type == t { - return i - } - } - return -1 -} - -func (n *Namespaces) Contains(t NamespaceType) bool { - return n.index(t) != -1 -} - -// Config defines configuration options for executing a process inside a contained environment. -type Config struct { - // Mount specific options. - MountConfig *MountConfig `json:"mount_config,omitempty"` - - // Pathname to container's root filesystem - RootFs string `json:"root_fs,omitempty"` - - // Hostname optionally sets the container's hostname if provided - Hostname string `json:"hostname,omitempty"` - - // User will set the uid and gid of the executing process running inside the container - User string `json:"user,omitempty"` - - // WorkingDir will change the processes current working directory inside the container's rootfs - WorkingDir string `json:"working_dir,omitempty"` - - // Env will populate the processes environment with the provided values - // Any values from the parent processes will be cleared before the values - // provided in Env are provided to the process - Env []string `json:"environment,omitempty"` - - // Tty when true will allocate a pty slave on the host for access by the container's process - // and ensure that it is mounted inside the container's rootfs - Tty bool `json:"tty,omitempty"` - - // Namespaces specifies the container's namespaces that it should setup when cloning the init process - // If a namespace is not provided that namespace is shared from the container's parent process - Namespaces Namespaces `json:"namespaces,omitempty"` - - // Capabilities specify the capabilities to keep when executing the process inside the container - // All capbilities not specified will be dropped from the processes capability mask - Capabilities []string `json:"capabilities,omitempty"` - - // Networks specifies the container's network setup to be created - Networks []*Network `json:"networks,omitempty"` - - // Routes can be specified to create entries in the route table as the container is started - Routes []*Route `json:"routes,omitempty"` - - // Cgroups specifies specific cgroup settings for the various subsystems that the container is - // placed into to limit the resources the container has available - Cgroups *cgroups.Cgroup `json:"cgroups,omitempty"` - - // AppArmorProfile specifies the profile to apply to the process running in the container and is - // change at the time the process is execed - AppArmorProfile string `json:"apparmor_profile,omitempty"` - - // ProcessLabel specifies the label to apply to the process running in the container. It is - // commonly used by selinux - ProcessLabel string `json:"process_label,omitempty"` - - // RestrictSys will remount /proc/sys, /sys, and mask over sysrq-trigger as well as /proc/irq and - // /proc/bus - RestrictSys bool `json:"restrict_sys,omitempty"` - - // Rlimits specifies the resource limits, such as max open files, to set in the container - // If Rlimits are not set, the container will inherit rlimits from the parent process - Rlimits []Rlimit `json:"rlimits,omitempty"` - - // AdditionalGroups specifies the gids that should be added to supplementary groups - // in addition to those that the user belongs to. - AdditionalGroups []int `json:"additional_groups,omitempty"` -} - -// Routes can be specified to create entries in the route table as the container is started -// -// All of destination, source, and gateway should be either IPv4 or IPv6. -// One of the three options must be present, and ommitted entries will use their -// IP family default for the route table. For IPv4 for example, setting the -// gateway to 1.2.3.4 and the interface to eth0 will set up a standard -// destination of 0.0.0.0(or *) when viewed in the route table. -type Route struct { - // Sets the destination and mask, should be a CIDR. Accepts IPv4 and IPv6 - Destination string `json:"destination,omitempty"` - - // Sets the source and mask, should be a CIDR. Accepts IPv4 and IPv6 - Source string `json:"source,omitempty"` - - // Sets the gateway. Accepts IPv4 and IPv6 - Gateway string `json:"gateway,omitempty"` - - // The device to set this route up for, for example: eth0 - InterfaceName string `json:"interface_name,omitempty"` -} - -type Rlimit struct { - Type int `json:"type,omitempty"` - Hard uint64 `json:"hard,omitempty"` - Soft uint64 `json:"soft,omitempty"` -} diff --git a/vendor/src/github.com/docker/libcontainer/configs/cgroup.go b/vendor/src/github.com/docker/libcontainer/configs/cgroup.go new file mode 100644 index 0000000000..8bf174c195 --- /dev/null +++ b/vendor/src/github.com/docker/libcontainer/configs/cgroup.go @@ -0,0 +1,57 @@ +package configs + +type FreezerState string + +const ( + Undefined FreezerState = "" + Frozen FreezerState = "FROZEN" + Thawed FreezerState = "THAWED" +) + +type Cgroup struct { + Name string `json:"name"` + + // name of parent cgroup or slice + Parent string `json:"parent"` + + // If this is true allow access to any kind of device within the container. If false, allow access only to devices explicitly listed in the allowed_devices list. + AllowAllDevices bool `json:"allow_all_devices"` + + AllowedDevices []*Device `json:"allowed_devices"` + + // Memory limit (in bytes) + Memory int64 `json:"memory"` + + // Memory reservation or soft_limit (in bytes) + MemoryReservation int64 `json:"memory_reservation"` + + // Total memory usage (memory + swap); set `-1' to disable swap + MemorySwap int64 `json:"memory_swap"` + + // CPU shares (relative weight vs. other containers) + CpuShares int64 `json:"cpu_shares"` + + // CPU hardcap limit (in usecs). Allowed cpu time in a given period. + CpuQuota int64 `json:"cpu_quota"` + + // CPU period to be used for hardcapping (in usecs). 0 to use system default. + CpuPeriod int64 `json:"cpu_period"` + + // CPU to use + CpusetCpus string `json:"cpuset_cpus"` + + // MEM to use + CpusetMems string `json:"cpuset_mems"` + + // Specifies per cgroup weight, range is from 10 to 1000. + BlkioWeight int64 `json:"blkio_weight"` + + // set the freeze value for the process + Freezer FreezerState `json:"freezer"` + + // Parent slice to use for systemd TODO: remove in favor or parent + Slice string `json:"slice"` + + // Whether to disable OOM Killer + OomKillDisable bool `json:"oom_kill_disable"` +} diff --git a/vendor/src/github.com/docker/libcontainer/configs/config.go b/vendor/src/github.com/docker/libcontainer/configs/config.go new file mode 100644 index 0000000000..b07f252b5e --- /dev/null +++ b/vendor/src/github.com/docker/libcontainer/configs/config.go @@ -0,0 +1,145 @@ +package configs + +import "fmt" + +type Rlimit struct { + Type int `json:"type"` + Hard uint64 `json:"hard"` + Soft uint64 `json:"soft"` +} + +// IDMap represents UID/GID Mappings for User Namespaces. +type IDMap struct { + ContainerID int `json:"container_id"` + HostID int `json:"host_id"` + Size int `json:"size"` +} + +// Config defines configuration options for executing a process inside a contained environment. +type Config struct { + // NoPivotRoot will use MS_MOVE and a chroot to jail the process into the container's rootfs + // This is a common option when the container is running in ramdisk + NoPivotRoot bool `json:"no_pivot_root"` + + // ParentDeathSignal specifies the signal that is sent to the container's process in the case + // that the parent process dies. + ParentDeathSignal int `json:"parent_death_signal"` + + // PivotDir allows a custom directory inside the container's root filesystem to be used as pivot, when NoPivotRoot is not set. + // When a custom PivotDir not set, a temporary dir inside the root filesystem will be used. The pivot dir needs to be writeable. + // This is required when using read only root filesystems. In these cases, a read/writeable path can be (bind) mounted somewhere inside the root filesystem to act as pivot. + PivotDir string `json:"pivot_dir"` + + // Path to a directory containing the container's root filesystem. + Rootfs string `json:"rootfs"` + + // Readonlyfs will remount the container's rootfs as readonly where only externally mounted + // bind mounts are writtable. + Readonlyfs bool `json:"readonlyfs"` + + // Mounts specify additional source and destination paths that will be mounted inside the container's + // rootfs and mount namespace if specified + Mounts []*Mount `json:"mounts"` + + // The device nodes that should be automatically created within the container upon container start. Note, make sure that the node is marked as allowed in the cgroup as well! + Devices []*Device `json:"devices"` + + MountLabel string `json:"mount_label"` + + // Hostname optionally sets the container's hostname if provided + Hostname string `json:"hostname"` + + // Namespaces specifies the container's namespaces that it should setup when cloning the init process + // If a namespace is not provided that namespace is shared from the container's parent process + Namespaces Namespaces `json:"namespaces"` + + // Capabilities specify the capabilities to keep when executing the process inside the container + // All capbilities not specified will be dropped from the processes capability mask + Capabilities []string `json:"capabilities"` + + // Networks specifies the container's network setup to be created + Networks []*Network `json:"networks"` + + // Routes can be specified to create entries in the route table as the container is started + Routes []*Route `json:"routes"` + + // Cgroups specifies specific cgroup settings for the various subsystems that the container is + // placed into to limit the resources the container has available + Cgroups *Cgroup `json:"cgroups"` + + // AppArmorProfile specifies the profile to apply to the process running in the container and is + // change at the time the process is execed + AppArmorProfile string `json:"apparmor_profile"` + + // ProcessLabel specifies the label to apply to the process running in the container. It is + // commonly used by selinux + ProcessLabel string `json:"process_label"` + + // Rlimits specifies the resource limits, such as max open files, to set in the container + // If Rlimits are not set, the container will inherit rlimits from the parent process + Rlimits []Rlimit `json:"rlimits"` + + // AdditionalGroups specifies the gids that should be added to supplementary groups + // in addition to those that the user belongs to. + AdditionalGroups []int `json:"additional_groups"` + + // UidMappings is an array of User ID mappings for User Namespaces + UidMappings []IDMap `json:"uid_mappings"` + + // GidMappings is an array of Group ID mappings for User Namespaces + GidMappings []IDMap `json:"gid_mappings"` + + // MaskPaths specifies paths within the container's rootfs to mask over with a bind + // mount pointing to /dev/null as to prevent reads of the file. + MaskPaths []string `json:"mask_paths"` + + // ReadonlyPaths specifies paths within the container's rootfs to remount as read-only + // so that these files prevent any writes. + ReadonlyPaths []string `json:"readonly_paths"` +} + +// Gets the root uid for the process on host which could be non-zero +// when user namespaces are enabled. +func (c Config) HostUID() (int, error) { + if c.Namespaces.Contains(NEWUSER) { + if c.UidMappings == nil { + return -1, fmt.Errorf("User namespaces enabled, but no user mappings found.") + } + id, found := c.hostIDFromMapping(0, c.UidMappings) + if !found { + return -1, fmt.Errorf("User namespaces enabled, but no root user mapping found.") + } + return id, nil + } + // Return default root uid 0 + return 0, nil +} + +// Gets the root uid for the process on host which could be non-zero +// when user namespaces are enabled. +func (c Config) HostGID() (int, error) { + if c.Namespaces.Contains(NEWUSER) { + if c.GidMappings == nil { + return -1, fmt.Errorf("User namespaces enabled, but no gid mappings found.") + } + id, found := c.hostIDFromMapping(0, c.GidMappings) + if !found { + return -1, fmt.Errorf("User namespaces enabled, but no root user mapping found.") + } + return id, nil + } + // Return default root uid 0 + return 0, nil +} + +// Utility function that gets a host ID for a container ID from user namespace map +// if that ID is present in the map. +func (c Config) hostIDFromMapping(containerID int, uMap []IDMap) (int, bool) { + for _, m := range uMap { + if (containerID >= m.ContainerID) && (containerID <= (m.ContainerID + m.Size - 1)) { + hostID := m.HostID + (containerID - m.ContainerID) + return hostID, true + } + } + return -1, false +} diff --git a/vendor/src/github.com/docker/libcontainer/config_test.go b/vendor/src/github.com/docker/libcontainer/configs/config_test.go similarity index 58% rename from vendor/src/github.com/docker/libcontainer/config_test.go rename to vendor/src/github.com/docker/libcontainer/configs/config_test.go index f2287fc741..765d5e50db 100644 --- a/vendor/src/github.com/docker/libcontainer/config_test.go +++ b/vendor/src/github.com/docker/libcontainer/configs/config_test.go @@ -1,12 +1,11 @@ -package libcontainer +package configs import ( "encoding/json" + "fmt" "os" "path/filepath" "testing" - - "github.com/docker/libcontainer/devices" ) // Checks whether the expected capability is specified in the capabilities. @@ -19,13 +18,13 @@ func contains(expected string, values []string) bool { return false } -func containsDevice(expected *devices.Device, values []*devices.Device) bool { +func containsDevice(expected *Device, values []*Device) bool { for _, d := range values { if d.Path == expected.Path && - d.CgroupPermissions == expected.CgroupPermissions && + d.Permissions == expected.Permissions && d.FileMode == expected.FileMode && - d.MajorNumber == expected.MajorNumber && - d.MinorNumber == expected.MinorNumber && + d.Major == expected.Major && + d.Minor == expected.Minor && d.Type == expected.Type { return true } @@ -34,7 +33,7 @@ func containsDevice(expected *devices.Device, values []*devices.Device) bool { } func loadConfig(name string) (*Config, error) { - f, err := os.Open(filepath.Join("sample_configs", name)) + f, err := os.Open(filepath.Join("../sample_configs", name)) if err != nil { return nil, err } @@ -45,6 +44,34 @@ func loadConfig(name string) (*Config, error) { return nil, err } + // Check that a config doesn't contain extra fields + var configMap, abstractMap map[string]interface{} + + if _, err := f.Seek(0, 0); err != nil { + return nil, err + } + + if err := json.NewDecoder(f).Decode(&abstractMap); err != nil { + return nil, err + } + + configData, err := json.Marshal(&container) + if err != nil { + return nil, err + } + + if err := json.Unmarshal(configData, &configMap); err != nil { + return nil, err + } + + for k := range configMap { + delete(abstractMap, k) + } + + if len(abstractMap) != 0 { + return nil, fmt.Errorf("unknown fields: %s", abstractMap) + } + return container, nil } @@ -59,11 +86,6 @@ func TestConfigJsonFormat(t *testing.T) { t.Fail() } - if !container.Tty { - t.Log("tty should be set to true") - t.Fail() - } - if !container.Namespaces.Contains(NEWNET) { t.Log("namespaces should contain NEWNET") t.Fail() @@ -101,11 +123,6 @@ func TestConfigJsonFormat(t *testing.T) { t.Fail() } - if n.VethPrefix != "veth" { - t.Logf("veth prefix should be veth but received %q", n.VethPrefix) - t.Fail() - } - if n.Gateway != "172.17.42.1" { t.Logf("veth gateway should be 172.17.42.1 but received %q", n.Gateway) t.Fail() @@ -119,18 +136,12 @@ func TestConfigJsonFormat(t *testing.T) { break } } - - for _, d := range devices.DefaultSimpleDevices { - if !containsDevice(d, container.MountConfig.DeviceNodes) { + for _, d := range DefaultSimpleDevices { + if !containsDevice(d, container.Devices) { t.Logf("expected device configuration for %s", d.Path) t.Fail() } } - - if !container.RestrictSys { - t.Log("expected restrict sys to be true") - t.Fail() - } } func TestApparmorProfile(t *testing.T) { @@ -154,8 +165,8 @@ func TestSelinuxLabels(t *testing.T) { if container.ProcessLabel != label { t.Fatalf("expected process label %q but received %q", label, container.ProcessLabel) } - if container.MountConfig.MountLabel != label { - t.Fatalf("expected mount label %q but received %q", label, container.MountConfig.MountLabel) + if container.MountLabel != label { + t.Fatalf("expected mount label %q but received %q", label, container.MountLabel) } } @@ -170,3 +181,69 @@ func TestRemoveNamespace(t *testing.T) { t.Fatalf("namespaces should have 0 items but reports %d", len(ns)) } } + +func TestHostUIDNoUSERNS(t *testing.T) { + config := &Config{ + Namespaces: Namespaces{}, + } + uid, err := config.HostUID() + if err != nil { + t.Fatal(err) + } + if uid != 0 { + t.Fatalf("expected uid 0 with no USERNS but received %d", uid) + } +} + +func TestHostUIDWithUSERNS(t *testing.T) { + config := &Config{ + Namespaces: Namespaces{{Type: NEWUSER}}, + UidMappings: []IDMap{ + { + ContainerID: 0, + HostID: 1000, + Size: 1, + }, + }, + } + uid, err := config.HostUID() + if err != nil { + t.Fatal(err) + } + if uid != 1000 { + t.Fatalf("expected uid 1000 with no USERNS but received %d", uid) + } +} + +func TestHostGIDNoUSERNS(t *testing.T) { + config := &Config{ + Namespaces: Namespaces{}, + } + uid, err := config.HostGID() + if err != nil { + t.Fatal(err) + } + if uid != 0 { + t.Fatalf("expected gid 0 with no USERNS but received %d", uid) + } +} + +func TestHostGIDWithUSERNS(t *testing.T) { + config := &Config{ + Namespaces: Namespaces{{Type: NEWUSER}}, + GidMappings: []IDMap{ + { + ContainerID: 0, + HostID: 1000, + Size: 1, + }, + }, + } + uid, err := config.HostGID() + if err != nil { + t.Fatal(err) + } + if uid != 1000 { + t.Fatalf("expected gid 1000 with no USERNS but received %d", uid) + } +} diff --git a/vendor/src/github.com/docker/libcontainer/configs/device.go b/vendor/src/github.com/docker/libcontainer/configs/device.go new file mode 100644 index 0000000000..abff26696e --- /dev/null +++ b/vendor/src/github.com/docker/libcontainer/configs/device.go @@ -0,0 +1,52 @@ +package configs + +import ( + "fmt" + "os" +) + +const ( + Wildcard = -1 +) + +type Device struct { + // Device type, block, char, etc. + Type rune `json:"type"` + + // Path to the device. + Path string `json:"path"` + + // Major is the device's major number. + Major int64 `json:"major"` + + // Minor is the device's minor number. + Minor int64 `json:"minor"` + + // Cgroup permissions format, rwm. + Permissions string `json:"permissions"` + + // FileMode permission bits for the device. + FileMode os.FileMode `json:"file_mode"` + + // Uid of the device. + Uid uint32 `json:"uid"` + + // Gid of the device. + Gid uint32 `json:"gid"` +} + +func (d *Device) CgroupString() string { + return fmt.Sprintf("%c %s:%s %s", d.Type, deviceNumberString(d.Major), deviceNumberString(d.Minor), d.Permissions) +} + +func (d *Device) Mkdev() int { + return int((d.Major << 8) | (d.Minor & 0xff) | ((d.Minor & 0xfff00) << 12)) +} + +// deviceNumberString converts the device number to a string return result. +func deviceNumberString(number int64) string { + if number == Wildcard { + return "*" + } + return fmt.Sprint(number) +} diff --git a/vendor/src/github.com/docker/libcontainer/configs/device_defaults.go b/vendor/src/github.com/docker/libcontainer/configs/device_defaults.go new file mode 100644 index 0000000000..70fa4af049 --- /dev/null +++ b/vendor/src/github.com/docker/libcontainer/configs/device_defaults.go @@ -0,0 +1,137 @@ +package configs + +var ( + // These are devices that are to be both allowed and created. + DefaultSimpleDevices = []*Device{ + // /dev/null and zero + { + Path: "/dev/null", + Type: 'c', + Major: 1, + Minor: 3, + Permissions: "rwm", + FileMode: 0666, + }, + { + Path: "/dev/zero", + Type: 'c', + Major: 1, + Minor: 5, + Permissions: "rwm", + FileMode: 0666, + }, + + { + Path: "/dev/full", + Type: 'c', + Major: 1, + Minor: 7, + Permissions: "rwm", + FileMode: 0666, + }, + + // consoles and ttys + { + Path: "/dev/tty", + Type: 'c', + Major: 5, + Minor: 0, + Permissions: "rwm", + FileMode: 0666, + }, + + // /dev/urandom,/dev/random + { + Path: "/dev/urandom", + Type: 'c', + Major: 1, + Minor: 9, + Permissions: "rwm", + FileMode: 0666, + }, + { + Path: "/dev/random", + Type: 'c', + Major: 1, + Minor: 8, + Permissions: "rwm", + FileMode: 0666, + }, + } + DefaultAllowedDevices = append([]*Device{ + // allow mknod for any device + { + Type: 'c', + Major: Wildcard, + Minor: Wildcard, + Permissions: "m", + }, + { + Type: 'b', + Major: Wildcard, + Minor: Wildcard, + Permissions: "m", + }, + + { + Path: "/dev/console", + Type: 'c', + Major: 5, + Minor: 1, + Permissions: "rwm", + }, + { + Path: "/dev/tty0", + Type: 'c', + Major: 4, + Minor: 0, + Permissions: "rwm", + }, + { + Path: "/dev/tty1", + Type: 'c', + Major: 4, + Minor: 1, + Permissions: "rwm", + }, + // /dev/pts/ - pts namespaces are "coming soon" + { + Path: "", + Type: 'c', + Major: 136, + Minor: Wildcard, + Permissions: "rwm", + }, + { + Path: "", + Type: 'c', + Major: 5, + Minor: 2, + Permissions: "rwm", + }, + + // tuntap + { + Path: "", + Type: 'c', + Major: 10, + Minor: 200, + Permissions: "rwm", + }, + }, DefaultSimpleDevices...) + DefaultAutoCreatedDevices = append([]*Device{ + { + // /dev/fuse is created but not allowed. + // This is to allow java to work. Because java + // Insists on there being a /dev/fuse + // https://github.com/docker/docker/issues/514 + // https://github.com/docker/docker/issues/2393 + // + Path: "/dev/fuse", + Type: 'c', + Major: 10, + Minor: 229, + Permissions: "rwm", + }, + }, DefaultSimpleDevices...) +) diff --git a/vendor/src/github.com/docker/libcontainer/configs/mount.go b/vendor/src/github.com/docker/libcontainer/configs/mount.go new file mode 100644 index 0000000000..7b3dea3312 --- /dev/null +++ b/vendor/src/github.com/docker/libcontainer/configs/mount.go @@ -0,0 +1,21 @@ +package configs + +type Mount struct { + // Source path for the mount. + Source string `json:"source"` + + // Destination path for the mount inside the container. + Destination string `json:"destination"` + + // Device the mount is for. + Device string `json:"device"` + + // Mount flags. + Flags int `json:"flags"` + + // Mount data applied to the mount. + Data string `json:"data"` + + // Relabel source if set, "z" indicates shared, "Z" indicates unshared. + Relabel string `json:"relabel"` +} diff --git a/vendor/src/github.com/docker/libcontainer/configs/namespaces.go b/vendor/src/github.com/docker/libcontainer/configs/namespaces.go new file mode 100644 index 0000000000..9078e6abfc --- /dev/null +++ b/vendor/src/github.com/docker/libcontainer/configs/namespaces.go @@ -0,0 +1,109 @@ +package configs + +import ( + "fmt" + "syscall" +) + +type NamespaceType string + +const ( + NEWNET NamespaceType = "NEWNET" + NEWPID NamespaceType = "NEWPID" + NEWNS NamespaceType = "NEWNS" + NEWUTS NamespaceType = "NEWUTS" + NEWIPC NamespaceType = "NEWIPC" + NEWUSER NamespaceType = "NEWUSER" +) + +// Namespace defines configuration for each namespace. It specifies an +// alternate path that is able to be joined via setns. +type Namespace struct { + Type NamespaceType `json:"type"` + Path string `json:"path"` +} + +func (n *Namespace) Syscall() int { + return namespaceInfo[n.Type] +} + +func (n *Namespace) GetPath(pid int) string { + if n.Path != "" { + return n.Path + } + return fmt.Sprintf("/proc/%d/ns/%s", pid, n.file()) +} + +func (n *Namespace) file() string { + file := "" + switch n.Type { + case NEWNET: + file = "net" + case NEWNS: + file = "mnt" + case NEWPID: + file = "pid" + case NEWIPC: + file = "ipc" + case NEWUSER: + file = "user" + case NEWUTS: + file = "uts" + } + return file +} + +type Namespaces []Namespace + +func (n *Namespaces) Remove(t NamespaceType) bool { + i := n.index(t) + if i == -1 { + return false + } + *n = append((*n)[:i], (*n)[i+1:]...) + return true +} + +func (n *Namespaces) Add(t NamespaceType, path string) { + i := n.index(t) + if i == -1 { + *n = append(*n, Namespace{Type: t, Path: path}) + return + } + (*n)[i].Path = path +} + +func (n *Namespaces) index(t NamespaceType) int { + for i, ns := range *n { + if ns.Type == t { + return i + } + } + return -1 +} + +func (n *Namespaces) Contains(t NamespaceType) bool { + return n.index(t) != -1 +} + +var namespaceInfo = map[NamespaceType]int{ + NEWNET: syscall.CLONE_NEWNET, + NEWNS: syscall.CLONE_NEWNS, + NEWUSER: syscall.CLONE_NEWUSER, + NEWIPC: syscall.CLONE_NEWIPC, + NEWUTS: syscall.CLONE_NEWUTS, + NEWPID: syscall.CLONE_NEWPID, +} + +// CloneFlags parses the container's Namespaces options to set the correct +// flags on clone, unshare. This functions returns flags only for new namespaces. +func (n *Namespaces) CloneFlags() uintptr { + var flag int + for _, v := range *n { + if v.Path != "" { + continue + } + flag |= namespaceInfo[v.Type] + } + return uintptr(flag) +} diff --git a/vendor/src/github.com/docker/libcontainer/configs/network.go b/vendor/src/github.com/docker/libcontainer/configs/network.go new file mode 100644 index 0000000000..5544398830 --- /dev/null +++ b/vendor/src/github.com/docker/libcontainer/configs/network.go @@ -0,0 +1,66 @@ +package configs + +// Network defines configuration for a container's networking stack +// +// The network configuration can be omited from a container causing the +// container to be setup with the host's networking stack +type Network struct { + // Type sets the networks type, commonly veth and loopback + Type string `json:"type"` + + // Name of the network interface + Name string `json:"name"` + + // The bridge to use. + Bridge string `json:"bridge"` + + // MacAddress contains the MAC address to set on the network interface + MacAddress string `json:"mac_address"` + + // Address contains the IPv4 and mask to set on the network interface + Address string `json:"address"` + + // Gateway sets the gateway address that is used as the default for the interface + Gateway string `json:"gateway"` + + // IPv6Address contains the IPv6 and mask to set on the network interface + IPv6Address string `json:"ipv6_address"` + + // IPv6Gateway sets the ipv6 gateway address that is used as the default for the interface + IPv6Gateway string `json:"ipv6_gateway"` + + // Mtu sets the mtu value for the interface and will be mirrored on both the host and + // container's interfaces if a pair is created, specifically in the case of type veth + // Note: This does not apply to loopback interfaces. + Mtu int `json:"mtu"` + + // TxQueueLen sets the tx_queuelen value for the interface and will be mirrored on both the host and + // container's interfaces if a pair is created, specifically in the case of type veth + // Note: This does not apply to loopback interfaces. + TxQueueLen int `json:"txqueuelen"` + + // HostInterfaceName is a unique name of a veth pair that resides on in the host interface of the + // container. + HostInterfaceName string `json:"host_interface_name"` +} + +// Routes can be specified to create entries in the route table as the container is started +// +// All of destination, source, and gateway should be either IPv4 or IPv6. +// One of the three options must be present, and ommitted entries will use their +// IP family default for the route table. For IPv4 for example, setting the +// gateway to 1.2.3.4 and the interface to eth0 will set up a standard +// destination of 0.0.0.0(or *) when viewed in the route table. +type Route struct { + // Sets the destination and mask, should be a CIDR. Accepts IPv4 and IPv6 + Destination string `json:"destination"` + + // Sets the source and mask, should be a CIDR. Accepts IPv4 and IPv6 + Source string `json:"source"` + + // Sets the gateway. Accepts IPv4 and IPv6 + Gateway string `json:"gateway"` + + // The device to set this route up for, for example: eth0 + InterfaceName string `json:"interface_name"` +} diff --git a/vendor/src/github.com/docker/libcontainer/configs/validate/config.go b/vendor/src/github.com/docker/libcontainer/configs/validate/config.go new file mode 100644 index 0000000000..98926dd26e --- /dev/null +++ b/vendor/src/github.com/docker/libcontainer/configs/validate/config.go @@ -0,0 +1,93 @@ +package validate + +import ( + "fmt" + "os" + "path/filepath" + + "github.com/docker/libcontainer/configs" +) + +type Validator interface { + Validate(*configs.Config) error +} + +func New() Validator { + return &ConfigValidator{} +} + +type ConfigValidator struct { +} + +func (v *ConfigValidator) Validate(config *configs.Config) error { + if err := v.rootfs(config); err != nil { + return err + } + if err := v.network(config); err != nil { + return err + } + if err := v.hostname(config); err != nil { + return err + } + if err := v.security(config); err != nil { + return err + } + if err := v.usernamespace(config); err != nil { + return err + } + return nil +} + +// rootfs validates the the rootfs is an absolute path and is not a symlink +// to the container's root filesystem. +func (v *ConfigValidator) rootfs(config *configs.Config) error { + cleaned, err := filepath.Abs(config.Rootfs) + if err != nil { + return err + } + if cleaned, err = filepath.EvalSymlinks(cleaned); err != nil { + return err + } + if config.Rootfs != cleaned { + return fmt.Errorf("%s is not an absolute path or is a symlink", config.Rootfs) + } + return nil +} + +func (v *ConfigValidator) network(config *configs.Config) error { + if !config.Namespaces.Contains(configs.NEWNET) { + if len(config.Networks) > 0 || len(config.Routes) > 0 { + return fmt.Errorf("unable to apply network settings without a private NET namespace") + } + } + return nil +} + +func (v *ConfigValidator) hostname(config *configs.Config) error { + if config.Hostname != "" && !config.Namespaces.Contains(configs.NEWUTS) { + return fmt.Errorf("unable to set hostname without a private UTS namespace") + } + return nil +} + +func (v *ConfigValidator) security(config *configs.Config) error { + // restrict sys without mount namespace + if (len(config.MaskPaths) > 0 || len(config.ReadonlyPaths) > 0) && + !config.Namespaces.Contains(configs.NEWNS) { + return fmt.Errorf("unable to restrict sys entries without a private MNT namespace") + } + return nil +} + +func (v *ConfigValidator) usernamespace(config *configs.Config) error { + if config.Namespaces.Contains(configs.NEWUSER) { + if _, err := os.Stat("/proc/self/ns/user"); os.IsNotExist(err) { + return fmt.Errorf("USER namespaces aren't enabled in the kernel") + } + } else { + if config.UidMappings != nil || config.GidMappings != nil { + return fmt.Errorf("User namespace mappings specified, but USER namespace isn't enabled in the config") + } + } + return nil +} diff --git a/vendor/src/github.com/docker/libcontainer/console.go b/vendor/src/github.com/docker/libcontainer/console.go new file mode 100644 index 0000000000..042a2a2e48 --- /dev/null +++ b/vendor/src/github.com/docker/libcontainer/console.go @@ -0,0 +1,15 @@ +package libcontainer + +import "io" + +// Console represents a pseudo TTY. +type Console interface { + io.ReadWriter + io.Closer + + // Path returns the filesystem path to the slave side of the pty. + Path() string + + // Fd returns the fd for the master of the pty. + Fd() uintptr +} diff --git a/vendor/src/github.com/docker/libcontainer/console/console.go b/vendor/src/github.com/docker/libcontainer/console/console.go deleted file mode 100644 index 438e670420..0000000000 --- a/vendor/src/github.com/docker/libcontainer/console/console.go +++ /dev/null @@ -1,128 +0,0 @@ -// +build linux - -package console - -import ( - "fmt" - "os" - "path/filepath" - "syscall" - "unsafe" - - "github.com/docker/libcontainer/label" -) - -// Setup initializes the proper /dev/console inside the rootfs path -func Setup(rootfs, consolePath, mountLabel string) error { - oldMask := syscall.Umask(0000) - defer syscall.Umask(oldMask) - - if err := os.Chmod(consolePath, 0600); err != nil { - return err - } - - if err := os.Chown(consolePath, 0, 0); err != nil { - return err - } - - if err := label.SetFileLabel(consolePath, mountLabel); err != nil { - return fmt.Errorf("set file label %s %s", consolePath, err) - } - - dest := filepath.Join(rootfs, "dev/console") - - f, err := os.Create(dest) - if err != nil && !os.IsExist(err) { - return fmt.Errorf("create %s %s", dest, err) - } - - if f != nil { - f.Close() - } - - if err := syscall.Mount(consolePath, dest, "bind", syscall.MS_BIND, ""); err != nil { - return fmt.Errorf("bind %s to %s %s", consolePath, dest, err) - } - - return nil -} - -func OpenAndDup(consolePath string) error { - slave, err := OpenTerminal(consolePath, syscall.O_RDWR) - if err != nil { - return fmt.Errorf("open terminal %s", err) - } - - if err := syscall.Dup2(int(slave.Fd()), 0); err != nil { - return err - } - - if err := syscall.Dup2(int(slave.Fd()), 1); err != nil { - return err - } - - return syscall.Dup2(int(slave.Fd()), 2) -} - -// Unlockpt unlocks the slave pseudoterminal device corresponding to the master pseudoterminal referred to by f. -// Unlockpt should be called before opening the slave side of a pseudoterminal. -func Unlockpt(f *os.File) error { - var u int32 - - return Ioctl(f.Fd(), syscall.TIOCSPTLCK, uintptr(unsafe.Pointer(&u))) -} - -// Ptsname retrieves the name of the first available pts for the given master. -func Ptsname(f *os.File) (string, error) { - var n int32 - - if err := Ioctl(f.Fd(), syscall.TIOCGPTN, uintptr(unsafe.Pointer(&n))); err != nil { - return "", err - } - - return fmt.Sprintf("/dev/pts/%d", n), nil -} - -// CreateMasterAndConsole will open /dev/ptmx on the host and retreive the -// pts name for use as the pty slave inside the container -func CreateMasterAndConsole() (*os.File, string, error) { - master, err := os.OpenFile("/dev/ptmx", syscall.O_RDWR|syscall.O_NOCTTY|syscall.O_CLOEXEC, 0) - if err != nil { - return nil, "", err - } - - console, err := Ptsname(master) - if err != nil { - return nil, "", err - } - - if err := Unlockpt(master); err != nil { - return nil, "", err - } - - return master, console, nil -} - -// OpenPtmx opens /dev/ptmx, i.e. the PTY master. -func OpenPtmx() (*os.File, error) { - // O_NOCTTY and O_CLOEXEC are not present in os package so we use the syscall's one for all. - return os.OpenFile("/dev/ptmx", syscall.O_RDONLY|syscall.O_NOCTTY|syscall.O_CLOEXEC, 0) -} - -// OpenTerminal is a clone of os.OpenFile without the O_CLOEXEC -// used to open the pty slave inside the container namespace -func OpenTerminal(name string, flag int) (*os.File, error) { - r, e := syscall.Open(name, flag, 0) - if e != nil { - return nil, &os.PathError{Op: "open", Path: name, Err: e} - } - return os.NewFile(uintptr(r), name), nil -} - -func Ioctl(fd uintptr, flag, data uintptr) error { - if _, _, err := syscall.Syscall(syscall.SYS_IOCTL, fd, flag, data); err != 0 { - return err - } - - return nil -} diff --git a/vendor/src/github.com/docker/libcontainer/console_linux.go b/vendor/src/github.com/docker/libcontainer/console_linux.go new file mode 100644 index 0000000000..afdc2976c4 --- /dev/null +++ b/vendor/src/github.com/docker/libcontainer/console_linux.go @@ -0,0 +1,147 @@ +// +build linux + +package libcontainer + +import ( + "fmt" + "os" + "path/filepath" + "syscall" + "unsafe" + + "github.com/docker/libcontainer/label" +) + +// newConsole returns an initalized console that can be used within a container by copying bytes +// from the master side to the slave that is attached as the tty for the container's init process. +func newConsole(uid, gid int) (Console, error) { + master, err := os.OpenFile("/dev/ptmx", syscall.O_RDWR|syscall.O_NOCTTY|syscall.O_CLOEXEC, 0) + if err != nil { + return nil, err + } + console, err := ptsname(master) + if err != nil { + return nil, err + } + if err := unlockpt(master); err != nil { + return nil, err + } + if err := os.Chmod(console, 0600); err != nil { + return nil, err + } + if err := os.Chown(console, uid, gid); err != nil { + return nil, err + } + return &linuxConsole{ + slavePath: console, + master: master, + }, nil +} + +// newConsoleFromPath is an internal fucntion returning an initialzied console for use inside +// a container's MNT namespace. +func newConsoleFromPath(slavePath string) *linuxConsole { + return &linuxConsole{ + slavePath: slavePath, + } +} + +// linuxConsole is a linux psuedo TTY for use within a container. +type linuxConsole struct { + master *os.File + slavePath string +} + +func (c *linuxConsole) Fd() uintptr { + return c.master.Fd() +} + +func (c *linuxConsole) Path() string { + return c.slavePath +} + +func (c *linuxConsole) Read(b []byte) (int, error) { + return c.master.Read(b) +} + +func (c *linuxConsole) Write(b []byte) (int, error) { + return c.master.Write(b) +} + +func (c *linuxConsole) Close() error { + if m := c.master; m != nil { + return m.Close() + } + return nil +} + +// mount initializes the console inside the rootfs mounting with the specified mount label +// and applying the correct ownership of the console. +func (c *linuxConsole) mount(rootfs, mountLabel string, uid, gid int) error { + oldMask := syscall.Umask(0000) + defer syscall.Umask(oldMask) + if err := label.SetFileLabel(c.slavePath, mountLabel); err != nil { + return err + } + dest := filepath.Join(rootfs, "/dev/console") + f, err := os.Create(dest) + if err != nil && !os.IsExist(err) { + return err + } + if f != nil { + f.Close() + } + return syscall.Mount(c.slavePath, dest, "bind", syscall.MS_BIND, "") +} + +// dupStdio opens the slavePath for the console and dup2s the fds to the current +// processes stdio, fd 0,1,2. +func (c *linuxConsole) dupStdio() error { + slave, err := c.open(syscall.O_RDWR) + if err != nil { + return err + } + fd := int(slave.Fd()) + for _, i := range []int{0, 1, 2} { + if err := syscall.Dup2(fd, i); err != nil { + return err + } + } + return nil +} + +// open is a clone of os.OpenFile without the O_CLOEXEC used to open the pty slave. +func (c *linuxConsole) open(flag int) (*os.File, error) { + r, e := syscall.Open(c.slavePath, flag, 0) + if e != nil { + return nil, &os.PathError{ + Op: "open", + Path: c.slavePath, + Err: e, + } + } + return os.NewFile(uintptr(r), c.slavePath), nil +} + +func ioctl(fd uintptr, flag, data uintptr) error { + if _, _, err := syscall.Syscall(syscall.SYS_IOCTL, fd, flag, data); err != 0 { + return err + } + return nil +} + +// unlockpt unlocks the slave pseudoterminal device corresponding to the master pseudoterminal referred to by f. +// unlockpt should be called before opening the slave side of a pty. +func unlockpt(f *os.File) error { + var u int32 + return ioctl(f.Fd(), syscall.TIOCSPTLCK, uintptr(unsafe.Pointer(&u))) +} + +// ptsname retrieves the name of the first available pts for the given master. +func ptsname(f *os.File) (string, error) { + var n int32 + if err := ioctl(f.Fd(), syscall.TIOCGPTN, uintptr(unsafe.Pointer(&n))); err != nil { + return "", err + } + return fmt.Sprintf("/dev/pts/%d", n), nil +} diff --git a/vendor/src/github.com/docker/libcontainer/container.go b/vendor/src/github.com/docker/libcontainer/container.go index 307e8cbcbb..cebe8273e9 100644 --- a/vendor/src/github.com/docker/libcontainer/container.go +++ b/vendor/src/github.com/docker/libcontainer/container.go @@ -1,8 +1,53 @@ -/* -NOTE: The API is in flux and mainly not implemented. Proceed with caution until further notice. -*/ +// Libcontainer provides a native Go implementation for creating containers +// with namespaces, cgroups, capabilities, and filesystem access controls. +// It allows you to manage the lifecycle of the container performing additional operations +// after the container is created. package libcontainer +import ( + "github.com/docker/libcontainer/configs" +) + +// The status of a container. +type Status int + +const ( + // The container exists and is running. + Running Status = iota + 1 + + // The container exists, it is in the process of being paused. + Pausing + + // The container exists, but all its processes are paused. + Paused + + // The container does not exist. + Destroyed +) + +// State represents a running container's state +type State struct { + // ID is the container ID. + ID string `json:"id"` + + // InitProcessPid is the init process id in the parent namespace. + InitProcessPid int `json:"init_process_pid"` + + // InitProcessStartTime is the init process start time. + InitProcessStartTime string `json:"init_process_start"` + + // Path to all the cgroups setup for a container. Key is cgroup subsystem name + // with the value as the path. + CgroupPaths map[string]string `json:"cgroup_paths"` + + // NamespacePaths are filepaths to the container's namespaces. Key is the namespace type + // with the value as the path. + NamespacePaths map[configs.NamespaceType]string `json:"namespace_paths"` + + // Config is the container's configuration. + Config configs.Config `json:"config"` +} + // A libcontainer container object. // // Each container is thread-safe within the same process. Since a container can @@ -12,67 +57,88 @@ type Container interface { // Returns the ID of the container ID() string - // Returns the current run state of the container. + // Returns the current status of the container. // - // Errors: + // errors: // ContainerDestroyed - Container no longer exists, - // SystemError - System error. - RunState() (*RunState, Error) + // Systemerror - System error. + Status() (Status, error) + + // State returns the current container's state information. + // + // errors: + // Systemerror - System erroor. + State() (*State, error) // Returns the current config of the container. - Config() *Config + Config() configs.Config - // Start a process inside the container. Returns the PID of the new process (in the caller process's namespace) and a channel that will return the exit status of the process whenever it dies. + // Returns the PIDs inside this container. The PIDs are in the namespace of the calling process. // - // Errors: + // errors: + // ContainerDestroyed - Container no longer exists, + // Systemerror - System error. + // + // Some of the returned PIDs may no longer refer to processes in the Container, unless + // the Container state is PAUSED in which case every PID in the slice is valid. + Processes() ([]int, error) + + // Returns statistics for the container. + // + // errors: + // ContainerDestroyed - Container no longer exists, + // Systemerror - System error. + Stats() (*Stats, error) + + // Set cgroup resources of container as configured + // + // We can use this to change resources when containers are running. + // + // errors: + // Systemerror - System error. + Set() error + + // Start a process inside the container. Returns error if process fails to + // start. You can track process lifecycle with passed Process structure. + // + // errors: // ContainerDestroyed - Container no longer exists, // ConfigInvalid - config is invalid, // ContainerPaused - Container is paused, - // SystemError - System error. - Start(config *ProcessConfig) (pid int, exitChan chan int, err Error) + // Systemerror - System error. + Start(process *Process) (err error) // Destroys the container after killing all running processes. // // Any event registrations are removed before the container is destroyed. // No error is returned if the container is already destroyed. // - // Errors: - // SystemError - System error. - Destroy() Error - - // Returns the PIDs inside this container. The PIDs are in the namespace of the calling process. - // - // Errors: - // ContainerDestroyed - Container no longer exists, - // SystemError - System error. - // - // Some of the returned PIDs may no longer refer to processes in the Container, unless - // the Container state is PAUSED in which case every PID in the slice is valid. - Processes() ([]int, Error) - - // Returns statistics for the container. - // - // Errors: - // ContainerDestroyed - Container no longer exists, - // SystemError - System error. - Stats() (*ContainerStats, Error) + // errors: + // Systemerror - System error. + Destroy() error // If the Container state is RUNNING or PAUSING, sets the Container state to PAUSING and pauses // the execution of any user processes. Asynchronously, when the container finished being paused the // state is changed to PAUSED. // If the Container state is PAUSED, do nothing. // - // Errors: + // errors: // ContainerDestroyed - Container no longer exists, - // SystemError - System error. - Pause() Error + // Systemerror - System error. + Pause() error // If the Container state is PAUSED, resumes the execution of any user processes in the // Container before setting the Container state to RUNNING. // If the Container state is RUNNING, do nothing. // - // Errors: + // errors: // ContainerDestroyed - Container no longer exists, - // SystemError - System error. - Resume() Error + // Systemerror - System error. + Resume() error + + // NotifyOOM returns a read-only channel signaling when the container receives an OOM notification. + // + // errors: + // Systemerror - System error. + NotifyOOM() (<-chan struct{}, error) } diff --git a/vendor/src/github.com/docker/libcontainer/container_linux.go b/vendor/src/github.com/docker/libcontainer/container_linux.go new file mode 100644 index 0000000000..bf729760e5 --- /dev/null +++ b/vendor/src/github.com/docker/libcontainer/container_linux.go @@ -0,0 +1,306 @@ +// +build linux + +package libcontainer + +import ( + "encoding/json" + "fmt" + "os" + "os/exec" + "path/filepath" + "sync" + "syscall" + + log "github.com/Sirupsen/logrus" + "github.com/docker/libcontainer/cgroups" + "github.com/docker/libcontainer/configs" +) + +type linuxContainer struct { + id string + root string + config *configs.Config + cgroupManager cgroups.Manager + initPath string + initArgs []string + initProcess parentProcess + m sync.Mutex +} + +// ID returns the container's unique ID +func (c *linuxContainer) ID() string { + return c.id +} + +// Config returns the container's configuration +func (c *linuxContainer) Config() configs.Config { + return *c.config +} + +func (c *linuxContainer) Status() (Status, error) { + c.m.Lock() + defer c.m.Unlock() + return c.currentStatus() +} + +func (c *linuxContainer) State() (*State, error) { + c.m.Lock() + defer c.m.Unlock() + return c.currentState() +} + +func (c *linuxContainer) Processes() ([]int, error) { + pids, err := c.cgroupManager.GetPids() + if err != nil { + return nil, newSystemError(err) + } + return pids, nil +} + +func (c *linuxContainer) Stats() (*Stats, error) { + var ( + err error + stats = &Stats{} + ) + if stats.CgroupStats, err = c.cgroupManager.GetStats(); err != nil { + return stats, newSystemError(err) + } + for _, iface := range c.config.Networks { + switch iface.Type { + case "veth": + istats, err := getNetworkInterfaceStats(iface.HostInterfaceName) + if err != nil { + return stats, newSystemError(err) + } + stats.Interfaces = append(stats.Interfaces, istats) + } + } + return stats, nil +} + +func (c *linuxContainer) Set() error { + c.m.Lock() + defer c.m.Unlock() + return c.cgroupManager.Set(c.config) +} + +func (c *linuxContainer) Start(process *Process) error { + c.m.Lock() + defer c.m.Unlock() + status, err := c.currentStatus() + if err != nil { + return err + } + doInit := status == Destroyed + parent, err := c.newParentProcess(process, doInit) + if err != nil { + return newSystemError(err) + } + if err := parent.start(); err != nil { + // terminate the process to ensure that it properly is reaped. + if err := parent.terminate(); err != nil { + log.Warn(err) + } + return newSystemError(err) + } + process.ops = parent + if doInit { + + c.updateState(parent) + } + return nil +} + +func (c *linuxContainer) newParentProcess(p *Process, doInit bool) (parentProcess, error) { + parentPipe, childPipe, err := newPipe() + if err != nil { + return nil, newSystemError(err) + } + cmd, err := c.commandTemplate(p, childPipe) + if err != nil { + return nil, newSystemError(err) + } + if !doInit { + return c.newSetnsProcess(p, cmd, parentPipe, childPipe), nil + } + return c.newInitProcess(p, cmd, parentPipe, childPipe) +} + +func (c *linuxContainer) commandTemplate(p *Process, childPipe *os.File) (*exec.Cmd, error) { + cmd := &exec.Cmd{ + Path: c.initPath, + Args: c.initArgs, + } + cmd.Stdin = p.Stdin + cmd.Stdout = p.Stdout + cmd.Stderr = p.Stderr + cmd.Dir = c.config.Rootfs + if cmd.SysProcAttr == nil { + cmd.SysProcAttr = &syscall.SysProcAttr{} + } + cmd.ExtraFiles = []*os.File{childPipe} + cmd.SysProcAttr.Pdeathsig = syscall.SIGKILL + if c.config.ParentDeathSignal > 0 { + cmd.SysProcAttr.Pdeathsig = syscall.Signal(c.config.ParentDeathSignal) + } + return cmd, nil +} + +func (c *linuxContainer) newInitProcess(p *Process, cmd *exec.Cmd, parentPipe, childPipe *os.File) (*initProcess, error) { + t := "_LIBCONTAINER_INITTYPE=standard" + cloneFlags := c.config.Namespaces.CloneFlags() + if cloneFlags&syscall.CLONE_NEWUSER != 0 { + if err := c.addUidGidMappings(cmd.SysProcAttr); err != nil { + // user mappings are not supported + return nil, err + } + // Default to root user when user namespaces are enabled. + if cmd.SysProcAttr.Credential == nil { + cmd.SysProcAttr.Credential = &syscall.Credential{} + } + } + cmd.Env = append(cmd.Env, t) + cmd.SysProcAttr.Cloneflags = cloneFlags + return &initProcess{ + cmd: cmd, + childPipe: childPipe, + parentPipe: parentPipe, + manager: c.cgroupManager, + config: c.newInitConfig(p), + }, nil +} + +func (c *linuxContainer) newSetnsProcess(p *Process, cmd *exec.Cmd, parentPipe, childPipe *os.File) *setnsProcess { + cmd.Env = append(cmd.Env, + fmt.Sprintf("_LIBCONTAINER_INITPID=%d", c.initProcess.pid()), + "_LIBCONTAINER_INITTYPE=setns", + ) + + if p.consolePath != "" { + cmd.Env = append(cmd.Env, "_LIBCONTAINER_CONSOLE_PATH="+p.consolePath) + } + + // TODO: set on container for process management + return &setnsProcess{ + cmd: cmd, + cgroupPaths: c.cgroupManager.GetPaths(), + childPipe: childPipe, + parentPipe: parentPipe, + config: c.newInitConfig(p), + } +} + +func (c *linuxContainer) newInitConfig(process *Process) *initConfig { + return &initConfig{ + Config: c.config, + Args: process.Args, + Env: process.Env, + User: process.User, + Cwd: process.Cwd, + Console: process.consolePath, + } +} + +func newPipe() (parent *os.File, child *os.File, err error) { + fds, err := syscall.Socketpair(syscall.AF_LOCAL, syscall.SOCK_STREAM|syscall.SOCK_CLOEXEC, 0) + if err != nil { + return nil, nil, err + } + return os.NewFile(uintptr(fds[1]), "parent"), os.NewFile(uintptr(fds[0]), "child"), nil +} + +func (c *linuxContainer) Destroy() error { + c.m.Lock() + defer c.m.Unlock() + status, err := c.currentStatus() + if err != nil { + return err + } + if status != Destroyed { + return newGenericError(fmt.Errorf("container is not destroyed"), ContainerNotStopped) + } + if !c.config.Namespaces.Contains(configs.NEWPID) { + if err := killCgroupProcesses(c.cgroupManager); err != nil { + log.Warn(err) + } + } + err = c.cgroupManager.Destroy() + if rerr := os.RemoveAll(c.root); err == nil { + err = rerr + } + c.initProcess = nil + return err +} + +func (c *linuxContainer) Pause() error { + c.m.Lock() + defer c.m.Unlock() + return c.cgroupManager.Freeze(configs.Frozen) +} + +func (c *linuxContainer) Resume() error { + c.m.Lock() + defer c.m.Unlock() + return c.cgroupManager.Freeze(configs.Thawed) +} + +func (c *linuxContainer) NotifyOOM() (<-chan struct{}, error) { + return notifyOnOOM(c.cgroupManager.GetPaths()) +} + +func (c *linuxContainer) updateState(process parentProcess) error { + c.initProcess = process + state, err := c.currentState() + if err != nil { + return err + } + f, err := os.Create(filepath.Join(c.root, stateFilename)) + if err != nil { + return err + } + defer f.Close() + return json.NewEncoder(f).Encode(state) +} + +func (c *linuxContainer) currentStatus() (Status, error) { + if c.initProcess == nil { + return Destroyed, nil + } + // return Running if the init process is alive + if err := syscall.Kill(c.initProcess.pid(), 0); err != nil { + if err == syscall.ESRCH { + return Destroyed, nil + } + return 0, newSystemError(err) + } + if c.config.Cgroups != nil && c.config.Cgroups.Freezer == configs.Frozen { + return Paused, nil + } + return Running, nil +} + +func (c *linuxContainer) currentState() (*State, error) { + status, err := c.currentStatus() + if err != nil { + return nil, err + } + if status == Destroyed { + return nil, newGenericError(fmt.Errorf("container destroyed"), ContainerNotExists) + } + startTime, err := c.initProcess.startTime() + if err != nil { + return nil, newSystemError(err) + } + state := &State{ + ID: c.ID(), + Config: *c.config, + InitProcessPid: c.initProcess.pid(), + InitProcessStartTime: startTime, + CgroupPaths: c.cgroupManager.GetPaths(), + NamespacePaths: make(map[configs.NamespaceType]string), + } + for _, ns := range c.config.Namespaces { + state.NamespacePaths[ns.Type] = ns.GetPath(c.initProcess.pid()) + } + return state, nil +} diff --git a/vendor/src/github.com/docker/libcontainer/container_linux_test.go b/vendor/src/github.com/docker/libcontainer/container_linux_test.go new file mode 100644 index 0000000000..5f589f3df9 --- /dev/null +++ b/vendor/src/github.com/docker/libcontainer/container_linux_test.go @@ -0,0 +1,200 @@ +// +build linux + +package libcontainer + +import ( + "fmt" + "os" + "testing" + + "github.com/docker/libcontainer/cgroups" + "github.com/docker/libcontainer/configs" +) + +type mockCgroupManager struct { + pids []int + stats *cgroups.Stats + paths map[string]string +} + +func (m *mockCgroupManager) GetPids() ([]int, error) { + return m.pids, nil +} + +func (m *mockCgroupManager) GetStats() (*cgroups.Stats, error) { + return m.stats, nil +} + +func (m *mockCgroupManager) Apply(pid int) error { + return nil +} + +func (m *mockCgroupManager) Set(container *configs.Config) error { + return nil +} + +func (m *mockCgroupManager) Destroy() error { + return nil +} + +func (m *mockCgroupManager) GetPaths() map[string]string { + return m.paths +} + +func (m *mockCgroupManager) Freeze(state configs.FreezerState) error { + return nil +} + +type mockProcess struct { + _pid int + started string +} + +func (m *mockProcess) terminate() error { + return nil +} + +func (m *mockProcess) pid() int { + return m._pid +} + +func (m *mockProcess) startTime() (string, error) { + return m.started, nil +} + +func (m *mockProcess) start() error { + return nil +} + +func (m *mockProcess) wait() (*os.ProcessState, error) { + return nil, nil +} + +func (m *mockProcess) signal(_ os.Signal) error { + return nil +} + +func TestGetContainerPids(t *testing.T) { + container := &linuxContainer{ + id: "myid", + config: &configs.Config{}, + cgroupManager: &mockCgroupManager{pids: []int{1, 2, 3}}, + } + pids, err := container.Processes() + if err != nil { + t.Fatal(err) + } + for i, expected := range []int{1, 2, 3} { + if pids[i] != expected { + t.Fatalf("expected pid %d but received %d", expected, pids[i]) + } + } +} + +func TestGetContainerStats(t *testing.T) { + container := &linuxContainer{ + id: "myid", + config: &configs.Config{}, + cgroupManager: &mockCgroupManager{ + pids: []int{1, 2, 3}, + stats: &cgroups.Stats{ + MemoryStats: cgroups.MemoryStats{ + Usage: 1024, + }, + }, + }, + } + stats, err := container.Stats() + if err != nil { + t.Fatal(err) + } + if stats.CgroupStats == nil { + t.Fatal("cgroup stats are nil") + } + if stats.CgroupStats.MemoryStats.Usage != 1024 { + t.Fatalf("expected memory usage 1024 but recevied %d", stats.CgroupStats.MemoryStats.Usage) + } +} + +func TestGetContainerState(t *testing.T) { + var ( + pid = os.Getpid() + expectedMemoryPath = "/sys/fs/cgroup/memory/myid" + expectedNetworkPath = "/networks/fd" + ) + container := &linuxContainer{ + id: "myid", + config: &configs.Config{ + Namespaces: configs.Namespaces{ + {Type: configs.NEWPID}, + {Type: configs.NEWNS}, + {Type: configs.NEWNET, Path: expectedNetworkPath}, + {Type: configs.NEWUTS}, + {Type: configs.NEWIPC}, + }, + }, + initProcess: &mockProcess{ + _pid: pid, + started: "010", + }, + cgroupManager: &mockCgroupManager{ + pids: []int{1, 2, 3}, + stats: &cgroups.Stats{ + MemoryStats: cgroups.MemoryStats{ + Usage: 1024, + }, + }, + paths: map[string]string{ + "memory": expectedMemoryPath, + }, + }, + } + state, err := container.State() + if err != nil { + t.Fatal(err) + } + if state.InitProcessPid != pid { + t.Fatalf("expected pid %d but received %d", pid, state.InitProcessPid) + } + if state.InitProcessStartTime != "010" { + t.Fatalf("expected process start time 010 but received %s", state.InitProcessStartTime) + } + paths := state.CgroupPaths + if paths == nil { + t.Fatal("cgroup paths should not be nil") + } + if memPath := paths["memory"]; memPath != expectedMemoryPath { + t.Fatalf("expected memory path %q but received %q", expectedMemoryPath, memPath) + } + for _, ns := range container.config.Namespaces { + path := state.NamespacePaths[ns.Type] + if path == "" { + t.Fatalf("expected non nil namespace path for %s", ns.Type) + } + if ns.Type == configs.NEWNET { + if path != expectedNetworkPath { + t.Fatalf("expected path %q but received %q", expectedNetworkPath, path) + } + } else { + file := "" + switch ns.Type { + case configs.NEWNET: + file = "net" + case configs.NEWNS: + file = "mnt" + case configs.NEWPID: + file = "pid" + case configs.NEWIPC: + file = "ipc" + case configs.NEWUSER: + file = "user" + case configs.NEWUTS: + file = "uts" + } + expected := fmt.Sprintf("/proc/%d/ns/%s", pid, file) + if expected != path { + t.Fatalf("expected path %q but received %q", expected, path) + } + } + } +} diff --git a/vendor/src/github.com/docker/libcontainer/container_nouserns_linux.go b/vendor/src/github.com/docker/libcontainer/container_nouserns_linux.go new file mode 100644 index 0000000000..3b75d593cc --- /dev/null +++ b/vendor/src/github.com/docker/libcontainer/container_nouserns_linux.go @@ -0,0 +1,13 @@ +// +build !go1.4 + +package libcontainer + +import ( + "fmt" + "syscall" +) + +// not available before go 1.4 +func (c *linuxContainer) addUidGidMappings(sys *syscall.SysProcAttr) error { + return fmt.Errorf("User namespace is not supported in golang < 1.4") +} diff --git a/vendor/src/github.com/docker/libcontainer/container_userns_linux.go b/vendor/src/github.com/docker/libcontainer/container_userns_linux.go new file mode 100644 index 0000000000..5f4cf3c9fe --- /dev/null +++ b/vendor/src/github.com/docker/libcontainer/container_userns_linux.go @@ -0,0 +1,26 @@ +// +build go1.4 + +package libcontainer + +import "syscall" + +// Converts IDMap to SysProcIDMap array and adds it to SysProcAttr. +func (c *linuxContainer) addUidGidMappings(sys *syscall.SysProcAttr) error { + if c.config.UidMappings != nil { + sys.UidMappings = make([]syscall.SysProcIDMap, len(c.config.UidMappings)) + for i, um := range c.config.UidMappings { + sys.UidMappings[i].ContainerID = um.ContainerID + sys.UidMappings[i].HostID = um.HostID + sys.UidMappings[i].Size = um.Size + } + } + if c.config.GidMappings != nil { + sys.GidMappings = make([]syscall.SysProcIDMap, len(c.config.GidMappings)) + for i, gm := range c.config.GidMappings { + sys.GidMappings[i].ContainerID = gm.ContainerID + sys.GidMappings[i].HostID = gm.HostID + sys.GidMappings[i].Size = gm.Size + } + } + return nil +} diff --git a/vendor/src/github.com/docker/libcontainer/devices/defaults.go b/vendor/src/github.com/docker/libcontainer/devices/defaults.go deleted file mode 100644 index e0ad0b08f8..0000000000 --- a/vendor/src/github.com/docker/libcontainer/devices/defaults.go +++ /dev/null @@ -1,159 +0,0 @@ -package devices - -var ( - // These are devices that are to be both allowed and created. - - DefaultSimpleDevices = []*Device{ - // /dev/null and zero - { - Path: "/dev/null", - Type: 'c', - MajorNumber: 1, - MinorNumber: 3, - CgroupPermissions: "rwm", - FileMode: 0666, - }, - { - Path: "/dev/zero", - Type: 'c', - MajorNumber: 1, - MinorNumber: 5, - CgroupPermissions: "rwm", - FileMode: 0666, - }, - - { - Path: "/dev/full", - Type: 'c', - MajorNumber: 1, - MinorNumber: 7, - CgroupPermissions: "rwm", - FileMode: 0666, - }, - - // consoles and ttys - { - Path: "/dev/tty", - Type: 'c', - MajorNumber: 5, - MinorNumber: 0, - CgroupPermissions: "rwm", - FileMode: 0666, - }, - - // /dev/urandom,/dev/random - { - Path: "/dev/urandom", - Type: 'c', - MajorNumber: 1, - MinorNumber: 9, - CgroupPermissions: "rwm", - FileMode: 0666, - }, - { - Path: "/dev/random", - Type: 'c', - MajorNumber: 1, - MinorNumber: 8, - CgroupPermissions: "rwm", - FileMode: 0666, - }, - } - - DefaultAllowedDevices = append([]*Device{ - // allow mknod for any device - { - Type: 'c', - MajorNumber: Wildcard, - MinorNumber: Wildcard, - CgroupPermissions: "m", - }, - { - Type: 'b', - MajorNumber: Wildcard, - MinorNumber: Wildcard, - CgroupPermissions: "m", - }, - - { - Path: "/dev/console", - Type: 'c', - MajorNumber: 5, - MinorNumber: 1, - CgroupPermissions: "rwm", - }, - { - Path: "/dev/tty0", - Type: 'c', - MajorNumber: 4, - MinorNumber: 0, - CgroupPermissions: "rwm", - }, - { - Path: "/dev/tty1", - Type: 'c', - MajorNumber: 4, - MinorNumber: 1, - CgroupPermissions: "rwm", - }, - // /dev/pts/ - pts namespaces are "coming soon" - { - Path: "", - Type: 'c', - MajorNumber: 136, - MinorNumber: Wildcard, - CgroupPermissions: "rwm", - }, - { - Path: "", - Type: 'c', - MajorNumber: 5, - MinorNumber: 2, - CgroupPermissions: "rwm", - }, - - // tuntap - { - Path: "", - Type: 'c', - MajorNumber: 10, - MinorNumber: 200, - CgroupPermissions: "rwm", - }, - - /*// fuse - { - Path: "", - Type: 'c', - MajorNumber: 10, - MinorNumber: 229, - CgroupPermissions: "rwm", - }, - - // rtc - { - Path: "", - Type: 'c', - MajorNumber: 254, - MinorNumber: 0, - CgroupPermissions: "rwm", - }, - */ - }, DefaultSimpleDevices...) - - DefaultAutoCreatedDevices = append([]*Device{ - { - // /dev/fuse is created but not allowed. - // This is to allow java to work. Because java - // Insists on there being a /dev/fuse - // https://github.com/docker/docker/issues/514 - // https://github.com/docker/docker/issues/2393 - // - Path: "/dev/fuse", - Type: 'c', - MajorNumber: 10, - MinorNumber: 229, - CgroupPermissions: "rwm", - }, - }, DefaultSimpleDevices...) -) diff --git a/vendor/src/github.com/docker/libcontainer/devices/devices.go b/vendor/src/github.com/docker/libcontainer/devices/devices.go index 8e86d95292..537f71aff1 100644 --- a/vendor/src/github.com/docker/libcontainer/devices/devices.go +++ b/vendor/src/github.com/docker/libcontainer/devices/devices.go @@ -7,14 +7,12 @@ import ( "os" "path/filepath" "syscall" -) -const ( - Wildcard = -1 + "github.com/docker/libcontainer/configs" ) var ( - ErrNotADeviceNode = errors.New("not a device node") + ErrNotADevice = errors.New("not a device node") ) // Testing dependencies @@ -23,45 +21,20 @@ var ( ioutilReadDir = ioutil.ReadDir ) -type Device struct { - Type rune `json:"type,omitempty"` - Path string `json:"path,omitempty"` // It is fine if this is an empty string in the case that you are using Wildcards - MajorNumber int64 `json:"major_number,omitempty"` // Use the wildcard constant for wildcards. - MinorNumber int64 `json:"minor_number,omitempty"` // Use the wildcard constant for wildcards. - CgroupPermissions string `json:"cgroup_permissions,omitempty"` // Typically just "rwm" - FileMode os.FileMode `json:"file_mode,omitempty"` // The permission bits of the file's mode - Uid uint32 `json:"uid,omitempty"` - Gid uint32 `json:"gid,omitempty"` -} - -func GetDeviceNumberString(deviceNumber int64) string { - if deviceNumber == Wildcard { - return "*" - } else { - return fmt.Sprintf("%d", deviceNumber) - } -} - -func (device *Device) GetCgroupAllowString() string { - return fmt.Sprintf("%c %s:%s %s", device.Type, GetDeviceNumberString(device.MajorNumber), GetDeviceNumberString(device.MinorNumber), device.CgroupPermissions) -} - // Given the path to a device and it's cgroup_permissions(which cannot be easilly queried) look up the information about a linux device and return that information as a Device struct. -func GetDevice(path, cgroupPermissions string) (*Device, error) { +func DeviceFromPath(path, permissions string) (*configs.Device, error) { fileInfo, err := osLstat(path) if err != nil { return nil, err } - var ( devType rune mode = fileInfo.Mode() fileModePermissionBits = os.FileMode.Perm(mode) ) - switch { case mode&os.ModeDevice == 0: - return nil, ErrNotADeviceNode + return nil, ErrNotADevice case mode&os.ModeCharDevice != 0: fileModePermissionBits |= syscall.S_IFCHR devType = 'c' @@ -69,36 +42,33 @@ func GetDevice(path, cgroupPermissions string) (*Device, error) { fileModePermissionBits |= syscall.S_IFBLK devType = 'b' } - stat_t, ok := fileInfo.Sys().(*syscall.Stat_t) if !ok { return nil, fmt.Errorf("cannot determine the device number for device %s", path) } devNumber := int(stat_t.Rdev) - - return &Device{ - Type: devType, - Path: path, - MajorNumber: Major(devNumber), - MinorNumber: Minor(devNumber), - CgroupPermissions: cgroupPermissions, - FileMode: fileModePermissionBits, - Uid: stat_t.Uid, - Gid: stat_t.Gid, + return &configs.Device{ + Type: devType, + Path: path, + Major: Major(devNumber), + Minor: Minor(devNumber), + Permissions: permissions, + FileMode: fileModePermissionBits, + Uid: stat_t.Uid, + Gid: stat_t.Gid, }, nil } -func GetHostDeviceNodes() ([]*Device, error) { - return getDeviceNodes("/dev") +func HostDevices() ([]*configs.Device, error) { + return getDevices("/dev") } -func getDeviceNodes(path string) ([]*Device, error) { +func getDevices(path string) ([]*configs.Device, error) { files, err := ioutilReadDir(path) if err != nil { return nil, err } - - out := []*Device{} + out := []*configs.Device{} for _, f := range files { switch { case f.IsDir(): @@ -106,7 +76,7 @@ func getDeviceNodes(path string) ([]*Device, error) { case "pts", "shm", "fd", "mqueue": continue default: - sub, err := getDeviceNodes(filepath.Join(path, f.Name())) + sub, err := getDevices(filepath.Join(path, f.Name())) if err != nil { return nil, err } @@ -117,16 +87,14 @@ func getDeviceNodes(path string) ([]*Device, error) { case f.Name() == "console": continue } - - device, err := GetDevice(filepath.Join(path, f.Name()), "rwm") + device, err := DeviceFromPath(filepath.Join(path, f.Name()), "rwm") if err != nil { - if err == ErrNotADeviceNode { + if err == ErrNotADevice { continue } return nil, err } out = append(out, device) } - return out, nil } diff --git a/vendor/src/github.com/docker/libcontainer/devices/devices_test.go b/vendor/src/github.com/docker/libcontainer/devices/devices_test.go index fec4002237..9e52fc4e25 100644 --- a/vendor/src/github.com/docker/libcontainer/devices/devices_test.go +++ b/vendor/src/github.com/docker/libcontainer/devices/devices_test.go @@ -6,7 +6,7 @@ import ( "testing" ) -func TestGetDeviceLstatFailure(t *testing.T) { +func TestDeviceFromPathLstatFailure(t *testing.T) { testError := errors.New("test error") // Override os.Lstat to inject error. @@ -14,13 +14,13 @@ func TestGetDeviceLstatFailure(t *testing.T) { return nil, testError } - _, err := GetDevice("", "") + _, err := DeviceFromPath("", "") if err != testError { t.Fatalf("Unexpected error %v, expected %v", err, testError) } } -func TestGetHostDeviceNodesIoutilReadDirFailure(t *testing.T) { +func TestHostDevicesIoutilReadDirFailure(t *testing.T) { testError := errors.New("test error") // Override ioutil.ReadDir to inject error. @@ -28,13 +28,13 @@ func TestGetHostDeviceNodesIoutilReadDirFailure(t *testing.T) { return nil, testError } - _, err := GetHostDeviceNodes() + _, err := HostDevices() if err != testError { t.Fatalf("Unexpected error %v, expected %v", err, testError) } } -func TestGetHostDeviceNodesIoutilReadDirDeepFailure(t *testing.T) { +func TestHostDevicesIoutilReadDirDeepFailure(t *testing.T) { testError := errors.New("test error") called := false @@ -54,7 +54,7 @@ func TestGetHostDeviceNodesIoutilReadDirDeepFailure(t *testing.T) { return []os.FileInfo{fi}, nil } - _, err := GetHostDeviceNodes() + _, err := HostDevices() if err != testError { t.Fatalf("Unexpected error %v, expected %v", err, testError) } diff --git a/vendor/src/github.com/docker/libcontainer/devices/number.go b/vendor/src/github.com/docker/libcontainer/devices/number.go index 3aae380bb1..9e8feb83b0 100644 --- a/vendor/src/github.com/docker/libcontainer/devices/number.go +++ b/vendor/src/github.com/docker/libcontainer/devices/number.go @@ -20,7 +20,3 @@ func Major(devNumber int) int64 { func Minor(devNumber int) int64 { return int64((devNumber & 0xff) | ((devNumber >> 12) & 0xfff00)) } - -func Mkdev(majorNumber int64, minorNumber int64) int { - return int((majorNumber << 8) | (minorNumber & 0xff) | ((minorNumber & 0xfff00) << 12)) -} diff --git a/vendor/src/github.com/docker/libcontainer/docs/man/nsinit.1.md b/vendor/src/github.com/docker/libcontainer/docs/man/nsinit.1.md new file mode 100644 index 0000000000..898dba2306 --- /dev/null +++ b/vendor/src/github.com/docker/libcontainer/docs/man/nsinit.1.md @@ -0,0 +1,38 @@ +% nsinit User Manual +% docker/libcontainer +% JAN 2015 + +NAME: + nsinit - A low-level utility for managing containers. + It is used to spawn new containers or join existing containers. + +USAGE: + nsinit [global options] command [command options] [arguments...] + +VERSION: + 0.1 + +COMMANDS: + config display the container configuration + exec execute a new command inside a container + init runs the init process inside the namespace + oom display oom notifications for a container + pause pause the container's processes + stats display statistics for the container + unpause unpause the container's processes + help, h shows a list of commands or help for one command + +EXAMPLES: + +Get the of an already running docker container. +`sudo docker ps` will return the list of all the running containers. + +take the (e.g. 4addb0b2d307) and go to its config directory +`/var/lib/docker/execdriver/native/4addb0b2d307` and here you can run the nsinit +command line utility. + +e.g. `nsinit exec /bin/bash` will start a shell on the already running container. + +# HISTORY +Jan 2015, Originally compiled by Shishir Mahajan (shishir dot mahajan at redhat dot com) +based on nsinit source material and internal work. diff --git a/vendor/src/github.com/docker/libcontainer/error.go b/vendor/src/github.com/docker/libcontainer/error.go index 5ff56d80ba..6c266620e7 100644 --- a/vendor/src/github.com/docker/libcontainer/error.go +++ b/vendor/src/github.com/docker/libcontainer/error.go @@ -1,5 +1,7 @@ package libcontainer +import "io" + // API error code type. type ErrorCode int @@ -8,29 +10,52 @@ const ( // Factory errors IdInUse ErrorCode = iota InvalidIdFormat - // TODO: add Load errors // Container errors - ContainerDestroyed + ContainerNotExists ContainerPaused + ContainerNotStopped + ContainerNotRunning + + // Process errors + ProcessNotExecuted // Common errors ConfigInvalid SystemError ) +func (c ErrorCode) String() string { + switch c { + case IdInUse: + return "Id already in use" + case InvalidIdFormat: + return "Invalid format" + case ContainerPaused: + return "Container paused" + case ConfigInvalid: + return "Invalid configuration" + case SystemError: + return "System error" + case ContainerNotExists: + return "Container does not exist" + case ContainerNotStopped: + return "Container is not stopped" + case ContainerNotRunning: + return "Container is not running" + default: + return "Unknown error" + } +} + // API Error type. type Error interface { error - // Returns the stack trace, if any, which identifies the - // point at which the error occurred. - Stack() []byte - // Returns a verbose string including the error message // and a representation of the stack trace suitable for // printing. - Detail() string + Detail(w io.Writer) error // Returns the error code for this error. Code() ErrorCode diff --git a/vendor/src/github.com/docker/libcontainer/error_test.go b/vendor/src/github.com/docker/libcontainer/error_test.go new file mode 100644 index 0000000000..4bf4c9f5d4 --- /dev/null +++ b/vendor/src/github.com/docker/libcontainer/error_test.go @@ -0,0 +1,20 @@ +package libcontainer + +import "testing" + +func TestErrorCode(t *testing.T) { + codes := map[ErrorCode]string{ + IdInUse: "Id already in use", + InvalidIdFormat: "Invalid format", + ContainerPaused: "Container paused", + ConfigInvalid: "Invalid configuration", + SystemError: "System error", + ContainerNotExists: "Container does not exist", + } + + for code, expected := range codes { + if actual := code.String(); actual != expected { + t.Fatalf("expected string %q but received %q", expected, actual) + } + } +} diff --git a/vendor/src/github.com/docker/libcontainer/factory.go b/vendor/src/github.com/docker/libcontainer/factory.go index e37773b2bd..37e629645b 100644 --- a/vendor/src/github.com/docker/libcontainer/factory.go +++ b/vendor/src/github.com/docker/libcontainer/factory.go @@ -1,7 +1,10 @@ package libcontainer -type Factory interface { +import ( + "github.com/docker/libcontainer/configs" +) +type Factory interface { // Creates a new container with the given id and starts the initial process inside it. // id must be a string containing only letters, digits and underscores and must contain // between 1 and 1024 characters, inclusive. @@ -11,22 +14,31 @@ type Factory interface { // // Returns the new container with a running process. // - // Errors: + // errors: // IdInUse - id is already in use by a container // InvalidIdFormat - id has incorrect format // ConfigInvalid - config is invalid - // SystemError - System error + // Systemerror - System error // // On error, any partially created container parts are cleaned up (the operation is atomic). - Create(id string, config *Config) (Container, Error) + Create(id string, config *configs.Config) (Container, error) - // Load takes an ID for an existing container and reconstructs the container - // from the state. + // Load takes an ID for an existing container and returns the container information + // from the state. This presents a read only view of the container. // - // Errors: + // errors: // Path does not exist // Container is stopped // System error - // TODO: fix description - Load(id string) (Container, Error) + Load(id string) (Container, error) + + // StartInitialization is an internal API to libcontainer used during the rexec of the + // container. pipefd is the fd to the child end of the pipe used to syncronize the + // parent and child process providing state and configuration to the child process and + // returning any errors during the init of the container + // + // Errors: + // pipe connection error + // system error + StartInitialization(pipefd uintptr) error } diff --git a/vendor/src/github.com/docker/libcontainer/factory_linux.go b/vendor/src/github.com/docker/libcontainer/factory_linux.go new file mode 100644 index 0000000000..468398c015 --- /dev/null +++ b/vendor/src/github.com/docker/libcontainer/factory_linux.go @@ -0,0 +1,262 @@ +// +build linux + +package libcontainer + +import ( + "encoding/json" + "fmt" + "io/ioutil" + "os" + "os/exec" + "path/filepath" + "regexp" + + "github.com/docker/libcontainer/cgroups" + "github.com/docker/libcontainer/cgroups/fs" + "github.com/docker/libcontainer/cgroups/systemd" + "github.com/docker/libcontainer/configs" + "github.com/docker/libcontainer/configs/validate" +) + +const ( + stateFilename = "state.json" +) + +var ( + idRegex = regexp.MustCompile(`^[\w_]+$`) + maxIdLen = 1024 +) + +// InitArgs returns an options func to configure a LinuxFactory with the +// provided init arguments. +func InitArgs(args ...string) func(*LinuxFactory) error { + return func(l *LinuxFactory) error { + name := args[0] + if filepath.Base(name) == name { + if lp, err := exec.LookPath(name); err == nil { + name = lp + } + } + l.InitPath = name + l.InitArgs = append([]string{name}, args[1:]...) + return nil + } +} + +// InitPath returns an options func to configure a LinuxFactory with the +// provided absolute path to the init binary and arguements. +func InitPath(path string, args ...string) func(*LinuxFactory) error { + return func(l *LinuxFactory) error { + l.InitPath = path + l.InitArgs = args + return nil + } +} + +// SystemdCgroups is an options func to configure a LinuxFactory to return +// containers that use systemd to create and manage cgroups. +func SystemdCgroups(l *LinuxFactory) error { + l.NewCgroupsManager = func(config *configs.Cgroup, paths map[string]string) cgroups.Manager { + return &systemd.Manager{ + Cgroups: config, + Paths: paths, + } + } + return nil +} + +// Cgroupfs is an options func to configure a LinuxFactory to return +// containers that use the native cgroups filesystem implementation to +// create and manage cgroups. +func Cgroupfs(l *LinuxFactory) error { + l.NewCgroupsManager = func(config *configs.Cgroup, paths map[string]string) cgroups.Manager { + return &fs.Manager{ + Cgroups: config, + Paths: paths, + } + } + return nil +} + +// New returns a linux based container factory based in the root directory and +// configures the factory with the provided option funcs. +func New(root string, options ...func(*LinuxFactory) error) (Factory, error) { + if root != "" { + if err := os.MkdirAll(root, 0700); err != nil { + return nil, newGenericError(err, SystemError) + } + } + l := &LinuxFactory{ + Root: root, + Validator: validate.New(), + } + InitArgs(os.Args[0], "init")(l) + Cgroupfs(l) + for _, opt := range options { + if err := opt(l); err != nil { + return nil, err + } + } + return l, nil +} + +// LinuxFactory implements the default factory interface for linux based systems. +type LinuxFactory struct { + // Root directory for the factory to store state. + Root string + + // InitPath is the absolute path to the init binary. + InitPath string + + // InitArgs are arguments for calling the init responsibilities for spawning + // a container. + InitArgs []string + + // Validator provides validation to container configurations. + Validator validate.Validator + + // NewCgroupsManager returns an initialized cgroups manager for a single container. + NewCgroupsManager func(config *configs.Cgroup, paths map[string]string) cgroups.Manager +} + +func (l *LinuxFactory) Create(id string, config *configs.Config) (Container, error) { + if l.Root == "" { + return nil, newGenericError(fmt.Errorf("invalid root"), ConfigInvalid) + } + if err := l.validateID(id); err != nil { + return nil, err + } + if err := l.Validator.Validate(config); err != nil { + return nil, newGenericError(err, ConfigInvalid) + } + containerRoot := filepath.Join(l.Root, id) + if _, err := os.Stat(containerRoot); err == nil { + return nil, newGenericError(fmt.Errorf("Container with id exists: %v", id), IdInUse) + } else if !os.IsNotExist(err) { + return nil, newGenericError(err, SystemError) + } + if err := os.MkdirAll(containerRoot, 0700); err != nil { + return nil, newGenericError(err, SystemError) + } + return &linuxContainer{ + id: id, + root: containerRoot, + config: config, + initPath: l.InitPath, + initArgs: l.InitArgs, + cgroupManager: l.NewCgroupsManager(config.Cgroups, nil), + }, nil +} + +func (l *LinuxFactory) Load(id string) (Container, error) { + if l.Root == "" { + return nil, newGenericError(fmt.Errorf("invalid root"), ConfigInvalid) + } + containerRoot := filepath.Join(l.Root, id) + state, err := l.loadState(containerRoot) + if err != nil { + return nil, err + } + r := &restoredProcess{ + processPid: state.InitProcessPid, + processStartTime: state.InitProcessStartTime, + } + return &linuxContainer{ + initProcess: r, + id: id, + config: &state.Config, + initPath: l.InitPath, + initArgs: l.InitArgs, + cgroupManager: l.NewCgroupsManager(state.Config.Cgroups, state.CgroupPaths), + root: containerRoot, + }, nil +} + +// StartInitialization loads a container by opening the pipe fd from the parent to read the configuration and state +// This is a low level implementation detail of the reexec and should not be consumed externally +func (l *LinuxFactory) StartInitialization(pipefd uintptr) (err error) { + var ( + pipe = os.NewFile(uintptr(pipefd), "pipe") + it = initType(os.Getenv("_LIBCONTAINER_INITTYPE")) + ) + // clear the current process's environment to clean any libcontainer + // specific env vars. + os.Clearenv() + defer func() { + // if we have an error during the initialization of the container's init then send it back to the + // parent process in the form of an initError. + if err != nil { + // ensure that any data sent from the parent is consumed so it doesn't + // receive ECONNRESET when the child writes to the pipe. + ioutil.ReadAll(pipe) + if err := json.NewEncoder(pipe).Encode(newSystemError(err)); err != nil { + panic(err) + } + } + // ensure that this pipe is always closed + pipe.Close() + }() + i, err := newContainerInit(it, pipe) + if err != nil { + return err + } + return i.Init() +} + +func (l *LinuxFactory) loadState(root string) (*State, error) { + f, err := os.Open(filepath.Join(root, stateFilename)) + if err != nil { + if os.IsNotExist(err) { + return nil, newGenericError(err, ContainerNotExists) + } + return nil, newGenericError(err, SystemError) + } + defer f.Close() + var state *State + if err := json.NewDecoder(f).Decode(&state); err != nil { + return nil, newGenericError(err, SystemError) + } + return state, nil +} + +func (l *LinuxFactory) validateID(id string) error { + if !idRegex.MatchString(id) { + return newGenericError(fmt.Errorf("Invalid id format: %v", id), InvalidIdFormat) + } + if len(id) > maxIdLen { + return newGenericError(fmt.Errorf("Invalid id format: %v", id), InvalidIdFormat) + } + return nil +} + +// restoredProcess represents a process where the calling process may or may not be +// the parent process. This process is created when a factory loads a container from +// a persisted state. +type restoredProcess struct { + processPid int + processStartTime string +} + +func (p *restoredProcess) start() error { + return newGenericError(fmt.Errorf("restored process cannot be started"), SystemError) +} + +func (p *restoredProcess) pid() int { + return p.processPid +} + +func (p *restoredProcess) terminate() error { + return newGenericError(fmt.Errorf("restored process cannot be terminated"), SystemError) +} + +func (p *restoredProcess) wait() (*os.ProcessState, error) { + return nil, newGenericError(fmt.Errorf("restored process cannot be waited on"), SystemError) +} + +func (p *restoredProcess) startTime() (string, error) { + return p.processStartTime, nil +} + +func (p *restoredProcess) signal(s os.Signal) error { + return newGenericError(fmt.Errorf("restored process cannot be signaled"), SystemError) +} diff --git a/vendor/src/github.com/docker/libcontainer/factory_linux_test.go b/vendor/src/github.com/docker/libcontainer/factory_linux_test.go new file mode 100644 index 0000000000..19fc77ba56 --- /dev/null +++ b/vendor/src/github.com/docker/libcontainer/factory_linux_test.go @@ -0,0 +1,125 @@ +// +build linux + +package libcontainer + +import ( + "encoding/json" + "io/ioutil" + "os" + "path/filepath" + "testing" + + "github.com/docker/libcontainer/configs" +) + +func newTestRoot() (string, error) { + dir, err := ioutil.TempDir("", "libcontainer") + if err != nil { + return "", err + } + if err := os.MkdirAll(dir, 0700); err != nil { + return "", err + } + return dir, nil +} + +func TestFactoryNew(t *testing.T) { + root, rerr := newTestRoot() + if rerr != nil { + t.Fatal(rerr) + } + defer os.RemoveAll(root) + factory, err := New(root, Cgroupfs) + if err != nil { + t.Fatal(err) + } + if factory == nil { + t.Fatal("factory should not be nil") + } + lfactory, ok := factory.(*LinuxFactory) + if !ok { + t.Fatal("expected linux factory returned on linux based systems") + } + if lfactory.Root != root { + t.Fatalf("expected factory root to be %q but received %q", root, lfactory.Root) + } +} + +func TestFactoryLoadNotExists(t *testing.T) { + root, rerr := newTestRoot() + if rerr != nil { + t.Fatal(rerr) + } + defer os.RemoveAll(root) + factory, err := New(root, Cgroupfs) + if err != nil { + t.Fatal(err) + } + _, err = factory.Load("nocontainer") + if err == nil { + t.Fatal("expected nil error loading non-existing container") + } + lerr, ok := err.(Error) + if !ok { + t.Fatal("expected libcontainer error type") + } + if lerr.Code() != ContainerNotExists { + t.Fatalf("expected error code %s but received %s", ContainerNotExists, lerr.Code()) + } +} + +func TestFactoryLoadContainer(t *testing.T) { + root, err := newTestRoot() + if err != nil { + t.Fatal(err) + } + defer os.RemoveAll(root) + // setup default container config and state for mocking + var ( + id = "1" + expectedConfig = &configs.Config{ + Rootfs: "/mycontainer/root", + } + expectedState = &State{ + InitProcessPid: 1024, + Config: *expectedConfig, + } + ) + if err := os.Mkdir(filepath.Join(root, id), 0700); err != nil { + t.Fatal(err) + } + if err := marshal(filepath.Join(root, id, stateFilename), expectedState); err != nil { + t.Fatal(err) + } + factory, err := New(root, Cgroupfs) + if err != nil { + t.Fatal(err) + } + container, err := factory.Load(id) + if err != nil { + t.Fatal(err) + } + if container.ID() != id { + t.Fatalf("expected container id %q but received %q", id, container.ID()) + } + config := container.Config() + if config.Rootfs != expectedConfig.Rootfs { + t.Fatalf("expected rootfs %q but received %q", expectedConfig.Rootfs, config.Rootfs) + } + lcontainer, ok := container.(*linuxContainer) + if !ok { + t.Fatal("expected linux container on linux based systems") + } + if lcontainer.initProcess.pid() != expectedState.InitProcessPid { + t.Fatalf("expected init pid %d but received %d", expectedState.InitProcessPid, lcontainer.initProcess.pid()) + } +} + +func marshal(path string, v interface{}) error { + f, err := os.Create(path) + if err != nil { + return err + } + defer f.Close() + return json.NewEncoder(f).Encode(v) +} diff --git a/vendor/src/github.com/docker/libcontainer/generic_error.go b/vendor/src/github.com/docker/libcontainer/generic_error.go new file mode 100644 index 0000000000..ff4d7248da --- /dev/null +++ b/vendor/src/github.com/docker/libcontainer/generic_error.go @@ -0,0 +1,74 @@ +package libcontainer + +import ( + "fmt" + "io" + "text/template" + "time" + + "github.com/docker/libcontainer/stacktrace" +) + +var errorTemplate = template.Must(template.New("error").Parse(`Timestamp: {{.Timestamp}} +Code: {{.ECode}} +{{if .Message }} +Message: {{.Message}} +{{end}} +Frames:{{range $i, $frame := .Stack.Frames}} +--- +{{$i}}: {{$frame.Function}} +Package: {{$frame.Package}} +File: {{$frame.File}}@{{$frame.Line}}{{end}} +`)) + +func newGenericError(err error, c ErrorCode) Error { + if le, ok := err.(Error); ok { + return le + } + gerr := &genericError{ + Timestamp: time.Now(), + Err: err, + ECode: c, + Stack: stacktrace.Capture(1), + } + if err != nil { + gerr.Message = err.Error() + } + return gerr +} + +func newSystemError(err error) Error { + if le, ok := err.(Error); ok { + return le + } + gerr := &genericError{ + Timestamp: time.Now(), + Err: err, + ECode: SystemError, + Stack: stacktrace.Capture(1), + } + if err != nil { + gerr.Message = err.Error() + } + return gerr +} + +type genericError struct { + Timestamp time.Time + ECode ErrorCode + Err error `json:"-"` + Message string + Stack stacktrace.Stacktrace +} + +func (e *genericError) Error() string { + return fmt.Sprintf("[%d] %s: %s", e.ECode, e.ECode, e.Message) +} + +func (e *genericError) Code() ErrorCode { + return e.ECode +} + +func (e *genericError) Detail(w io.Writer) error { + return errorTemplate.Execute(w, e) +} diff --git a/vendor/src/github.com/docker/libcontainer/generic_error_test.go b/vendor/src/github.com/docker/libcontainer/generic_error_test.go new file mode 100644 index 0000000000..292d2a36bd --- /dev/null +++ b/vendor/src/github.com/docker/libcontainer/generic_error_test.go @@ -0,0 +1,14 @@ +package libcontainer + +import ( + "fmt" + "io/ioutil" + "testing" +) + +func TestErrorDetail(t *testing.T) { + err := newGenericError(fmt.Errorf("test error"), SystemError) + if derr := err.Detail(ioutil.Discard); derr != nil { + t.Fatal(derr) + } +} diff --git a/vendor/src/github.com/docker/libcontainer/hack/validate.sh b/vendor/src/github.com/docker/libcontainer/hack/validate.sh new file mode 100755 index 0000000000..19bd7adaa5 --- /dev/null +++ b/vendor/src/github.com/docker/libcontainer/hack/validate.sh @@ -0,0 +1,13 @@ +#!/usr/bin/env bash +set -e + +# This script runs all validations + +validate() { + sed -i 's!docker/docker!docker/libcontainer!' /go/src/github.com/docker/docker/hack/make/.validate + bash /go/src/github.com/docker/docker/hack/make/validate-dco + bash /go/src/github.com/docker/docker/hack/make/validate-gofmt +} + +# run validations +validate diff --git a/vendor/src/github.com/docker/libcontainer/init_linux.go b/vendor/src/github.com/docker/libcontainer/init_linux.go new file mode 100644 index 0000000000..1c5f6a87ee --- /dev/null +++ b/vendor/src/github.com/docker/libcontainer/init_linux.go @@ -0,0 +1,253 @@ +// +build linux + +package libcontainer + +import ( + "encoding/json" + "fmt" + "os" + "strings" + "syscall" + + log "github.com/Sirupsen/logrus" + "github.com/docker/libcontainer/cgroups" + "github.com/docker/libcontainer/configs" + "github.com/docker/libcontainer/netlink" + "github.com/docker/libcontainer/system" + "github.com/docker/libcontainer/user" + "github.com/docker/libcontainer/utils" +) + +type initType string + +const ( + initSetns initType = "setns" + initStandard initType = "standard" +) + +type pid struct { + Pid int `json:"pid"` +} + +// network is an internal struct used to setup container networks. +type network struct { + configs.Network + + // TempVethPeerName is a unique tempory veth peer name that was placed into + // the container's namespace. + TempVethPeerName string `json:"temp_veth_peer_name"` +} + +// initConfig is used for transferring parameters from Exec() to Init() +type initConfig struct { + Args []string `json:"args"` + Env []string `json:"env"` + Cwd string `json:"cwd"` + User string `json:"user"` + Config *configs.Config `json:"config"` + Console string `json:"console"` + Networks []*network `json:"network"` +} + +type initer interface { + Init() error +} + +func newContainerInit(t initType, pipe *os.File) (initer, error) { + var config *initConfig + if err := json.NewDecoder(pipe).Decode(&config); err != nil { + return nil, err + } + if err := populateProcessEnvironment(config.Env); err != nil { + return nil, err + } + switch t { + case initSetns: + return &linuxSetnsInit{ + config: config, + }, nil + case initStandard: + return &linuxStandardInit{ + config: config, + }, nil + } + return nil, fmt.Errorf("unknown init type %q", t) +} + +// populateProcessEnvironment loads the provided environment variables into the +// current processes's environment. +func populateProcessEnvironment(env []string) error { + for _, pair := range env { + p := strings.SplitN(pair, "=", 2) + if len(p) < 2 { + return fmt.Errorf("invalid environment '%v'", pair) + } + if err := os.Setenv(p[0], p[1]); err != nil { + return err + } + } + return nil +} + +// finalizeNamespace drops the caps, sets the correct user +// and working dir, and closes any leaked file descriptors +// before execing the command inside the namespace +func finalizeNamespace(config *initConfig) error { + // Ensure that all non-standard fds we may have accidentally + // inherited are marked close-on-exec so they stay out of the + // container + if err := utils.CloseExecFrom(3); err != nil { + return err + } + w, err := newCapWhitelist(config.Config.Capabilities) + if err != nil { + return err + } + // drop capabilities in bounding set before changing user + if err := w.dropBoundingSet(); err != nil { + return err + } + // preserve existing capabilities while we change users + if err := system.SetKeepCaps(); err != nil { + return err + } + if err := setupUser(config); err != nil { + return err + } + if err := system.ClearKeepCaps(); err != nil { + return err + } + // drop all other capabilities + if err := w.drop(); err != nil { + return err + } + if config.Cwd != "" { + if err := syscall.Chdir(config.Cwd); err != nil { + return err + } + } + return nil +} + +// joinExistingNamespaces gets all the namespace paths specified for the container and +// does a setns on the namespace fd so that the current process joins the namespace. +func joinExistingNamespaces(namespaces []configs.Namespace) error { + for _, ns := range namespaces { + if ns.Path != "" { + f, err := os.OpenFile(ns.Path, os.O_RDONLY, 0) + if err != nil { + return err + } + err = system.Setns(f.Fd(), uintptr(ns.Syscall())) + f.Close() + if err != nil { + return err + } + } + } + return nil +} + +// setupUser changes the groups, gid, and uid for the user inside the container +func setupUser(config *initConfig) error { + // Set up defaults. + defaultExecUser := user.ExecUser{ + Uid: syscall.Getuid(), + Gid: syscall.Getgid(), + Home: "/", + } + passwdPath, err := user.GetPasswdPath() + if err != nil { + return err + } + groupPath, err := user.GetGroupPath() + if err != nil { + return err + } + execUser, err := user.GetExecUserPath(config.User, &defaultExecUser, passwdPath, groupPath) + if err != nil { + return err + } + suppGroups := append(execUser.Sgids, config.Config.AdditionalGroups...) + if err := syscall.Setgroups(suppGroups); err != nil { + return err + } + if err := system.Setgid(execUser.Gid); err != nil { + return err + } + if err := system.Setuid(execUser.Uid); err != nil { + return err + } + // if we didn't get HOME already, set it based on the user's HOME + if envHome := os.Getenv("HOME"); envHome == "" { + if err := os.Setenv("HOME", execUser.Home); err != nil { + return err + } + } + return nil +} + +// setupNetwork sets up and initializes any network interface inside the container. +func setupNetwork(config *initConfig) error { + for _, config := range config.Networks { + strategy, err := getStrategy(config.Type) + if err != nil { + return err + } + if err := strategy.initialize(config); err != nil { + return err + } + } + return nil +} + +func setupRoute(config *configs.Config) error { + for _, config := range config.Routes { + if err := netlink.AddRoute(config.Destination, config.Source, config.Gateway, config.InterfaceName); err != nil { + return err + } + } + return nil +} + +func setupRlimits(config *configs.Config) error { + for _, rlimit := range config.Rlimits { + l := &syscall.Rlimit{Max: rlimit.Hard, Cur: rlimit.Soft} + if err := syscall.Setrlimit(rlimit.Type, l); err != nil { + return fmt.Errorf("error setting rlimit type %v: %v", rlimit.Type, err) + } + } + return nil +} + +// killCgroupProcesses freezes then iterates over all the processes inside the +// manager's cgroups sending a SIGKILL to each process then waiting for them to +// exit. +func killCgroupProcesses(m cgroups.Manager) error { + var procs []*os.Process + if err := m.Freeze(configs.Frozen); err != nil { + log.Warn(err) + } + pids, err := m.GetPids() + if err != nil { + m.Freeze(configs.Thawed) + return err + } + for _, pid := range pids { + if p, err := os.FindProcess(pid); err == nil { + procs = append(procs, p) + if err := p.Kill(); err != nil { + log.Warn(err) + } + } + } + if err := m.Freeze(configs.Thawed); err != nil { + log.Warn(err) + } + for _, p := range procs { + if _, err := p.Wait(); err != nil { + log.Warn(err) + } + } + return nil +} diff --git a/vendor/src/github.com/docker/libcontainer/integration/exec_test.go b/vendor/src/github.com/docker/libcontainer/integration/exec_test.go index fb1d1d7a10..b68cb739c3 100644 --- a/vendor/src/github.com/docker/libcontainer/integration/exec_test.go +++ b/vendor/src/github.com/docker/libcontainer/integration/exec_test.go @@ -1,34 +1,50 @@ package integration import ( + "bytes" + "io/ioutil" "os" "strings" "testing" "github.com/docker/libcontainer" + "github.com/docker/libcontainer/configs" ) func TestExecPS(t *testing.T) { + testExecPS(t, false) +} + +func TestUsernsExecPS(t *testing.T) { + if _, err := os.Stat("/proc/self/ns/user"); os.IsNotExist(err) { + t.Skip("userns is unsupported") + } + testExecPS(t, true) +} + +func testExecPS(t *testing.T, userns bool) { if testing.Short() { return } - - rootfs, err := newRootFs() + rootfs, err := newRootfs() if err != nil { t.Fatal(err) } defer remove(rootfs) - config := newTemplateConfig(rootfs) - buffers, exitCode, err := runContainer(config, "", "ps") - if err != nil { - t.Fatal(err) + if userns { + config.UidMappings = []configs.IDMap{{0, 0, 1000}} + config.GidMappings = []configs.IDMap{{0, 0, 1000}} + config.Namespaces = append(config.Namespaces, configs.Namespace{Type: configs.NEWUSER}) } + buffers, exitCode, err := runContainer(config, "", "ps") + if err != nil { + t.Fatalf("%s: %s", buffers, err) + } if exitCode != 0 { t.Fatalf("exit code not 0. code %d stderr %q", exitCode, buffers.Stderr) } - lines := strings.Split(buffers.Stdout.String(), "\n") if len(lines) < 2 { t.Fatalf("more than one process running for output %q", buffers.Stdout.String()) @@ -45,7 +61,7 @@ func TestIPCPrivate(t *testing.T) { return } - rootfs, err := newRootFs() + rootfs, err := newRootfs() if err != nil { t.Fatal(err) } @@ -76,7 +92,7 @@ func TestIPCHost(t *testing.T) { return } - rootfs, err := newRootFs() + rootfs, err := newRootfs() if err != nil { t.Fatal(err) } @@ -88,7 +104,7 @@ func TestIPCHost(t *testing.T) { } config := newTemplateConfig(rootfs) - config.Namespaces.Remove(libcontainer.NEWIPC) + config.Namespaces.Remove(configs.NEWIPC) buffers, exitCode, err := runContainer(config, "", "readlink", "/proc/self/ns/ipc") if err != nil { t.Fatal(err) @@ -108,7 +124,7 @@ func TestIPCJoinPath(t *testing.T) { return } - rootfs, err := newRootFs() + rootfs, err := newRootfs() if err != nil { t.Fatal(err) } @@ -120,7 +136,7 @@ func TestIPCJoinPath(t *testing.T) { } config := newTemplateConfig(rootfs) - config.Namespaces.Add(libcontainer.NEWIPC, "/proc/1/ns/ipc") + config.Namespaces.Add(configs.NEWIPC, "/proc/1/ns/ipc") buffers, exitCode, err := runContainer(config, "", "readlink", "/proc/self/ns/ipc") if err != nil { @@ -141,14 +157,14 @@ func TestIPCBadPath(t *testing.T) { return } - rootfs, err := newRootFs() + rootfs, err := newRootfs() if err != nil { t.Fatal(err) } defer remove(rootfs) config := newTemplateConfig(rootfs) - config.Namespaces.Add(libcontainer.NEWIPC, "/proc/1/ns/ipcc") + config.Namespaces.Add(configs.NEWIPC, "/proc/1/ns/ipcc") _, _, err = runContainer(config, "", "true") if err == nil { @@ -161,7 +177,7 @@ func TestRlimit(t *testing.T) { return } - rootfs, err := newRootFs() + rootfs, err := newRootfs() if err != nil { t.Fatal(err) } @@ -172,38 +188,289 @@ func TestRlimit(t *testing.T) { if err != nil { t.Fatal(err) } - if limit := strings.TrimSpace(out.Stdout.String()); limit != "1024" { - t.Fatalf("expected rlimit to be 1024, got %s", limit) + if limit := strings.TrimSpace(out.Stdout.String()); limit != "1025" { + t.Fatalf("expected rlimit to be 1025, got %s", limit) } } -func TestPIDNSPrivate(t *testing.T) { +func newTestRoot() (string, error) { + dir, err := ioutil.TempDir("", "libcontainer") + if err != nil { + return "", err + } + if err := os.MkdirAll(dir, 0700); err != nil { + return "", err + } + return dir, nil +} + +func waitProcess(p *libcontainer.Process, t *testing.T) { + status, err := p.Wait() + if err != nil { + t.Fatal(err) + } + if !status.Success() { + t.Fatal(status) + } +} + +func TestEnter(t *testing.T) { if testing.Short() { return } + root, err := newTestRoot() + if err != nil { + t.Fatal(err) + } + defer os.RemoveAll(root) - rootfs, err := newRootFs() + rootfs, err := newRootfs() if err != nil { t.Fatal(err) } defer remove(rootfs) - l, err := os.Readlink("/proc/1/ns/pid") - if err != nil { - t.Fatal(err) - } - config := newTemplateConfig(rootfs) - buffers, exitCode, err := runContainer(config, "", "readlink", "/proc/self/ns/pid") + + factory, err := libcontainer.New(root, libcontainer.Cgroupfs) if err != nil { t.Fatal(err) } - if exitCode != 0 { - t.Fatalf("exit code not 0. code %d stderr %q", exitCode, buffers.Stderr) + container, err := factory.Create("test", config) + if err != nil { + t.Fatal(err) + } + defer container.Destroy() + + // Execute a first process in the container + stdinR, stdinW, err := os.Pipe() + if err != nil { + t.Fatal(err) } - if actual := strings.Trim(buffers.Stdout.String(), "\n"); actual == l { - t.Fatalf("pid link should be private to the container but equals host %q %q", actual, l) + var stdout, stdout2 bytes.Buffer + + pconfig := libcontainer.Process{ + Args: []string{"sh", "-c", "cat && readlink /proc/self/ns/pid"}, + Env: standardEnvironment, + Stdin: stdinR, + Stdout: &stdout, + } + err = container.Start(&pconfig) + stdinR.Close() + defer stdinW.Close() + if err != nil { + t.Fatal(err) + } + pid, err := pconfig.Pid() + if err != nil { + t.Fatal(err) + } + + // Execute another process in the container + stdinR2, stdinW2, err := os.Pipe() + if err != nil { + t.Fatal(err) + } + pconfig2 := libcontainer.Process{ + Env: standardEnvironment, + } + pconfig2.Args = []string{"sh", "-c", "cat && readlink /proc/self/ns/pid"} + pconfig2.Stdin = stdinR2 + pconfig2.Stdout = &stdout2 + + err = container.Start(&pconfig2) + stdinR2.Close() + defer stdinW2.Close() + if err != nil { + t.Fatal(err) + } + + pid2, err := pconfig2.Pid() + if err != nil { + t.Fatal(err) + } + + processes, err := container.Processes() + if err != nil { + t.Fatal(err) + } + + n := 0 + for i := range processes { + if processes[i] == pid || processes[i] == pid2 { + n++ + } + } + if n != 2 { + t.Fatal("unexpected number of processes", processes, pid, pid2) + } + + // Wait processes + stdinW2.Close() + waitProcess(&pconfig2, t) + + stdinW.Close() + waitProcess(&pconfig, t) + + // Check that both processes live in the same pidns + pidns := string(stdout.Bytes()) + if err != nil { + t.Fatal(err) + } + + pidns2 := string(stdout2.Bytes()) + if err != nil { + t.Fatal(err) + } + + if pidns != pidns2 { + t.Fatal("The second process isn't in the required pid namespace", pidns, pidns2) + } +} + +func TestProcessEnv(t *testing.T) { + if testing.Short() { + return + } + root, err := newTestRoot() + if err != nil { + t.Fatal(err) + } + defer os.RemoveAll(root) + + rootfs, err := newRootfs() + if err != nil { + t.Fatal(err) + } + defer remove(rootfs) + + config := newTemplateConfig(rootfs) + + factory, err := libcontainer.New(root, libcontainer.Cgroupfs) + if err != nil { + t.Fatal(err) + } + + container, err := factory.Create("test", config) + if err != nil { + t.Fatal(err) + } + defer container.Destroy() + + var stdout bytes.Buffer + pconfig := libcontainer.Process{ + Args: []string{"sh", "-c", "env"}, + Env: []string{ + "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", + "HOSTNAME=integration", + "TERM=xterm", + "FOO=BAR", + }, + Stdin: nil, + Stdout: &stdout, + } + err = container.Start(&pconfig) + if err != nil { + t.Fatal(err) + } + + // Wait for process + waitProcess(&pconfig, t) + + outputEnv := string(stdout.Bytes()) + if err != nil { + t.Fatal(err) + } + + // Check that the environment has the key/value pair we added + if !strings.Contains(outputEnv, "FOO=BAR") { + t.Fatal("Environment doesn't have the expected FOO=BAR key/value pair: ", outputEnv) + } + + // Make sure that HOME is set + if !strings.Contains(outputEnv, "HOME=/root") { + t.Fatal("Environment doesn't have HOME set: ", outputEnv) + } +} + +func TestFreeze(t *testing.T) { + if testing.Short() { + return + } + root, err := newTestRoot() + if err != nil { + t.Fatal(err) + } + defer os.RemoveAll(root) + + rootfs, err := newRootfs() + if err != nil { + t.Fatal(err) + } + defer remove(rootfs) + + config := newTemplateConfig(rootfs) + + factory, err := libcontainer.New(root, libcontainer.Cgroupfs) + if err != nil { + t.Fatal(err) + } + + container, err := factory.Create("test", config) + if err != nil { + t.Fatal(err) + } + defer container.Destroy() + + stdinR, stdinW, err := os.Pipe() + if err != nil { + t.Fatal(err) + } + + pconfig := libcontainer.Process{ + Args: []string{"cat"}, + Env: standardEnvironment, + Stdin: stdinR, + } + err = container.Start(&pconfig) + stdinR.Close() + defer stdinW.Close() + if err != nil { + t.Fatal(err) + } + + pid, err := pconfig.Pid() + if err != nil { + t.Fatal(err) + } + + process, err := os.FindProcess(pid) + if err != nil { + t.Fatal(err) + } + + if err := container.Pause(); err != nil { + t.Fatal(err) + } + state, err := container.Status() + if err != nil { + t.Fatal(err) + } + if err := container.Resume(); err != nil { + t.Fatal(err) + } + if state != libcontainer.Paused { + t.Fatal("Unexpected state: ", state) + } + + stdinW.Close() + s, err := process.Wait() + if err != nil { + t.Fatal(err) + } + if !s.Success() { + t.Fatal(s.String()) } } diff --git a/vendor/src/github.com/docker/libcontainer/integration/execin_test.go b/vendor/src/github.com/docker/libcontainer/integration/execin_test.go index 86d9c5c260..252e6e415e 100644 --- a/vendor/src/github.com/docker/libcontainer/integration/execin_test.go +++ b/vendor/src/github.com/docker/libcontainer/integration/execin_test.go @@ -1,62 +1,70 @@ package integration import ( + "bytes" + "io" "os" - "os/exec" "strings" - "sync" "testing" + "time" "github.com/docker/libcontainer" - "github.com/docker/libcontainer/namespaces" ) func TestExecIn(t *testing.T) { if testing.Short() { return } - - rootfs, err := newRootFs() + rootfs, err := newRootfs() if err != nil { t.Fatal(err) } defer remove(rootfs) - config := newTemplateConfig(rootfs) - if err := writeConfig(config); err != nil { - t.Fatalf("failed to write config %s", err) - } - - containerCmd, statePath, containerErr := startLongRunningContainer(config) - defer func() { - // kill the container - if containerCmd.Process != nil { - containerCmd.Process.Kill() - } - if err := <-containerErr; err != nil { - t.Fatal(err) - } - }() - - // start the exec process - state, err := libcontainer.GetState(statePath) + container, err := newContainer(config) if err != nil { - t.Fatalf("failed to get state %s", err) + t.Fatal(err) } - buffers := newStdBuffers() - execErr := make(chan error, 1) - go func() { - _, err := namespaces.ExecIn(config, state, []string{"ps"}, - os.Args[0], "exec", buffers.Stdin, buffers.Stdout, buffers.Stderr, - "", nil) - execErr <- err - }() - if err := <-execErr; err != nil { - t.Fatalf("exec finished with error %s", err) + defer container.Destroy() + + // Execute a first process in the container + stdinR, stdinW, err := os.Pipe() + if err != nil { + t.Fatal(err) + } + process := &libcontainer.Process{ + Args: []string{"cat"}, + Env: standardEnvironment, + Stdin: stdinR, + } + err = container.Start(process) + stdinR.Close() + defer stdinW.Close() + if err != nil { + t.Fatal(err) } + buffers := newStdBuffers() + ps := &libcontainer.Process{ + Args: []string{"ps"}, + Env: standardEnvironment, + Stdin: buffers.Stdin, + Stdout: buffers.Stdout, + Stderr: buffers.Stderr, + } + err = container.Start(ps) + if err != nil { + t.Fatal(err) + } + if _, err := ps.Wait(); err != nil { + t.Fatal(err) + } + stdinW.Close() + if _, err := process.Wait(); err != nil { + t.Log(err) + } out := buffers.Stdout.String() - if !strings.Contains(out, "sleep 10") || !strings.Contains(out, "ps") { + if !strings.Contains(out, "cat") || !strings.Contains(out, "ps") { t.Fatalf("unexpected running process, output %q", out) } } @@ -65,76 +73,244 @@ func TestExecInRlimit(t *testing.T) { if testing.Short() { return } - - rootfs, err := newRootFs() + rootfs, err := newRootfs() if err != nil { t.Fatal(err) } defer remove(rootfs) - config := newTemplateConfig(rootfs) - if err := writeConfig(config); err != nil { - t.Fatalf("failed to write config %s", err) - } - - containerCmd, statePath, containerErr := startLongRunningContainer(config) - defer func() { - // kill the container - if containerCmd.Process != nil { - containerCmd.Process.Kill() - } - if err := <-containerErr; err != nil { - t.Fatal(err) - } - }() - - // start the exec process - state, err := libcontainer.GetState(statePath) + container, err := newContainer(config) if err != nil { - t.Fatalf("failed to get state %s", err) + t.Fatal(err) } + defer container.Destroy() + + stdinR, stdinW, err := os.Pipe() + if err != nil { + t.Fatal(err) + } + process := &libcontainer.Process{ + Args: []string{"cat"}, + Env: standardEnvironment, + Stdin: stdinR, + } + err = container.Start(process) + stdinR.Close() + defer stdinW.Close() + if err != nil { + t.Fatal(err) + } + buffers := newStdBuffers() - execErr := make(chan error, 1) - go func() { - _, err := namespaces.ExecIn(config, state, []string{"/bin/sh", "-c", "ulimit -n"}, - os.Args[0], "exec", buffers.Stdin, buffers.Stdout, buffers.Stderr, - "", nil) - execErr <- err - }() - if err := <-execErr; err != nil { - t.Fatalf("exec finished with error %s", err) + ps := &libcontainer.Process{ + Args: []string{"/bin/sh", "-c", "ulimit -n"}, + Env: standardEnvironment, + Stdin: buffers.Stdin, + Stdout: buffers.Stdout, + Stderr: buffers.Stderr, + } + err = container.Start(ps) + if err != nil { + t.Fatal(err) + } + if _, err := ps.Wait(); err != nil { + t.Fatal(err) + } + stdinW.Close() + if _, err := process.Wait(); err != nil { + t.Log(err) } - out := buffers.Stdout.String() - if limit := strings.TrimSpace(out); limit != "1024" { - t.Fatalf("expected rlimit to be 1024, got %s", limit) + if limit := strings.TrimSpace(out); limit != "1025" { + t.Fatalf("expected rlimit to be 1025, got %s", limit) } } -// start a long-running container so we have time to inspect execin processes -func startLongRunningContainer(config *libcontainer.Config) (*exec.Cmd, string, chan error) { - containerErr := make(chan error, 1) - containerCmd := &exec.Cmd{} - var statePath string - - createCmd := func(container *libcontainer.Config, console, dataPath, init string, - pipe *os.File, args []string) *exec.Cmd { - containerCmd = namespaces.DefaultCreateCommand(container, console, dataPath, init, pipe, args) - statePath = dataPath - return containerCmd +func TestExecInError(t *testing.T) { + if testing.Short() { + return } + rootfs, err := newRootfs() + if err != nil { + t.Fatal(err) + } + defer remove(rootfs) + config := newTemplateConfig(rootfs) + container, err := newContainer(config) + if err != nil { + t.Fatal(err) + } + defer container.Destroy() - var containerStart sync.WaitGroup - containerStart.Add(1) - go func() { - buffers := newStdBuffers() - _, err := namespaces.Exec(config, - buffers.Stdin, buffers.Stdout, buffers.Stderr, - "", config.RootFs, []string{"sleep", "10"}, - createCmd, containerStart.Done) - containerErr <- err + // Execute a first process in the container + stdinR, stdinW, err := os.Pipe() + if err != nil { + t.Fatal(err) + } + process := &libcontainer.Process{ + Args: []string{"cat"}, + Env: standardEnvironment, + Stdin: stdinR, + } + err = container.Start(process) + stdinR.Close() + defer func() { + stdinW.Close() + if _, err := process.Wait(); err != nil { + t.Log(err) + } }() - containerStart.Wait() + if err != nil { + t.Fatal(err) + } - return containerCmd, statePath, containerErr + unexistent := &libcontainer.Process{ + Args: []string{"unexistent"}, + Env: standardEnvironment, + } + err = container.Start(unexistent) + if err == nil { + t.Fatal("Should be an error") + } + if !strings.Contains(err.Error(), "executable file not found") { + t.Fatalf("Should be error about not found executable, got %s", err) + } +} + +func TestExecInTTY(t *testing.T) { + if testing.Short() { + return + } + rootfs, err := newRootfs() + if err != nil { + t.Fatal(err) + } + defer remove(rootfs) + config := newTemplateConfig(rootfs) + container, err := newContainer(config) + if err != nil { + t.Fatal(err) + } + defer container.Destroy() + + // Execute a first process in the container + stdinR, stdinW, err := os.Pipe() + if err != nil { + t.Fatal(err) + } + process := &libcontainer.Process{ + Args: []string{"cat"}, + Env: standardEnvironment, + Stdin: stdinR, + } + err = container.Start(process) + stdinR.Close() + defer stdinW.Close() + if err != nil { + t.Fatal(err) + } + + var stdout bytes.Buffer + ps := &libcontainer.Process{ + Args: []string{"ps"}, + Env: standardEnvironment, + } + console, err := ps.NewConsole(0) + copy := make(chan struct{}) + go func() { + io.Copy(&stdout, console) + close(copy) + }() + if err != nil { + t.Fatal(err) + } + err = container.Start(ps) + if err != nil { + t.Fatal(err) + } + select { + case <-time.After(5 * time.Second): + t.Fatal("Waiting for copy timed out") + case <-copy: + } + if _, err := ps.Wait(); err != nil { + t.Fatal(err) + } + stdinW.Close() + if _, err := process.Wait(); err != nil { + t.Log(err) + } + out := stdout.String() + if !strings.Contains(out, "cat") || !strings.Contains(string(out), "ps") { + t.Fatalf("unexpected running process, output %q", out) + } +} + +func TestExecInEnvironment(t *testing.T) { + if testing.Short() { + return + } + rootfs, err := newRootfs() + if err != nil { + t.Fatal(err) + } + defer remove(rootfs) + config := newTemplateConfig(rootfs) + container, err := newContainer(config) + if err != nil { + t.Fatal(err) + } + defer container.Destroy() + + // Execute a first process in the container + stdinR, stdinW, err := os.Pipe() + if err != nil { + t.Fatal(err) + } + process := &libcontainer.Process{ + Args: []string{"cat"}, + Env: standardEnvironment, + Stdin: stdinR, + } + err = container.Start(process) + stdinR.Close() + defer stdinW.Close() + if err != nil { + t.Fatal(err) + } + + buffers := newStdBuffers() + process2 := &libcontainer.Process{ + Args: []string{"env"}, + Env: []string{ + "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", + "DEBUG=true", + "DEBUG=false", + "ENV=test", + }, + Stdin: buffers.Stdin, + Stdout: buffers.Stdout, + Stderr: buffers.Stderr, + } + err = container.Start(process2) + if err != nil { + t.Fatal(err) + } + if _, err := process2.Wait(); err != nil { + out := buffers.Stdout.String() + t.Fatal(err, out) + } + stdinW.Close() + if _, err := process.Wait(); err != nil { + t.Log(err) + } + out := buffers.Stdout.String() + // check execin's process environment + if !strings.Contains(out, "DEBUG=false") || + !strings.Contains(out, "ENV=test") || + !strings.Contains(out, "HOME=/root") || + !strings.Contains(out, "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin") || + strings.Contains(out, "DEBUG=true") { + t.Fatalf("unexpected running process, output %q", out) + } } diff --git a/vendor/src/github.com/docker/libcontainer/integration/init_test.go b/vendor/src/github.com/docker/libcontainer/integration/init_test.go index 3106a5fb1e..f11834de34 100644 --- a/vendor/src/github.com/docker/libcontainer/integration/init_test.go +++ b/vendor/src/github.com/docker/libcontainer/integration/init_test.go @@ -1,76 +1,27 @@ package integration import ( - "encoding/json" "log" "os" "runtime" "github.com/docker/libcontainer" - "github.com/docker/libcontainer/namespaces" - _ "github.com/docker/libcontainer/namespaces/nsenter" + _ "github.com/docker/libcontainer/nsenter" ) // init runs the libcontainer initialization code because of the busybox style needs // to work around the go runtime and the issues with forking func init() { - if len(os.Args) < 2 { + if len(os.Args) < 2 || os.Args[1] != "init" { return } - // handle init - if len(os.Args) >= 2 && os.Args[1] == "init" { - runtime.LockOSThread() - - container, err := loadConfig() - if err != nil { - log.Fatal(err) - } - - rootfs, err := os.Getwd() - if err != nil { - log.Fatal(err) - } - - if err := namespaces.Init(container, rootfs, "", os.NewFile(3, "pipe"), os.Args[3:]); err != nil { - log.Fatalf("unable to initialize for container: %s", err) - } - os.Exit(1) + runtime.GOMAXPROCS(1) + runtime.LockOSThread() + factory, err := libcontainer.New("") + if err != nil { + log.Fatalf("unable to initialize for container: %s", err) } - - // handle execin - if len(os.Args) >= 2 && os.Args[0] == "nsenter-exec" { - runtime.LockOSThread() - - // User args are passed after '--' in the command line. - userArgs := findUserArgs() - - config, err := loadConfigFromFd() - if err != nil { - log.Fatalf("docker-exec: unable to receive config from sync pipe: %s", err) - } - - if err := namespaces.FinalizeSetns(config, userArgs); err != nil { - log.Fatalf("docker-exec: failed to exec: %s", err) - } - os.Exit(1) + if err := factory.StartInitialization(3); err != nil { + log.Fatal(err) } } - -func findUserArgs() []string { - for i, a := range os.Args { - if a == "--" { - return os.Args[i+1:] - } - } - return []string{} -} - -// loadConfigFromFd loads a container's config from the sync pipe that is provided by -// fd 3 when running a process -func loadConfigFromFd() (*libcontainer.Config, error) { - var config *libcontainer.Config - if err := json.NewDecoder(os.NewFile(3, "child")).Decode(&config); err != nil { - return nil, err - } - return config, nil -} diff --git a/vendor/src/github.com/docker/libcontainer/integration/template_test.go b/vendor/src/github.com/docker/libcontainer/integration/template_test.go index 98846eb199..45acaf77c8 100644 --- a/vendor/src/github.com/docker/libcontainer/integration/template_test.go +++ b/vendor/src/github.com/docker/libcontainer/integration/template_test.go @@ -3,19 +3,25 @@ package integration import ( "syscall" - "github.com/docker/libcontainer" - "github.com/docker/libcontainer/cgroups" - "github.com/docker/libcontainer/devices" + "github.com/docker/libcontainer/configs" ) +var standardEnvironment = []string{ + "HOME=/root", + "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", + "HOSTNAME=integration", + "TERM=xterm", +} + +const defaultMountFlags = syscall.MS_NOEXEC | syscall.MS_NOSUID | syscall.MS_NODEV + // newTemplateConfig returns a base template for running a container // // it uses a network strategy of just setting a loopback interface // and the default setup for devices -func newTemplateConfig(rootfs string) *libcontainer.Config { - return &libcontainer.Config{ - RootFs: rootfs, - Tty: false, +func newTemplateConfig(rootfs string) *configs.Config { + return &configs.Config{ + Rootfs: rootfs, Capabilities: []string{ "CHOWN", "DAC_OVERRIDE", @@ -32,41 +38,60 @@ func newTemplateConfig(rootfs string) *libcontainer.Config { "KILL", "AUDIT_WRITE", }, - Namespaces: libcontainer.Namespaces([]libcontainer.Namespace{ - {Type: libcontainer.NEWNS}, - {Type: libcontainer.NEWUTS}, - {Type: libcontainer.NEWIPC}, - {Type: libcontainer.NEWPID}, - {Type: libcontainer.NEWNET}, + Namespaces: configs.Namespaces([]configs.Namespace{ + {Type: configs.NEWNS}, + {Type: configs.NEWUTS}, + {Type: configs.NEWIPC}, + {Type: configs.NEWPID}, + {Type: configs.NEWNET}, }), - Cgroups: &cgroups.Cgroup{ + Cgroups: &configs.Cgroup{ + Name: "test", Parent: "integration", AllowAllDevices: false, - AllowedDevices: devices.DefaultAllowedDevices, + AllowedDevices: configs.DefaultAllowedDevices, }, - - MountConfig: &libcontainer.MountConfig{ - DeviceNodes: devices.DefaultAutoCreatedDevices, + MaskPaths: []string{ + "/proc/kcore", }, + ReadonlyPaths: []string{ + "/proc/sys", "/proc/sysrq-trigger", "/proc/irq", "/proc/bus", + }, + Devices: configs.DefaultAutoCreatedDevices, Hostname: "integration", - Env: []string{ - "HOME=/root", - "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", - "HOSTNAME=integration", - "TERM=xterm", + Mounts: []*configs.Mount{ + { + Device: "tmpfs", + Source: "shm", + Destination: "/dev/shm", + Data: "mode=1777,size=65536k", + Flags: defaultMountFlags, + }, + { + Source: "mqueue", + Destination: "/dev/mqueue", + Device: "mqueue", + Flags: defaultMountFlags, + }, + { + Source: "sysfs", + Destination: "/sys", + Device: "sysfs", + Flags: defaultMountFlags | syscall.MS_RDONLY, + }, }, - Networks: []*libcontainer.Network{ + Networks: []*configs.Network{ { Type: "loopback", Address: "127.0.0.1/0", Gateway: "localhost", }, }, - Rlimits: []libcontainer.Rlimit{ + Rlimits: []configs.Rlimit{ { Type: syscall.RLIMIT_NOFILE, - Hard: uint64(1024), - Soft: uint64(1024), + Hard: uint64(1025), + Soft: uint64(1025), }, }, } diff --git a/vendor/src/github.com/docker/libcontainer/integration/utils_test.go b/vendor/src/github.com/docker/libcontainer/integration/utils_test.go index 6393fb9982..c444eecfc5 100644 --- a/vendor/src/github.com/docker/libcontainer/integration/utils_test.go +++ b/vendor/src/github.com/docker/libcontainer/integration/utils_test.go @@ -2,15 +2,15 @@ package integration import ( "bytes" - "encoding/json" "fmt" "io/ioutil" "os" "os/exec" - "path/filepath" + "strings" + "syscall" "github.com/docker/libcontainer" - "github.com/docker/libcontainer/namespaces" + "github.com/docker/libcontainer/configs" ) func newStdBuffers() *stdBuffers { @@ -27,31 +27,19 @@ type stdBuffers struct { Stderr *bytes.Buffer } -func writeConfig(config *libcontainer.Config) error { - f, err := os.OpenFile(filepath.Join(config.RootFs, "container.json"), os.O_CREATE|os.O_RDWR|os.O_TRUNC, 0700) - if err != nil { - return err +func (b *stdBuffers) String() string { + s := []string{} + if b.Stderr != nil { + s = append(s, b.Stderr.String()) } - defer f.Close() - return json.NewEncoder(f).Encode(config) + if b.Stdout != nil { + s = append(s, b.Stdout.String()) + } + return strings.Join(s, "|") } -func loadConfig() (*libcontainer.Config, error) { - f, err := os.Open(filepath.Join(os.Getenv("data_path"), "container.json")) - if err != nil { - return nil, err - } - defer f.Close() - - var container *libcontainer.Config - if err := json.NewDecoder(f).Decode(&container); err != nil { - return nil, err - } - return container, nil -} - -// newRootFs creates a new tmp directory and copies the busybox root filesystem -func newRootFs() (string, error) { +// newRootfs creates a new tmp directory and copies the busybox root filesystem +func newRootfs() (string, error) { dir, err := ioutil.TempDir("", "") if err != nil { return "", err @@ -79,17 +67,51 @@ func copyBusybox(dest string) error { return nil } +func newContainer(config *configs.Config) (libcontainer.Container, error) { + factory, err := libcontainer.New(".", + libcontainer.InitArgs(os.Args[0], "init", "--"), + libcontainer.Cgroupfs, + ) + if err != nil { + return nil, err + } + return factory.Create("testCT", config) +} + // runContainer runs the container with the specific config and arguments // // buffers are returned containing the STDOUT and STDERR output for the run // along with the exit code and any go error -func runContainer(config *libcontainer.Config, console string, args ...string) (buffers *stdBuffers, exitCode int, err error) { - if err := writeConfig(config); err != nil { +func runContainer(config *configs.Config, console string, args ...string) (buffers *stdBuffers, exitCode int, err error) { + container, err := newContainer(config) + if err != nil { return nil, -1, err } - + defer container.Destroy() buffers = newStdBuffers() - exitCode, err = namespaces.Exec(config, buffers.Stdin, buffers.Stdout, buffers.Stderr, - console, config.RootFs, args, namespaces.DefaultCreateCommand, nil) + process := &libcontainer.Process{ + Args: args, + Env: standardEnvironment, + Stdin: buffers.Stdin, + Stdout: buffers.Stdout, + Stderr: buffers.Stderr, + } + + err = container.Start(process) + if err != nil { + return nil, -1, err + } + ps, err := process.Wait() + if err != nil { + return nil, -1, err + } + status := ps.Sys().(syscall.WaitStatus) + if status.Exited() { + exitCode = status.ExitStatus() + } else if status.Signaled() { + exitCode = -int(status.Signal()) + } else { + return nil, -1, err + } return } diff --git a/vendor/src/github.com/docker/libcontainer/mount/init.go b/vendor/src/github.com/docker/libcontainer/mount/init.go deleted file mode 100644 index a2c3d52026..0000000000 --- a/vendor/src/github.com/docker/libcontainer/mount/init.go +++ /dev/null @@ -1,209 +0,0 @@ -// +build linux - -package mount - -import ( - "fmt" - "os" - "path/filepath" - "syscall" - - "github.com/docker/libcontainer/label" - "github.com/docker/libcontainer/mount/nodes" -) - -// default mount point flags -const defaultMountFlags = syscall.MS_NOEXEC | syscall.MS_NOSUID | syscall.MS_NODEV - -type mount struct { - source string - path string - device string - flags int - data string -} - -// InitializeMountNamespace sets up the devices, mount points, and filesystems for use inside a -// new mount namespace. -func InitializeMountNamespace(rootfs, console string, sysReadonly bool, mountConfig *MountConfig) error { - var ( - err error - flag = syscall.MS_PRIVATE - ) - - if mountConfig.NoPivotRoot { - flag = syscall.MS_SLAVE - } - - if err := syscall.Mount("", "/", "", uintptr(flag|syscall.MS_REC), ""); err != nil { - return fmt.Errorf("mounting / with flags %X %s", (flag | syscall.MS_REC), err) - } - - if err := syscall.Mount(rootfs, rootfs, "bind", syscall.MS_BIND|syscall.MS_REC, ""); err != nil { - return fmt.Errorf("mouting %s as bind %s", rootfs, err) - } - - if err := mountSystem(rootfs, sysReadonly, mountConfig); err != nil { - return fmt.Errorf("mount system %s", err) - } - - // apply any user specified mounts within the new mount namespace - for _, m := range mountConfig.Mounts { - if err := m.Mount(rootfs, mountConfig.MountLabel); err != nil { - return err - } - } - - if err := nodes.CreateDeviceNodes(rootfs, mountConfig.DeviceNodes); err != nil { - return fmt.Errorf("create device nodes %s", err) - } - - if err := SetupPtmx(rootfs, console, mountConfig.MountLabel); err != nil { - return err - } - - // stdin, stdout and stderr could be pointing to /dev/null from parent namespace. - // Re-open them inside this namespace. - if err := reOpenDevNull(rootfs); err != nil { - return fmt.Errorf("Failed to reopen /dev/null %s", err) - } - - if err := setupDevSymlinks(rootfs); err != nil { - return fmt.Errorf("dev symlinks %s", err) - } - - if err := syscall.Chdir(rootfs); err != nil { - return fmt.Errorf("chdir into %s %s", rootfs, err) - } - - if mountConfig.NoPivotRoot { - err = MsMoveRoot(rootfs) - } else { - err = PivotRoot(rootfs) - } - - if err != nil { - return err - } - - if mountConfig.ReadonlyFs { - if err := SetReadonly(); err != nil { - return fmt.Errorf("set readonly %s", err) - } - } - - syscall.Umask(0022) - - return nil -} - -// mountSystem sets up linux specific system mounts like mqueue, sys, proc, shm, and devpts -// inside the mount namespace -func mountSystem(rootfs string, sysReadonly bool, mountConfig *MountConfig) error { - for _, m := range newSystemMounts(rootfs, mountConfig.MountLabel, sysReadonly) { - if err := os.MkdirAll(m.path, 0755); err != nil && !os.IsExist(err) { - return fmt.Errorf("mkdirall %s %s", m.path, err) - } - if err := syscall.Mount(m.source, m.path, m.device, uintptr(m.flags), m.data); err != nil { - return fmt.Errorf("mounting %s into %s %s", m.source, m.path, err) - } - } - return nil -} - -func createIfNotExists(path string, isDir bool) error { - if _, err := os.Stat(path); err != nil { - if os.IsNotExist(err) { - if isDir { - if err := os.MkdirAll(path, 0755); err != nil { - return err - } - } else { - if err := os.MkdirAll(filepath.Dir(path), 0755); err != nil { - return err - } - f, err := os.OpenFile(path, os.O_CREATE, 0755) - if err != nil { - return err - } - f.Close() - } - } - } - return nil -} - -func setupDevSymlinks(rootfs string) error { - var links = [][2]string{ - {"/proc/self/fd", "/dev/fd"}, - {"/proc/self/fd/0", "/dev/stdin"}, - {"/proc/self/fd/1", "/dev/stdout"}, - {"/proc/self/fd/2", "/dev/stderr"}, - } - - // kcore support can be toggled with CONFIG_PROC_KCORE; only create a symlink - // in /dev if it exists in /proc. - if _, err := os.Stat("/proc/kcore"); err == nil { - links = append(links, [2]string{"/proc/kcore", "/dev/kcore"}) - } - - for _, link := range links { - var ( - src = link[0] - dst = filepath.Join(rootfs, link[1]) - ) - - if err := os.Symlink(src, dst); err != nil && !os.IsExist(err) { - return fmt.Errorf("symlink %s %s %s", src, dst, err) - } - } - - return nil -} - -// TODO: this is crappy right now and should be cleaned up with a better way of handling system and -// standard bind mounts allowing them to be more dynamic -func newSystemMounts(rootfs, mountLabel string, sysReadonly bool) []mount { - systemMounts := []mount{ - {source: "proc", path: filepath.Join(rootfs, "proc"), device: "proc", flags: defaultMountFlags}, - {source: "tmpfs", path: filepath.Join(rootfs, "dev"), device: "tmpfs", flags: syscall.MS_NOSUID | syscall.MS_STRICTATIME, data: label.FormatMountLabel("mode=755", mountLabel)}, - {source: "shm", path: filepath.Join(rootfs, "dev", "shm"), device: "tmpfs", flags: defaultMountFlags, data: label.FormatMountLabel("mode=1777,size=65536k", mountLabel)}, - {source: "mqueue", path: filepath.Join(rootfs, "dev", "mqueue"), device: "mqueue", flags: defaultMountFlags}, - {source: "devpts", path: filepath.Join(rootfs, "dev", "pts"), device: "devpts", flags: syscall.MS_NOSUID | syscall.MS_NOEXEC, data: label.FormatMountLabel("newinstance,ptmxmode=0666,mode=620,gid=5", mountLabel)}, - } - - sysMountFlags := defaultMountFlags - if sysReadonly { - sysMountFlags |= syscall.MS_RDONLY - } - - systemMounts = append(systemMounts, mount{source: "sysfs", path: filepath.Join(rootfs, "sys"), device: "sysfs", flags: sysMountFlags}) - - return systemMounts -} - -// Is stdin, stdout or stderr were to be pointing to '/dev/null', -// this method will make them point to '/dev/null' from within this namespace. -func reOpenDevNull(rootfs string) error { - var stat, devNullStat syscall.Stat_t - file, err := os.Open(filepath.Join(rootfs, "/dev/null")) - if err != nil { - return fmt.Errorf("Failed to open /dev/null - %s", err) - } - defer file.Close() - if err = syscall.Fstat(int(file.Fd()), &devNullStat); err != nil { - return fmt.Errorf("Failed to stat /dev/null - %s", err) - } - for fd := 0; fd < 3; fd++ { - if err = syscall.Fstat(fd, &stat); err != nil { - return fmt.Errorf("Failed to stat fd %d - %s", fd, err) - } - if stat.Rdev == devNullStat.Rdev { - // Close and re-open the fd. - if err = syscall.Dup2(int(file.Fd()), fd); err != nil { - return fmt.Errorf("Failed to dup fd %d to fd %d - %s", file.Fd(), fd, err) - } - } - } - return nil -} diff --git a/vendor/src/github.com/docker/libcontainer/mount/mount.go b/vendor/src/github.com/docker/libcontainer/mount/mount.go deleted file mode 100644 index c1b424214f..0000000000 --- a/vendor/src/github.com/docker/libcontainer/mount/mount.go +++ /dev/null @@ -1,109 +0,0 @@ -package mount - -import ( - "fmt" - "os" - "path/filepath" - "syscall" - - "github.com/docker/docker/pkg/symlink" - "github.com/docker/libcontainer/label" -) - -type Mount struct { - Type string `json:"type,omitempty"` - Source string `json:"source,omitempty"` // Source path, in the host namespace - Destination string `json:"destination,omitempty"` // Destination path, in the container - Writable bool `json:"writable,omitempty"` - Relabel string `json:"relabel,omitempty"` // Relabel source if set, "z" indicates shared, "Z" indicates unshared - Private bool `json:"private,omitempty"` - Slave bool `json:"slave,omitempty"` -} - -func (m *Mount) Mount(rootfs, mountLabel string) error { - switch m.Type { - case "bind": - return m.bindMount(rootfs, mountLabel) - case "tmpfs": - return m.tmpfsMount(rootfs, mountLabel) - default: - return fmt.Errorf("unsupported mount type %s for %s", m.Type, m.Destination) - } -} - -func (m *Mount) bindMount(rootfs, mountLabel string) error { - var ( - flags = syscall.MS_BIND | syscall.MS_REC - dest = filepath.Join(rootfs, m.Destination) - ) - - if !m.Writable { - flags = flags | syscall.MS_RDONLY - } - - if m.Slave { - flags = flags | syscall.MS_SLAVE - } - - stat, err := os.Stat(m.Source) - if err != nil { - return err - } - - // FIXME: (crosbymichael) This does not belong here and should be done a layer above - dest, err = symlink.FollowSymlinkInScope(dest, rootfs) - if err != nil { - return err - } - - if err := createIfNotExists(dest, stat.IsDir()); err != nil { - return fmt.Errorf("creating new bind mount target %s", err) - } - - if err := syscall.Mount(m.Source, dest, "bind", uintptr(flags), ""); err != nil { - return fmt.Errorf("mounting %s into %s %s", m.Source, dest, err) - } - - if !m.Writable { - if err := syscall.Mount(m.Source, dest, "bind", uintptr(flags|syscall.MS_REMOUNT), ""); err != nil { - return fmt.Errorf("remounting %s into %s %s", m.Source, dest, err) - } - } - - if m.Relabel != "" { - if err := label.Relabel(m.Source, mountLabel, m.Relabel); err != nil { - return fmt.Errorf("relabeling %s to %s %s", m.Source, mountLabel, err) - } - } - - if m.Private { - if err := syscall.Mount("", dest, "none", uintptr(syscall.MS_PRIVATE), ""); err != nil { - return fmt.Errorf("mounting %s private %s", dest, err) - } - } - - return nil -} - -func (m *Mount) tmpfsMount(rootfs, mountLabel string) error { - var ( - err error - l = label.FormatMountLabel("", mountLabel) - dest = filepath.Join(rootfs, m.Destination) - ) - - // FIXME: (crosbymichael) This does not belong here and should be done a layer above - if dest, err = symlink.FollowSymlinkInScope(dest, rootfs); err != nil { - return err - } - - if err := createIfNotExists(dest, true); err != nil { - return fmt.Errorf("creating new tmpfs mount target %s", err) - } - - if err := syscall.Mount("tmpfs", dest, "tmpfs", uintptr(defaultMountFlags), l); err != nil { - return fmt.Errorf("%s mounting %s in tmpfs", err, dest) - } - - return nil -} diff --git a/vendor/src/github.com/docker/libcontainer/mount/mount_config.go b/vendor/src/github.com/docker/libcontainer/mount/mount_config.go deleted file mode 100644 index eef9b8ce4d..0000000000 --- a/vendor/src/github.com/docker/libcontainer/mount/mount_config.go +++ /dev/null @@ -1,28 +0,0 @@ -package mount - -import ( - "errors" - - "github.com/docker/libcontainer/devices" -) - -var ErrUnsupported = errors.New("Unsupported method") - -type MountConfig struct { - // NoPivotRoot will use MS_MOVE and a chroot to jail the process into the container's rootfs - // This is a common option when the container is running in ramdisk - NoPivotRoot bool `json:"no_pivot_root,omitempty"` - - // ReadonlyFs will remount the container's rootfs as readonly where only externally mounted - // bind mounts are writtable - ReadonlyFs bool `json:"readonly_fs,omitempty"` - - // Mounts specify additional source and destination paths that will be mounted inside the container's - // rootfs and mount namespace if specified - Mounts []*Mount `json:"mounts,omitempty"` - - // The device nodes that should be automatically created within the container upon container start. Note, make sure that the node is marked as allowed in the cgroup as well! - DeviceNodes []*devices.Device `json:"device_nodes,omitempty"` - - MountLabel string `json:"mount_label,omitempty"` -} diff --git a/vendor/src/github.com/docker/libcontainer/mount/msmoveroot.go b/vendor/src/github.com/docker/libcontainer/mount/msmoveroot.go deleted file mode 100644 index 94afd3a99c..0000000000 --- a/vendor/src/github.com/docker/libcontainer/mount/msmoveroot.go +++ /dev/null @@ -1,20 +0,0 @@ -// +build linux - -package mount - -import ( - "fmt" - "syscall" -) - -func MsMoveRoot(rootfs string) error { - if err := syscall.Mount(rootfs, "/", "", syscall.MS_MOVE, ""); err != nil { - return fmt.Errorf("mount move %s into / %s", rootfs, err) - } - - if err := syscall.Chroot("."); err != nil { - return fmt.Errorf("chroot . %s", err) - } - - return syscall.Chdir("/") -} diff --git a/vendor/src/github.com/docker/libcontainer/mount/nodes/nodes.go b/vendor/src/github.com/docker/libcontainer/mount/nodes/nodes.go deleted file mode 100644 index 322c0c0ee2..0000000000 --- a/vendor/src/github.com/docker/libcontainer/mount/nodes/nodes.go +++ /dev/null @@ -1,57 +0,0 @@ -// +build linux - -package nodes - -import ( - "fmt" - "os" - "path/filepath" - "syscall" - - "github.com/docker/libcontainer/devices" -) - -// Create the device nodes in the container. -func CreateDeviceNodes(rootfs string, nodesToCreate []*devices.Device) error { - oldMask := syscall.Umask(0000) - defer syscall.Umask(oldMask) - - for _, node := range nodesToCreate { - if err := CreateDeviceNode(rootfs, node); err != nil { - return err - } - } - return nil -} - -// Creates the device node in the rootfs of the container. -func CreateDeviceNode(rootfs string, node *devices.Device) error { - var ( - dest = filepath.Join(rootfs, node.Path) - parent = filepath.Dir(dest) - ) - - if err := os.MkdirAll(parent, 0755); err != nil { - return err - } - - fileMode := node.FileMode - switch node.Type { - case 'c': - fileMode |= syscall.S_IFCHR - case 'b': - fileMode |= syscall.S_IFBLK - default: - return fmt.Errorf("%c is not a valid device type for device %s", node.Type, node.Path) - } - - if err := syscall.Mknod(dest, uint32(fileMode), devices.Mkdev(node.MajorNumber, node.MinorNumber)); err != nil && !os.IsExist(err) { - return fmt.Errorf("mknod %s %s", node.Path, err) - } - - if err := syscall.Chown(dest, int(node.Uid), int(node.Gid)); err != nil { - return fmt.Errorf("chown %s to %d:%d", node.Path, node.Uid, node.Gid) - } - - return nil -} diff --git a/vendor/src/github.com/docker/libcontainer/mount/nodes/nodes_unsupported.go b/vendor/src/github.com/docker/libcontainer/mount/nodes/nodes_unsupported.go deleted file mode 100644 index 83660715d4..0000000000 --- a/vendor/src/github.com/docker/libcontainer/mount/nodes/nodes_unsupported.go +++ /dev/null @@ -1,13 +0,0 @@ -// +build !linux - -package nodes - -import ( - "errors" - - "github.com/docker/libcontainer/devices" -) - -func CreateDeviceNodes(rootfs string, nodesToCreate []*devices.Device) error { - return errors.New("Unsupported method") -} diff --git a/vendor/src/github.com/docker/libcontainer/mount/pivotroot.go b/vendor/src/github.com/docker/libcontainer/mount/pivotroot.go deleted file mode 100644 index a88ed4a84c..0000000000 --- a/vendor/src/github.com/docker/libcontainer/mount/pivotroot.go +++ /dev/null @@ -1,34 +0,0 @@ -// +build linux - -package mount - -import ( - "fmt" - "io/ioutil" - "os" - "path/filepath" - "syscall" -) - -func PivotRoot(rootfs string) error { - pivotDir, err := ioutil.TempDir(rootfs, ".pivot_root") - if err != nil { - return fmt.Errorf("can't create pivot_root dir %s, error %v", pivotDir, err) - } - - if err := syscall.PivotRoot(rootfs, pivotDir); err != nil { - return fmt.Errorf("pivot_root %s", err) - } - - if err := syscall.Chdir("/"); err != nil { - return fmt.Errorf("chdir / %s", err) - } - - // path to pivot dir now changed, update - pivotDir = filepath.Join("/", filepath.Base(pivotDir)) - if err := syscall.Unmount(pivotDir, syscall.MNT_DETACH); err != nil { - return fmt.Errorf("unmount pivot_root dir %s", err) - } - - return os.Remove(pivotDir) -} diff --git a/vendor/src/github.com/docker/libcontainer/mount/ptmx.go b/vendor/src/github.com/docker/libcontainer/mount/ptmx.go deleted file mode 100644 index c316481adf..0000000000 --- a/vendor/src/github.com/docker/libcontainer/mount/ptmx.go +++ /dev/null @@ -1,30 +0,0 @@ -// +build linux - -package mount - -import ( - "fmt" - "os" - "path/filepath" - - "github.com/docker/libcontainer/console" -) - -func SetupPtmx(rootfs, consolePath, mountLabel string) error { - ptmx := filepath.Join(rootfs, "dev/ptmx") - if err := os.Remove(ptmx); err != nil && !os.IsNotExist(err) { - return err - } - - if err := os.Symlink("pts/ptmx", ptmx); err != nil { - return fmt.Errorf("symlink dev ptmx %s", err) - } - - if consolePath != "" { - if err := console.Setup(rootfs, consolePath, mountLabel); err != nil { - return err - } - } - - return nil -} diff --git a/vendor/src/github.com/docker/libcontainer/mount/readonly.go b/vendor/src/github.com/docker/libcontainer/mount/readonly.go deleted file mode 100644 index 9b4a6f704c..0000000000 --- a/vendor/src/github.com/docker/libcontainer/mount/readonly.go +++ /dev/null @@ -1,11 +0,0 @@ -// +build linux - -package mount - -import ( - "syscall" -) - -func SetReadonly() error { - return syscall.Mount("/", "/", "bind", syscall.MS_BIND|syscall.MS_REMOUNT|syscall.MS_RDONLY|syscall.MS_REC, "") -} diff --git a/vendor/src/github.com/docker/libcontainer/mount/remount.go b/vendor/src/github.com/docker/libcontainer/mount/remount.go deleted file mode 100644 index 99a01209d1..0000000000 --- a/vendor/src/github.com/docker/libcontainer/mount/remount.go +++ /dev/null @@ -1,31 +0,0 @@ -// +build linux - -package mount - -import "syscall" - -func RemountProc() error { - if err := syscall.Unmount("/proc", syscall.MNT_DETACH); err != nil { - return err - } - - if err := syscall.Mount("proc", "/proc", "proc", uintptr(defaultMountFlags), ""); err != nil { - return err - } - - return nil -} - -func RemountSys() error { - if err := syscall.Unmount("/sys", syscall.MNT_DETACH); err != nil { - if err != syscall.EINVAL { - return err - } - } else { - if err := syscall.Mount("sysfs", "/sys", "sysfs", uintptr(defaultMountFlags), ""); err != nil { - return err - } - } - - return nil -} diff --git a/vendor/src/github.com/docker/libcontainer/namespaces/create.go b/vendor/src/github.com/docker/libcontainer/namespaces/create.go deleted file mode 100644 index b6418b6e9f..0000000000 --- a/vendor/src/github.com/docker/libcontainer/namespaces/create.go +++ /dev/null @@ -1,10 +0,0 @@ -package namespaces - -import ( - "os" - "os/exec" - - "github.com/docker/libcontainer" -) - -type CreateCommand func(container *libcontainer.Config, console, dataPath, init string, childPipe *os.File, args []string) *exec.Cmd diff --git a/vendor/src/github.com/docker/libcontainer/namespaces/exec.go b/vendor/src/github.com/docker/libcontainer/namespaces/exec.go deleted file mode 100644 index ff00396979..0000000000 --- a/vendor/src/github.com/docker/libcontainer/namespaces/exec.go +++ /dev/null @@ -1,229 +0,0 @@ -// +build linux - -package namespaces - -import ( - "encoding/json" - "io" - "os" - "os/exec" - "syscall" - - "github.com/docker/libcontainer" - "github.com/docker/libcontainer/cgroups" - "github.com/docker/libcontainer/cgroups/fs" - "github.com/docker/libcontainer/cgroups/systemd" - "github.com/docker/libcontainer/network" - "github.com/docker/libcontainer/system" -) - -const ( - EXIT_SIGNAL_OFFSET = 128 -) - -// TODO(vishh): This is part of the libcontainer API and it does much more than just namespaces related work. -// Move this to libcontainer package. -// Exec performs setup outside of a namespace so that a container can be -// executed. Exec is a high level function for working with container namespaces. -func Exec(container *libcontainer.Config, stdin io.Reader, stdout, stderr io.Writer, console, dataPath string, args []string, createCommand CreateCommand, startCallback func()) (int, error) { - var err error - - // create a pipe so that we can syncronize with the namespaced process and - // pass the state and configuration to the child process - parent, child, err := newInitPipe() - if err != nil { - return -1, err - } - defer parent.Close() - - command := createCommand(container, console, dataPath, os.Args[0], child, args) - // Note: these are only used in non-tty mode - // if there is a tty for the container it will be opened within the namespace and the - // fds will be duped to stdin, stdiout, and stderr - command.Stdin = stdin - command.Stdout = stdout - command.Stderr = stderr - - if err := command.Start(); err != nil { - child.Close() - return -1, err - } - child.Close() - - wait := func() (*os.ProcessState, error) { - ps, err := command.Process.Wait() - // we should kill all processes in cgroup when init is died if we use - // host PID namespace - if !container.Namespaces.Contains(libcontainer.NEWPID) { - killAllPids(container) - } - return ps, err - } - - terminate := func(terr error) (int, error) { - // TODO: log the errors for kill and wait - command.Process.Kill() - wait() - return -1, terr - } - - started, err := system.GetProcessStartTime(command.Process.Pid) - if err != nil { - return terminate(err) - } - - // Do this before syncing with child so that no children - // can escape the cgroup - cgroupPaths, err := SetupCgroups(container, command.Process.Pid) - if err != nil { - return terminate(err) - } - defer cgroups.RemovePaths(cgroupPaths) - - var networkState network.NetworkState - if err := InitializeNetworking(container, command.Process.Pid, &networkState); err != nil { - return terminate(err) - } - // send the state to the container's init process then shutdown writes for the parent - if err := json.NewEncoder(parent).Encode(networkState); err != nil { - return terminate(err) - } - // shutdown writes for the parent side of the pipe - if err := syscall.Shutdown(int(parent.Fd()), syscall.SHUT_WR); err != nil { - return terminate(err) - } - - state := &libcontainer.State{ - InitPid: command.Process.Pid, - InitStartTime: started, - NetworkState: networkState, - CgroupPaths: cgroupPaths, - } - - if err := libcontainer.SaveState(dataPath, state); err != nil { - return terminate(err) - } - defer libcontainer.DeleteState(dataPath) - - // wait for the child process to fully complete and receive an error message - // if one was encoutered - var ierr *initError - if err := json.NewDecoder(parent).Decode(&ierr); err != nil && err != io.EOF { - return terminate(err) - } - if ierr != nil { - return terminate(ierr) - } - - if startCallback != nil { - startCallback() - } - - ps, err := wait() - if err != nil { - if _, ok := err.(*exec.ExitError); !ok { - return -1, err - } - } - // waiting for pipe flushing - command.Wait() - - waitStatus := ps.Sys().(syscall.WaitStatus) - if waitStatus.Signaled() { - return EXIT_SIGNAL_OFFSET + int(waitStatus.Signal()), nil - } - return waitStatus.ExitStatus(), nil -} - -// killAllPids itterates over all of the container's processes -// sending a SIGKILL to each process. -func killAllPids(container *libcontainer.Config) error { - var ( - procs []*os.Process - freeze = fs.Freeze - getPids = fs.GetPids - ) - if systemd.UseSystemd() { - freeze = systemd.Freeze - getPids = systemd.GetPids - } - freeze(container.Cgroups, cgroups.Frozen) - pids, err := getPids(container.Cgroups) - if err != nil { - return err - } - for _, pid := range pids { - // TODO: log err without aborting if we are unable to find - // a single PID - if p, err := os.FindProcess(pid); err == nil { - procs = append(procs, p) - p.Kill() - } - } - freeze(container.Cgroups, cgroups.Thawed) - for _, p := range procs { - p.Wait() - } - return err -} - -// DefaultCreateCommand will return an exec.Cmd with the Cloneflags set to the proper namespaces -// defined on the container's configuration and use the current binary as the init with the -// args provided -// -// console: the /dev/console to setup inside the container -// init: the program executed inside the namespaces -// root: the path to the container json file and information -// pipe: sync pipe to synchronize the parent and child processes -// args: the arguments to pass to the container to run as the user's program -func DefaultCreateCommand(container *libcontainer.Config, console, dataPath, init string, pipe *os.File, args []string) *exec.Cmd { - // get our binary name from arg0 so we can always reexec ourself - env := []string{ - "console=" + console, - "pipe=3", - "data_path=" + dataPath, - } - - command := exec.Command(init, append([]string{"init", "--"}, args...)...) - // make sure the process is executed inside the context of the rootfs - command.Dir = container.RootFs - command.Env = append(os.Environ(), env...) - - if command.SysProcAttr == nil { - command.SysProcAttr = &syscall.SysProcAttr{} - } - command.SysProcAttr.Cloneflags = uintptr(GetNamespaceFlags(container.Namespaces)) - - command.SysProcAttr.Pdeathsig = syscall.SIGKILL - command.ExtraFiles = []*os.File{pipe} - - return command -} - -// SetupCgroups applies the cgroup restrictions to the process running in the container based -// on the container's configuration -func SetupCgroups(container *libcontainer.Config, nspid int) (map[string]string, error) { - if container.Cgroups != nil { - c := container.Cgroups - if systemd.UseSystemd() { - return systemd.Apply(c, nspid) - } - return fs.Apply(c, nspid) - } - return map[string]string{}, nil -} - -// InitializeNetworking creates the container's network stack outside of the namespace and moves -// interfaces into the container's net namespaces if necessary -func InitializeNetworking(container *libcontainer.Config, nspid int, networkState *network.NetworkState) error { - for _, config := range container.Networks { - strategy, err := network.GetStrategy(config.Type) - if err != nil { - return err - } - if err := strategy.Create((*network.Network)(config), nspid, networkState); err != nil { - return err - } - } - return nil -} diff --git a/vendor/src/github.com/docker/libcontainer/namespaces/execin.go b/vendor/src/github.com/docker/libcontainer/namespaces/execin.go deleted file mode 100644 index ddff5c3a9f..0000000000 --- a/vendor/src/github.com/docker/libcontainer/namespaces/execin.go +++ /dev/null @@ -1,132 +0,0 @@ -// +build linux - -package namespaces - -import ( - "encoding/json" - "fmt" - "io" - "os" - "os/exec" - "path/filepath" - "strconv" - "syscall" - - "github.com/docker/libcontainer" - "github.com/docker/libcontainer/apparmor" - "github.com/docker/libcontainer/cgroups" - "github.com/docker/libcontainer/label" - "github.com/docker/libcontainer/system" -) - -// ExecIn reexec's the initPath with the argv 0 rewrite to "nsenter" so that it is able to run the -// setns code in a single threaded environment joining the existing containers' namespaces. -func ExecIn(container *libcontainer.Config, state *libcontainer.State, userArgs []string, initPath, action string, - stdin io.Reader, stdout, stderr io.Writer, console string, startCallback func(*exec.Cmd)) (int, error) { - - args := []string{fmt.Sprintf("nsenter-%s", action), "--nspid", strconv.Itoa(state.InitPid)} - - if console != "" { - args = append(args, "--console", console) - } - - cmd := &exec.Cmd{ - Path: initPath, - Args: append(args, append([]string{"--"}, userArgs...)...), - } - - if filepath.Base(initPath) == initPath { - if lp, err := exec.LookPath(initPath); err == nil { - cmd.Path = lp - } - } - - parent, child, err := newInitPipe() - if err != nil { - return -1, err - } - defer parent.Close() - - // Note: these are only used in non-tty mode - // if there is a tty for the container it will be opened within the namespace and the - // fds will be duped to stdin, stdiout, and stderr - cmd.Stdin = stdin - cmd.Stdout = stdout - cmd.Stderr = stderr - cmd.ExtraFiles = []*os.File{child} - - if err := cmd.Start(); err != nil { - child.Close() - return -1, err - } - child.Close() - - terminate := func(terr error) (int, error) { - // TODO: log the errors for kill and wait - cmd.Process.Kill() - cmd.Wait() - return -1, terr - } - - // Enter cgroups. - if err := EnterCgroups(state, cmd.Process.Pid); err != nil { - return terminate(err) - } - - // finish cgroups' setup, unblock the child process. - if _, err := parent.WriteString("1"); err != nil { - return terminate(err) - } - - if err := json.NewEncoder(parent).Encode(container); err != nil { - return terminate(err) - } - - if startCallback != nil { - startCallback(cmd) - } - - if err := cmd.Wait(); err != nil { - if _, ok := err.(*exec.ExitError); !ok { - return -1, err - } - } - return cmd.ProcessState.Sys().(syscall.WaitStatus).ExitStatus(), nil -} - -// Finalize expects that the setns calls have been setup and that is has joined an -// existing namespace -func FinalizeSetns(container *libcontainer.Config, args []string) error { - // clear the current processes env and replace it with the environment defined on the container - if err := LoadContainerEnvironment(container); err != nil { - return err - } - - if err := setupRlimits(container); err != nil { - return fmt.Errorf("setup rlimits %s", err) - } - - if err := FinalizeNamespace(container); err != nil { - return err - } - - if err := apparmor.ApplyProfile(container.AppArmorProfile); err != nil { - return fmt.Errorf("set apparmor profile %s: %s", container.AppArmorProfile, err) - } - - if container.ProcessLabel != "" { - if err := label.SetProcessLabel(container.ProcessLabel); err != nil { - return err - } - } - - if err := system.Execv(args[0], args[0:], os.Environ()); err != nil { - return err - } - - panic("unreachable") -} - -func EnterCgroups(state *libcontainer.State, pid int) error { - return cgroups.EnterPid(state.CgroupPaths, pid) -} diff --git a/vendor/src/github.com/docker/libcontainer/namespaces/init.go b/vendor/src/github.com/docker/libcontainer/namespaces/init.go deleted file mode 100644 index 0a4ff19620..0000000000 --- a/vendor/src/github.com/docker/libcontainer/namespaces/init.go +++ /dev/null @@ -1,331 +0,0 @@ -// +build linux - -package namespaces - -import ( - "encoding/json" - "fmt" - "io/ioutil" - "os" - "strings" - "syscall" - - "github.com/docker/libcontainer" - "github.com/docker/libcontainer/apparmor" - "github.com/docker/libcontainer/console" - "github.com/docker/libcontainer/label" - "github.com/docker/libcontainer/mount" - "github.com/docker/libcontainer/netlink" - "github.com/docker/libcontainer/network" - "github.com/docker/libcontainer/security/capabilities" - "github.com/docker/libcontainer/security/restrict" - "github.com/docker/libcontainer/system" - "github.com/docker/libcontainer/user" - "github.com/docker/libcontainer/utils" -) - -// TODO(vishh): This is part of the libcontainer API and it does much more than just namespaces related work. -// Move this to libcontainer package. -// Init is the init process that first runs inside a new namespace to setup mounts, users, networking, -// and other options required for the new container. -// The caller of Init function has to ensure that the go runtime is locked to an OS thread -// (using runtime.LockOSThread) else system calls like setns called within Init may not work as intended. -func Init(container *libcontainer.Config, uncleanRootfs, consolePath string, pipe *os.File, args []string) (err error) { - defer func() { - // if we have an error during the initialization of the container's init then send it back to the - // parent process in the form of an initError. - if err != nil { - // ensure that any data sent from the parent is consumed so it doesn't - // receive ECONNRESET when the child writes to the pipe. - ioutil.ReadAll(pipe) - if err := json.NewEncoder(pipe).Encode(initError{ - Message: err.Error(), - }); err != nil { - panic(err) - } - } - // ensure that this pipe is always closed - pipe.Close() - }() - - rootfs, err := utils.ResolveRootfs(uncleanRootfs) - if err != nil { - return err - } - - // clear the current processes env and replace it with the environment - // defined on the container - if err := LoadContainerEnvironment(container); err != nil { - return err - } - - // We always read this as it is a way to sync with the parent as well - var networkState *network.NetworkState - if err := json.NewDecoder(pipe).Decode(&networkState); err != nil { - return err - } - // join any namespaces via a path to the namespace fd if provided - if err := joinExistingNamespaces(container.Namespaces); err != nil { - return err - } - if consolePath != "" { - if err := console.OpenAndDup(consolePath); err != nil { - return err - } - } - if _, err := syscall.Setsid(); err != nil { - return fmt.Errorf("setsid %s", err) - } - if consolePath != "" { - if err := system.Setctty(); err != nil { - return fmt.Errorf("setctty %s", err) - } - } - - if err := setupNetwork(container, networkState); err != nil { - return fmt.Errorf("setup networking %s", err) - } - if err := setupRoute(container); err != nil { - return fmt.Errorf("setup route %s", err) - } - - if err := setupRlimits(container); err != nil { - return fmt.Errorf("setup rlimits %s", err) - } - - label.Init() - - if err := mount.InitializeMountNamespace(rootfs, - consolePath, - container.RestrictSys, - (*mount.MountConfig)(container.MountConfig)); err != nil { - return fmt.Errorf("setup mount namespace %s", err) - } - - if container.Hostname != "" { - if err := syscall.Sethostname([]byte(container.Hostname)); err != nil { - return fmt.Errorf("unable to sethostname %q: %s", container.Hostname, err) - } - } - - if err := apparmor.ApplyProfile(container.AppArmorProfile); err != nil { - return fmt.Errorf("set apparmor profile %s: %s", container.AppArmorProfile, err) - } - - if err := label.SetProcessLabel(container.ProcessLabel); err != nil { - return fmt.Errorf("set process label %s", err) - } - - // TODO: (crosbymichael) make this configurable at the Config level - if container.RestrictSys { - if err := restrict.Restrict("proc/sys", "proc/sysrq-trigger", "proc/irq", "proc/bus"); err != nil { - return err - } - } - - pdeathSignal, err := system.GetParentDeathSignal() - if err != nil { - return fmt.Errorf("get parent death signal %s", err) - } - - if err := FinalizeNamespace(container); err != nil { - return fmt.Errorf("finalize namespace %s", err) - } - - // FinalizeNamespace can change user/group which clears the parent death - // signal, so we restore it here. - if err := RestoreParentDeathSignal(pdeathSignal); err != nil { - return fmt.Errorf("restore parent death signal %s", err) - } - - return system.Execv(args[0], args[0:], os.Environ()) -} - -// RestoreParentDeathSignal sets the parent death signal to old. -func RestoreParentDeathSignal(old int) error { - if old == 0 { - return nil - } - - current, err := system.GetParentDeathSignal() - if err != nil { - return fmt.Errorf("get parent death signal %s", err) - } - - if old == current { - return nil - } - - if err := system.ParentDeathSignal(uintptr(old)); err != nil { - return fmt.Errorf("set parent death signal %s", err) - } - - // Signal self if parent is already dead. Does nothing if running in a new - // PID namespace, as Getppid will always return 0. - if syscall.Getppid() == 1 { - return syscall.Kill(syscall.Getpid(), syscall.SIGKILL) - } - - return nil -} - -// SetupUser changes the groups, gid, and uid for the user inside the container -func SetupUser(container *libcontainer.Config) error { - // Set up defaults. - defaultExecUser := user.ExecUser{ - Uid: syscall.Getuid(), - Gid: syscall.Getgid(), - Home: "/", - } - - passwdPath, err := user.GetPasswdPath() - if err != nil { - return err - } - - groupPath, err := user.GetGroupPath() - if err != nil { - return err - } - - execUser, err := user.GetExecUserPath(container.User, &defaultExecUser, passwdPath, groupPath) - if err != nil { - return fmt.Errorf("get supplementary groups %s", err) - } - - suppGroups := append(execUser.Sgids, container.AdditionalGroups...) - - if err := syscall.Setgroups(suppGroups); err != nil { - return fmt.Errorf("setgroups %s", err) - } - - if err := system.Setgid(execUser.Gid); err != nil { - return fmt.Errorf("setgid %s", err) - } - - if err := system.Setuid(execUser.Uid); err != nil { - return fmt.Errorf("setuid %s", err) - } - - // if we didn't get HOME already, set it based on the user's HOME - if envHome := os.Getenv("HOME"); envHome == "" { - if err := os.Setenv("HOME", execUser.Home); err != nil { - return fmt.Errorf("set HOME %s", err) - } - } - - return nil -} - -// setupVethNetwork uses the Network config if it is not nil to initialize -// the new veth interface inside the container for use by changing the name to eth0 -// setting the MTU and IP address along with the default gateway -func setupNetwork(container *libcontainer.Config, networkState *network.NetworkState) error { - for _, config := range container.Networks { - strategy, err := network.GetStrategy(config.Type) - if err != nil { - return err - } - - err1 := strategy.Initialize((*network.Network)(config), networkState) - if err1 != nil { - return err1 - } - } - return nil -} - -func setupRoute(container *libcontainer.Config) error { - for _, config := range container.Routes { - if err := netlink.AddRoute(config.Destination, config.Source, config.Gateway, config.InterfaceName); err != nil { - return err - } - } - return nil -} - -func setupRlimits(container *libcontainer.Config) error { - for _, rlimit := range container.Rlimits { - l := &syscall.Rlimit{Max: rlimit.Hard, Cur: rlimit.Soft} - if err := syscall.Setrlimit(rlimit.Type, l); err != nil { - return fmt.Errorf("error setting rlimit type %v: %v", rlimit.Type, err) - } - } - return nil -} - -// FinalizeNamespace drops the caps, sets the correct user -// and working dir, and closes any leaky file descriptors -// before execing the command inside the namespace -func FinalizeNamespace(container *libcontainer.Config) error { - // Ensure that all non-standard fds we may have accidentally - // inherited are marked close-on-exec so they stay out of the - // container - if err := utils.CloseExecFrom(3); err != nil { - return fmt.Errorf("close open file descriptors %s", err) - } - - // drop capabilities in bounding set before changing user - if err := capabilities.DropBoundingSet(container.Capabilities); err != nil { - return fmt.Errorf("drop bounding set %s", err) - } - - // preserve existing capabilities while we change users - if err := system.SetKeepCaps(); err != nil { - return fmt.Errorf("set keep caps %s", err) - } - - if err := SetupUser(container); err != nil { - return fmt.Errorf("setup user %s", err) - } - - if err := system.ClearKeepCaps(); err != nil { - return fmt.Errorf("clear keep caps %s", err) - } - - // drop all other capabilities - if err := capabilities.DropCapabilities(container.Capabilities); err != nil { - return fmt.Errorf("drop capabilities %s", err) - } - - if container.WorkingDir != "" { - if err := syscall.Chdir(container.WorkingDir); err != nil { - return fmt.Errorf("chdir to %s %s", container.WorkingDir, err) - } - } - - return nil -} - -func LoadContainerEnvironment(container *libcontainer.Config) error { - os.Clearenv() - for _, pair := range container.Env { - p := strings.SplitN(pair, "=", 2) - if len(p) < 2 { - return fmt.Errorf("invalid environment '%v'", pair) - } - if err := os.Setenv(p[0], p[1]); err != nil { - return err - } - } - return nil -} - -// joinExistingNamespaces gets all the namespace paths specified for the container and -// does a setns on the namespace fd so that the current process joins the namespace. -func joinExistingNamespaces(namespaces []libcontainer.Namespace) error { - for _, ns := range namespaces { - if ns.Path != "" { - f, err := os.OpenFile(ns.Path, os.O_RDONLY, 0) - if err != nil { - return err - } - err = system.Setns(f.Fd(), uintptr(namespaceInfo[ns.Type])) - f.Close() - if err != nil { - return err - } - } - } - return nil -} diff --git a/vendor/src/github.com/docker/libcontainer/namespaces/nsenter/nsenter.c b/vendor/src/github.com/docker/libcontainer/namespaces/nsenter/nsenter.c deleted file mode 100644 index 4ab21774fb..0000000000 --- a/vendor/src/github.com/docker/libcontainer/namespaces/nsenter/nsenter.c +++ /dev/null @@ -1,245 +0,0 @@ -// +build cgo -// -// formated with indent -linux nsenter.c - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define pr_perror(fmt, ...) fprintf(stderr, "nsenter: " fmt ": %m\n", ##__VA_ARGS__) - -static const kBufSize = 256; -static const char *kNsEnter = "nsenter"; - -void get_args(int *argc, char ***argv) -{ - // Read argv - int fd = open("/proc/self/cmdline", O_RDONLY); - if (fd < 0) { - pr_perror("Unable to open /proc/self/cmdline"); - exit(1); - } - // Read the whole commandline. - ssize_t contents_size = 0; - ssize_t contents_offset = 0; - char *contents = NULL; - ssize_t bytes_read = 0; - do { - contents_size += kBufSize; - contents = (char *)realloc(contents, contents_size); - bytes_read = - read(fd, contents + contents_offset, - contents_size - contents_offset); - if (bytes_read < 0) { - pr_perror("Unable to read from /proc/self/cmdline"); - exit(1); - } - contents_offset += bytes_read; - } - while (bytes_read > 0); - close(fd); - - // Parse the commandline into an argv. /proc/self/cmdline has \0 delimited args. - ssize_t i; - *argc = 0; - for (i = 0; i < contents_offset; i++) { - if (contents[i] == '\0') { - (*argc)++; - } - } - *argv = (char **)malloc(sizeof(char *) * ((*argc) + 1)); - int idx; - for (idx = 0; idx < (*argc); idx++) { - (*argv)[idx] = contents; - contents += strlen(contents) + 1; - } - (*argv)[*argc] = NULL; -} - -// Use raw setns syscall for versions of glibc that don't include it (namely glibc-2.12) -#if __GLIBC__ == 2 && __GLIBC_MINOR__ < 14 -#define _GNU_SOURCE -#include -#include "syscall.h" -#ifdef SYS_setns -int setns(int fd, int nstype) -{ - return syscall(SYS_setns, fd, nstype); -} -#endif -#endif - -void print_usage() -{ - fprintf(stderr, - "nsenter --nspid --console -- cmd1 arg1 arg2...\n"); -} - -void nsenter() -{ - int argc, c; - char **argv; - get_args(&argc, &argv); - - // check argv 0 to ensure that we are supposed to setns - // we use strncmp to test for a value of "nsenter" but also allows alternate implmentations - // after the setns code path to continue to use the argv 0 to determine actions to be run - // resulting in the ability to specify "nsenter-mknod", "nsenter-exec", etc... - if (strncmp(argv[0], kNsEnter, strlen(kNsEnter)) != 0) { - return; - } -#ifdef PR_SET_CHILD_SUBREAPER - if (prctl(PR_SET_CHILD_SUBREAPER, 1, 0, 0, 0) == -1) { - pr_perror("Failed to set child subreaper"); - exit(1); - } -#endif - - static const struct option longopts[] = { - {"nspid", required_argument, NULL, 'n'}, - {"console", required_argument, NULL, 't'}, - {NULL, 0, NULL, 0} - }; - - pid_t init_pid = -1; - char *init_pid_str = NULL; - char *console = NULL; - while ((c = getopt_long_only(argc, argv, "n:c:", longopts, NULL)) != -1) { - switch (c) { - case 'n': - init_pid_str = optarg; - break; - case 't': - console = optarg; - break; - } - } - - if (init_pid_str == NULL) { - print_usage(); - exit(1); - } - - init_pid = strtol(init_pid_str, NULL, 10); - if ((init_pid == 0 && errno == EINVAL) || errno == ERANGE) { - pr_perror("Failed to parse PID from \"%s\" with output \"%d\"", - init_pid_str, init_pid); - print_usage(); - exit(1); - } - - argc -= 3; - argv += 3; - - if (setsid() == -1) { - pr_perror("setsid failed"); - exit(1); - } - // before we setns we need to dup the console - int consolefd = -1; - if (console != NULL) { - consolefd = open(console, O_RDWR); - if (consolefd < 0) { - pr_perror("Failed to open console %s", console); - exit(1); - } - } - // blocking until the parent placed the process inside correct cgroups. - unsigned char s; - if (read(3, &s, 1) != 1 || s != '1') { - pr_perror("failed to receive synchronization data from parent"); - exit(1); - } - // Setns on all supported namespaces. - char ns_dir[PATH_MAX]; - memset(ns_dir, 0, PATH_MAX); - snprintf(ns_dir, PATH_MAX - 1, "/proc/%d/ns/", init_pid); - - int ns_dir_fd; - ns_dir_fd = open(ns_dir, O_RDONLY | O_DIRECTORY); - if (ns_dir_fd < 0) { - pr_perror("Unable to open %s", ns_dir); - exit(1); - } - - char *namespaces[] = { "ipc", "uts", "net", "pid", "mnt" }; - const int num = sizeof(namespaces) / sizeof(char *); - int i; - for (i = 0; i < num; i++) { - // A zombie process has links on namespaces, but they can't be opened - struct stat st; - if (fstatat(ns_dir_fd, namespaces[i], &st, AT_SYMLINK_NOFOLLOW) - == -1) { - if (errno == ENOENT) - continue; - pr_perror("Failed to stat ns file %s for ns %s", - ns_dir, namespaces[i]); - exit(1); - } - - int fd = openat(ns_dir_fd, namespaces[i], O_RDONLY); - if (fd == -1) { - pr_perror("Failed to open ns file %s for ns %s", - ns_dir, namespaces[i]); - exit(1); - } - // Set the namespace. - if (setns(fd, 0) == -1) { - pr_perror("Failed to setns for %s", namespaces[i]); - exit(1); - } - close(fd); - } - close(ns_dir_fd); - - // We must fork to actually enter the PID namespace. - int child = fork(); - if (child == -1) { - pr_perror("Unable to fork a process"); - exit(1); - } - if (child == 0) { - if (consolefd != -1) { - if (dup2(consolefd, STDIN_FILENO) != 0) { - pr_perror("Failed to dup 0"); - exit(1); - } - if (dup2(consolefd, STDOUT_FILENO) != STDOUT_FILENO) { - pr_perror("Failed to dup 1"); - exit(1); - } - if (dup2(consolefd, STDERR_FILENO) != STDERR_FILENO) { - pr_perror("Failed to dup 2\n"); - exit(1); - } - } - // Finish executing, let the Go runtime take over. - return; - } else { - // Parent, wait for the child. - int status = 0; - if (waitpid(child, &status, 0) == -1) { - pr_perror("nsenter: Failed to waitpid with error"); - exit(1); - } - // Forward the child's exit code or re-send its death signal. - if (WIFEXITED(status)) { - exit(WEXITSTATUS(status)); - } else if (WIFSIGNALED(status)) { - kill(getpid(), WTERMSIG(status)); - } - - exit(1); - } - - return; -} diff --git a/vendor/src/github.com/docker/libcontainer/namespaces/nsenter/nsenter.go b/vendor/src/github.com/docker/libcontainer/namespaces/nsenter/nsenter.go deleted file mode 100644 index 7d21e8e59f..0000000000 --- a/vendor/src/github.com/docker/libcontainer/namespaces/nsenter/nsenter.go +++ /dev/null @@ -1,10 +0,0 @@ -// +build linux - -package nsenter - -/* -__attribute__((constructor)) init() { - nsenter(); -} -*/ -import "C" diff --git a/vendor/src/github.com/docker/libcontainer/namespaces/utils.go b/vendor/src/github.com/docker/libcontainer/namespaces/utils.go deleted file mode 100644 index de71a379f8..0000000000 --- a/vendor/src/github.com/docker/libcontainer/namespaces/utils.go +++ /dev/null @@ -1,45 +0,0 @@ -// +build linux - -package namespaces - -import ( - "os" - "syscall" - - "github.com/docker/libcontainer" -) - -type initError struct { - Message string `json:"message,omitempty"` -} - -func (i initError) Error() string { - return i.Message -} - -var namespaceInfo = map[libcontainer.NamespaceType]int{ - libcontainer.NEWNET: syscall.CLONE_NEWNET, - libcontainer.NEWNS: syscall.CLONE_NEWNS, - libcontainer.NEWUSER: syscall.CLONE_NEWUSER, - libcontainer.NEWIPC: syscall.CLONE_NEWIPC, - libcontainer.NEWUTS: syscall.CLONE_NEWUTS, - libcontainer.NEWPID: syscall.CLONE_NEWPID, -} - -// New returns a newly initialized Pipe for communication between processes -func newInitPipe() (parent *os.File, child *os.File, err error) { - fds, err := syscall.Socketpair(syscall.AF_LOCAL, syscall.SOCK_STREAM|syscall.SOCK_CLOEXEC, 0) - if err != nil { - return nil, nil, err - } - return os.NewFile(uintptr(fds[1]), "parent"), os.NewFile(uintptr(fds[0]), "child"), nil -} - -// GetNamespaceFlags parses the container's Namespaces options to set the correct -// flags on clone, unshare, and setns -func GetNamespaceFlags(namespaces libcontainer.Namespaces) (flag int) { - for _, v := range namespaces { - flag |= namespaceInfo[v.Type] - } - return flag -} diff --git a/vendor/src/github.com/docker/libcontainer/netlink/netlink_linux.go b/vendor/src/github.com/docker/libcontainer/netlink/netlink_linux.go index 3cc3cc94f7..3ecb81fb78 100644 --- a/vendor/src/github.com/docker/libcontainer/netlink/netlink_linux.go +++ b/vendor/src/github.com/docker/libcontainer/netlink/netlink_linux.go @@ -7,7 +7,6 @@ import ( "math/rand" "net" "os" - "path/filepath" "sync/atomic" "syscall" "unsafe" @@ -23,6 +22,7 @@ const ( IFLA_VLAN_ID = 1 IFLA_NET_NS_FD = 28 IFLA_ADDRESS = 1 + IFLA_BRPORT_MODE = 4 SIOC_BRADDBR = 0x89a0 SIOC_BRDELBR = 0x89a1 SIOC_BRADDIF = 0x89a2 @@ -1253,25 +1253,33 @@ func SetMacAddress(name, addr string) error { } func SetHairpinMode(iface *net.Interface, enabled bool) error { - sysPath := filepath.Join("/sys/class/net", iface.Name, "brport/hairpin_mode") - - sysFile, err := os.OpenFile(sysPath, os.O_WRONLY, 0) + s, err := getNetlinkSocket() if err != nil { return err } - defer sysFile.Close() + defer s.Close() + req := newNetlinkRequest(syscall.RTM_SETLINK, syscall.NLM_F_ACK) - var writeVal []byte + msg := newIfInfomsg(syscall.AF_BRIDGE) + msg.Type = syscall.RTM_SETLINK + msg.Flags = syscall.NLM_F_REQUEST + msg.Index = int32(iface.Index) + msg.Change = DEFAULT_CHANGE + req.AddData(msg) + + mode := []byte{0} if enabled { - writeVal = []byte("1") - } else { - writeVal = []byte("0") + mode[0] = byte(1) } - if _, err := sysFile.Write(writeVal); err != nil { + + br := newRtAttr(syscall.IFLA_PROTINFO|syscall.NLA_F_NESTED, nil) + newRtAttrChild(br, IFLA_BRPORT_MODE, mode) + req.AddData(br) + if err := s.Send(req); err != nil { return err } - return nil + return s.HandleAck(req.Seq) } func ChangeName(iface *net.Interface, newName string) error { diff --git a/vendor/src/github.com/docker/libcontainer/network/loopback.go b/vendor/src/github.com/docker/libcontainer/network/loopback.go deleted file mode 100644 index 1667b4d82a..0000000000 --- a/vendor/src/github.com/docker/libcontainer/network/loopback.go +++ /dev/null @@ -1,23 +0,0 @@ -// +build linux - -package network - -import ( - "fmt" -) - -// Loopback is a network strategy that provides a basic loopback device -type Loopback struct { -} - -func (l *Loopback) Create(n *Network, nspid int, networkState *NetworkState) error { - return nil -} - -func (l *Loopback) Initialize(config *Network, networkState *NetworkState) error { - // Do not set the MTU on the loopback interface - use the default. - if err := InterfaceUp("lo"); err != nil { - return fmt.Errorf("lo up %s", err) - } - return nil -} diff --git a/vendor/src/github.com/docker/libcontainer/network/network.go b/vendor/src/github.com/docker/libcontainer/network/network.go deleted file mode 100644 index 40b25b135b..0000000000 --- a/vendor/src/github.com/docker/libcontainer/network/network.go +++ /dev/null @@ -1,117 +0,0 @@ -// +build linux - -package network - -import ( - "net" - - "github.com/docker/libcontainer/netlink" -) - -func InterfaceUp(name string) error { - iface, err := net.InterfaceByName(name) - if err != nil { - return err - } - return netlink.NetworkLinkUp(iface) -} - -func InterfaceDown(name string) error { - iface, err := net.InterfaceByName(name) - if err != nil { - return err - } - return netlink.NetworkLinkDown(iface) -} - -func ChangeInterfaceName(old, newName string) error { - iface, err := net.InterfaceByName(old) - if err != nil { - return err - } - return netlink.NetworkChangeName(iface, newName) -} - -func CreateVethPair(name1, name2 string, txQueueLen int) error { - return netlink.NetworkCreateVethPair(name1, name2, txQueueLen) -} - -func SetInterfaceInNamespacePid(name string, nsPid int) error { - iface, err := net.InterfaceByName(name) - if err != nil { - return err - } - return netlink.NetworkSetNsPid(iface, nsPid) -} - -func SetInterfaceInNamespaceFd(name string, fd uintptr) error { - iface, err := net.InterfaceByName(name) - if err != nil { - return err - } - return netlink.NetworkSetNsFd(iface, int(fd)) -} - -func SetInterfaceMaster(name, master string) error { - iface, err := net.InterfaceByName(name) - if err != nil { - return err - } - masterIface, err := net.InterfaceByName(master) - if err != nil { - return err - } - return netlink.AddToBridge(iface, masterIface) -} - -func SetDefaultGateway(ip, ifaceName string) error { - return netlink.AddDefaultGw(ip, ifaceName) -} - -func SetInterfaceMac(name string, macaddr string) error { - iface, err := net.InterfaceByName(name) - if err != nil { - return err - } - return netlink.NetworkSetMacAddress(iface, macaddr) -} - -func SetInterfaceIp(name string, rawIp string) error { - iface, err := net.InterfaceByName(name) - if err != nil { - return err - } - ip, ipNet, err := net.ParseCIDR(rawIp) - if err != nil { - return err - } - return netlink.NetworkLinkAddIp(iface, ip, ipNet) -} - -func DeleteInterfaceIp(name string, rawIp string) error { - iface, err := net.InterfaceByName(name) - if err != nil { - return err - } - ip, ipNet, err := net.ParseCIDR(rawIp) - if err != nil { - return err - } - return netlink.NetworkLinkDelIp(iface, ip, ipNet) -} - -func SetMtu(name string, mtu int) error { - iface, err := net.InterfaceByName(name) - if err != nil { - return err - } - return netlink.NetworkSetMTU(iface, mtu) -} - -func SetHairpinMode(name string, enabled bool) error { - iface, err := net.InterfaceByName(name) - if err != nil { - return err - } - return netlink.SetHairpinMode(iface, enabled) -} diff --git a/vendor/src/github.com/docker/libcontainer/network/stats.go b/vendor/src/github.com/docker/libcontainer/network/stats.go deleted file mode 100644 index e2156c74da..0000000000 --- a/vendor/src/github.com/docker/libcontainer/network/stats.go +++ /dev/null @@ -1,74 +0,0 @@ -package network - -import ( - "io/ioutil" - "path/filepath" - "strconv" - "strings" -) - -type NetworkStats struct { - RxBytes uint64 `json:"rx_bytes"` - RxPackets uint64 `json:"rx_packets"` - RxErrors uint64 `json:"rx_errors"` - RxDropped uint64 `json:"rx_dropped"` - TxBytes uint64 `json:"tx_bytes"` - TxPackets uint64 `json:"tx_packets"` - TxErrors uint64 `json:"tx_errors"` - TxDropped uint64 `json:"tx_dropped"` -} - -// Returns the network statistics for the network interfaces represented by the NetworkRuntimeInfo. -func GetStats(networkState *NetworkState) (*NetworkStats, error) { - // This can happen if the network runtime information is missing - possible if the container was created by an old version of libcontainer. - if networkState.VethHost == "" { - return &NetworkStats{}, nil - } - - out := &NetworkStats{} - - type netStatsPair struct { - // Where to write the output. - Out *uint64 - - // The network stats file to read. - File string - } - - // Ingress for host veth is from the container. Hence tx_bytes stat on the host veth is actually number of bytes received by the container. - netStats := []netStatsPair{ - {Out: &out.RxBytes, File: "tx_bytes"}, - {Out: &out.RxPackets, File: "tx_packets"}, - {Out: &out.RxErrors, File: "tx_errors"}, - {Out: &out.RxDropped, File: "tx_dropped"}, - - {Out: &out.TxBytes, File: "rx_bytes"}, - {Out: &out.TxPackets, File: "rx_packets"}, - {Out: &out.TxErrors, File: "rx_errors"}, - {Out: &out.TxDropped, File: "rx_dropped"}, - } - for _, netStat := range netStats { - data, err := readSysfsNetworkStats(networkState.VethHost, netStat.File) - if err != nil { - return nil, err - } - *(netStat.Out) = data - } - - return out, nil -} - -// Reads the specified statistics available under /sys/class/net//statistics -func readSysfsNetworkStats(ethInterface, statsFile string) (uint64, error) { - fullPath := filepath.Join("/sys/class/net", ethInterface, "statistics", statsFile) - data, err := ioutil.ReadFile(fullPath) - if err != nil { - return 0, err - } - value, err := strconv.ParseUint(strings.TrimSpace(string(data)), 10, 64) - if err != nil { - return 0, err - } - - return value, err -} diff --git a/vendor/src/github.com/docker/libcontainer/network/strategy.go b/vendor/src/github.com/docker/libcontainer/network/strategy.go deleted file mode 100644 index 019fe62f41..0000000000 --- a/vendor/src/github.com/docker/libcontainer/network/strategy.go +++ /dev/null @@ -1,34 +0,0 @@ -// +build linux - -package network - -import ( - "errors" -) - -var ( - ErrNotValidStrategyType = errors.New("not a valid network strategy type") -) - -var strategies = map[string]NetworkStrategy{ - "veth": &Veth{}, - "loopback": &Loopback{}, -} - -// NetworkStrategy represents a specific network configuration for -// a container's networking stack -type NetworkStrategy interface { - Create(*Network, int, *NetworkState) error - Initialize(*Network, *NetworkState) error -} - -// GetStrategy returns the specific network strategy for the -// provided type. If no strategy is registered for the type an -// ErrNotValidStrategyType is returned. -func GetStrategy(tpe string) (NetworkStrategy, error) { - s, exists := strategies[tpe] - if !exists { - return nil, ErrNotValidStrategyType - } - return s, nil -} diff --git a/vendor/src/github.com/docker/libcontainer/network/types.go b/vendor/src/github.com/docker/libcontainer/network/types.go deleted file mode 100644 index dcf00420f3..0000000000 --- a/vendor/src/github.com/docker/libcontainer/network/types.go +++ /dev/null @@ -1,50 +0,0 @@ -package network - -// Network defines configuration for a container's networking stack -// -// The network configuration can be omited from a container causing the -// container to be setup with the host's networking stack -type Network struct { - // Type sets the networks type, commonly veth and loopback - Type string `json:"type,omitempty"` - - // The bridge to use. - Bridge string `json:"bridge,omitempty"` - - // Prefix for the veth interfaces. - VethPrefix string `json:"veth_prefix,omitempty"` - - // MacAddress contains the MAC address to set on the network interface - MacAddress string `json:"mac_address,omitempty"` - - // Address contains the IPv4 and mask to set on the network interface - Address string `json:"address,omitempty"` - - // IPv6Address contains the IPv6 and mask to set on the network interface - IPv6Address string `json:"ipv6_address,omitempty"` - - // Gateway sets the gateway address that is used as the default for the interface - Gateway string `json:"gateway,omitempty"` - - // IPv6Gateway sets the ipv6 gateway address that is used as the default for the interface - IPv6Gateway string `json:"ipv6_gateway,omitempty"` - - // Mtu sets the mtu value for the interface and will be mirrored on both the host and - // container's interfaces if a pair is created, specifically in the case of type veth - // Note: This does not apply to loopback interfaces. - Mtu int `json:"mtu,omitempty"` - - // TxQueueLen sets the tx_queuelen value for the interface and will be mirrored on both the host and - // container's interfaces if a pair is created, specifically in the case of type veth - // Note: This does not apply to loopback interfaces. - TxQueueLen int `json:"txqueuelen,omitempty"` -} - -// Struct describing the network specific runtime state that will be maintained by libcontainer for all running containers -// Do not depend on it outside of libcontainer. -type NetworkState struct { - // The name of the veth interface on the Host. - VethHost string `json:"veth_host,omitempty"` - // The name of the veth interface created inside the container for the child. - VethChild string `json:"veth_child,omitempty"` -} diff --git a/vendor/src/github.com/docker/libcontainer/network/veth.go b/vendor/src/github.com/docker/libcontainer/network/veth.go deleted file mode 100644 index 3d7dc8729e..0000000000 --- a/vendor/src/github.com/docker/libcontainer/network/veth.go +++ /dev/null @@ -1,122 +0,0 @@ -// +build linux - -package network - -import ( - "fmt" - - "github.com/docker/libcontainer/netlink" - "github.com/docker/libcontainer/utils" -) - -// Veth is a network strategy that uses a bridge and creates -// a veth pair, one that stays outside on the host and the other -// is placed inside the container's namespace -type Veth struct { -} - -const defaultDevice = "eth0" - -func (v *Veth) Create(n *Network, nspid int, networkState *NetworkState) error { - var ( - bridge = n.Bridge - prefix = n.VethPrefix - txQueueLen = n.TxQueueLen - ) - if bridge == "" { - return fmt.Errorf("bridge is not specified") - } - if prefix == "" { - return fmt.Errorf("veth prefix is not specified") - } - name1, name2, err := createVethPair(prefix, txQueueLen) - if err != nil { - return err - } - if err := SetInterfaceMaster(name1, bridge); err != nil { - return err - } - if err := SetMtu(name1, n.Mtu); err != nil { - return err - } - if err := InterfaceUp(name1); err != nil { - return err - } - if err := SetInterfaceInNamespacePid(name2, nspid); err != nil { - return err - } - networkState.VethHost = name1 - networkState.VethChild = name2 - - return nil -} - -func (v *Veth) Initialize(config *Network, networkState *NetworkState) error { - var vethChild = networkState.VethChild - if vethChild == "" { - return fmt.Errorf("vethChild is not specified") - } - if err := InterfaceDown(vethChild); err != nil { - return fmt.Errorf("interface down %s %s", vethChild, err) - } - if err := ChangeInterfaceName(vethChild, defaultDevice); err != nil { - return fmt.Errorf("change %s to %s %s", vethChild, defaultDevice, err) - } - if config.MacAddress != "" { - if err := SetInterfaceMac(defaultDevice, config.MacAddress); err != nil { - return fmt.Errorf("set %s mac %s", defaultDevice, err) - } - } - if err := SetInterfaceIp(defaultDevice, config.Address); err != nil { - return fmt.Errorf("set %s ip %s", defaultDevice, err) - } - if config.IPv6Address != "" { - if err := SetInterfaceIp(defaultDevice, config.IPv6Address); err != nil { - return fmt.Errorf("set %s ipv6 %s", defaultDevice, err) - } - } - - if err := SetMtu(defaultDevice, config.Mtu); err != nil { - return fmt.Errorf("set %s mtu to %d %s", defaultDevice, config.Mtu, err) - } - if err := InterfaceUp(defaultDevice); err != nil { - return fmt.Errorf("%s up %s", defaultDevice, err) - } - if config.Gateway != "" { - if err := SetDefaultGateway(config.Gateway, defaultDevice); err != nil { - return fmt.Errorf("set gateway to %s on device %s failed with %s", config.Gateway, defaultDevice, err) - } - } - if config.IPv6Gateway != "" { - if err := SetDefaultGateway(config.IPv6Gateway, defaultDevice); err != nil { - return fmt.Errorf("set gateway for ipv6 to %s on device %s failed with %s", config.IPv6Gateway, defaultDevice, err) - } - } - return nil -} - -// createVethPair will automatically generage two random names for -// the veth pair and ensure that they have been created -func createVethPair(prefix string, txQueueLen int) (name1 string, name2 string, err error) { - for i := 0; i < 10; i++ { - if name1, err = utils.GenerateRandomName(prefix, 7); err != nil { - return - } - - if name2, err = utils.GenerateRandomName(prefix, 7); err != nil { - return - } - - if err = CreateVethPair(name1, name2, txQueueLen); err != nil { - if err == netlink.ErrInterfaceExists { - continue - } - - return - } - - break - } - - return -} diff --git a/vendor/src/github.com/docker/libcontainer/network/veth_test.go b/vendor/src/github.com/docker/libcontainer/network/veth_test.go deleted file mode 100644 index b92b284eb0..0000000000 --- a/vendor/src/github.com/docker/libcontainer/network/veth_test.go +++ /dev/null @@ -1,53 +0,0 @@ -// +build linux - -package network - -import ( - "testing" - - "github.com/docker/libcontainer/netlink" -) - -func TestGenerateVethNames(t *testing.T) { - if testing.Short() { - return - } - - prefix := "veth" - - name1, name2, err := createVethPair(prefix, 0) - if err != nil { - t.Fatal(err) - } - - if name1 == "" { - t.Fatal("name1 should not be empty") - } - - if name2 == "" { - t.Fatal("name2 should not be empty") - } -} - -func TestCreateDuplicateVethPair(t *testing.T) { - if testing.Short() { - return - } - - prefix := "veth" - - name1, name2, err := createVethPair(prefix, 0) - if err != nil { - t.Fatal(err) - } - - // retry to create the name interfaces and make sure that we get the correct error - err = CreateVethPair(name1, name2, 0) - if err == nil { - t.Fatal("expected error to not be nil with duplicate interface") - } - - if err != netlink.ErrInterfaceExists { - t.Fatalf("expected error to be ErrInterfaceExists but received %q", err) - } -} diff --git a/vendor/src/github.com/docker/libcontainer/network_linux.go b/vendor/src/github.com/docker/libcontainer/network_linux.go new file mode 100644 index 0000000000..687c5e8fa0 --- /dev/null +++ b/vendor/src/github.com/docker/libcontainer/network_linux.go @@ -0,0 +1,208 @@ +// +build linux + +package libcontainer + +import ( + "fmt" + "io/ioutil" + "net" + "path/filepath" + "strconv" + "strings" + + "github.com/docker/libcontainer/netlink" + "github.com/docker/libcontainer/utils" +) + +var strategies = map[string]networkStrategy{ + "veth": &veth{}, + "loopback": &loopback{}, +} + +// networkStrategy represents a specific network configuration for +// a container's networking stack +type networkStrategy interface { + create(*network, int) error + initialize(*network) error +} + +// getStrategy returns the specific network strategy for the +// provided type. +func getStrategy(tpe string) (networkStrategy, error) { + s, exists := strategies[tpe] + if !exists { + return nil, fmt.Errorf("unknown strategy type %q", tpe) + } + return s, nil +} + +// Returns the network statistics for the network interfaces represented by the NetworkRuntimeInfo. +func getNetworkInterfaceStats(interfaceName string) (*NetworkInterface, error) { + out := &NetworkInterface{Name: interfaceName} + // This can happen if the network runtime information is missing - possible if the + // container was created by an old version of libcontainer. + if interfaceName == "" { + return out, nil + } + type netStatsPair struct { + // Where to write the output. + Out *uint64 + // The network stats file to read. + File string + } + // Ingress for host veth is from the container. Hence tx_bytes stat on the host veth is actually number of bytes received by the container. + netStats := []netStatsPair{ + {Out: &out.RxBytes, File: "tx_bytes"}, + {Out: &out.RxPackets, File: "tx_packets"}, + {Out: &out.RxErrors, File: "tx_errors"}, + {Out: &out.RxDropped, File: "tx_dropped"}, + + {Out: &out.TxBytes, File: "rx_bytes"}, + {Out: &out.TxPackets, File: "rx_packets"}, + {Out: &out.TxErrors, File: "rx_errors"}, + {Out: &out.TxDropped, File: "rx_dropped"}, + } + for _, netStat := range netStats { + data, err := readSysfsNetworkStats(interfaceName, netStat.File) + if err != nil { + return nil, err + } + *(netStat.Out) = data + } + return out, nil +} + +// Reads the specified statistics available under /sys/class/net//statistics +func readSysfsNetworkStats(ethInterface, statsFile string) (uint64, error) { + data, err := ioutil.ReadFile(filepath.Join("/sys/class/net", ethInterface, "statistics", statsFile)) + if err != nil { + return 0, err + } + return strconv.ParseUint(strings.TrimSpace(string(data)), 10, 64) +} + +// loopback is a network strategy that provides a basic loopback device +type loopback struct { +} + +func (l *loopback) create(n *network, nspid int) error { + return nil +} + +func (l *loopback) initialize(config *network) error { + iface, err := net.InterfaceByName("lo") + if err != nil { + return err + } + return netlink.NetworkLinkUp(iface) +} + +// veth is a network strategy that uses a bridge and creates +// a veth pair, one that is attached to the bridge on the host and the other +// is placed inside the container's namespace +type veth struct { +} + +func (v *veth) create(n *network, nspid int) (err error) { + tmpName, err := v.generateTempPeerName() + if err != nil { + return err + } + n.TempVethPeerName = tmpName + defer func() { + if err != nil { + netlink.NetworkLinkDel(n.HostInterfaceName) + netlink.NetworkLinkDel(n.TempVethPeerName) + } + }() + if n.Bridge == "" { + return fmt.Errorf("bridge is not specified") + } + bridge, err := net.InterfaceByName(n.Bridge) + if err != nil { + return err + } + if err := netlink.NetworkCreateVethPair(n.HostInterfaceName, n.TempVethPeerName, n.TxQueueLen); err != nil { + return err + } + host, err := net.InterfaceByName(n.HostInterfaceName) + if err != nil { + return err + } + if err := netlink.AddToBridge(host, bridge); err != nil { + return err + } + if err := netlink.NetworkSetMTU(host, n.Mtu); err != nil { + return err + } + if err := netlink.NetworkLinkUp(host); err != nil { + return err + } + child, err := net.InterfaceByName(n.TempVethPeerName) + if err != nil { + return err + } + return netlink.NetworkSetNsPid(child, nspid) +} + +func (v *veth) generateTempPeerName() (string, error) { + return utils.GenerateRandomName("veth", 7) +} + +func (v *veth) initialize(config *network) error { + peer := config.TempVethPeerName + if peer == "" { + return fmt.Errorf("peer is not specified") + } + child, err := net.InterfaceByName(peer) + if err != nil { + return err + } + if err := netlink.NetworkLinkDown(child); err != nil { + return err + } + if err := netlink.NetworkChangeName(child, config.Name); err != nil { + return err + } + // get the interface again after we changed the name as the index also changes. + if child, err = net.InterfaceByName(config.Name); err != nil { + return err + } + if config.MacAddress != "" { + if err := netlink.NetworkSetMacAddress(child, config.MacAddress); err != nil { + return err + } + } + ip, ipNet, err := net.ParseCIDR(config.Address) + if err != nil { + return err + } + if err := netlink.NetworkLinkAddIp(child, ip, ipNet); err != nil { + return err + } + if config.IPv6Address != "" { + if ip, ipNet, err = net.ParseCIDR(config.IPv6Address); err != nil { + return err + } + if err := netlink.NetworkLinkAddIp(child, ip, ipNet); err != nil { + return err + } + } + if err := netlink.NetworkSetMTU(child, config.Mtu); err != nil { + return err + } + if err := netlink.NetworkLinkUp(child); err != nil { + return err + } + if config.Gateway != "" { + if err := netlink.AddDefaultGw(config.Gateway, config.Name); err != nil { + return err + } + } + if config.IPv6Gateway != "" { + if err := netlink.AddDefaultGw(config.IPv6Gateway, config.Name); err != nil { + return err + } + } + return nil +} diff --git a/vendor/src/github.com/docker/libcontainer/notify_linux.go b/vendor/src/github.com/docker/libcontainer/notify_linux.go index a4923273a3..cf81e24d44 100644 --- a/vendor/src/github.com/docker/libcontainer/notify_linux.go +++ b/vendor/src/github.com/docker/libcontainer/notify_linux.go @@ -12,11 +12,11 @@ import ( const oomCgroupName = "memory" -// NotifyOnOOM returns channel on which you can expect event about OOM, +// notifyOnOOM returns channel on which you can expect event about OOM, // if process died without OOM this channel will be closed. // s is current *libcontainer.State for container. -func NotifyOnOOM(s *State) (<-chan struct{}, error) { - dir := s.CgroupPaths[oomCgroupName] +func notifyOnOOM(paths map[string]string) (<-chan struct{}, error) { + dir := paths[oomCgroupName] if dir == "" { return nil, fmt.Errorf("There is no path for %q in state", oomCgroupName) } @@ -26,6 +26,7 @@ func NotifyOnOOM(s *State) (<-chan struct{}, error) { } fd, _, syserr := syscall.RawSyscall(syscall.SYS_EVENTFD2, 0, syscall.FD_CLOEXEC, 0) if syserr != 0 { + oomControl.Close() return nil, syserr } diff --git a/vendor/src/github.com/docker/libcontainer/notify_linux_test.go b/vendor/src/github.com/docker/libcontainer/notify_linux_test.go index 5d1d54576b..09bdf64432 100644 --- a/vendor/src/github.com/docker/libcontainer/notify_linux_test.go +++ b/vendor/src/github.com/docker/libcontainer/notify_linux_test.go @@ -27,12 +27,10 @@ func TestNotifyOnOOM(t *testing.T) { t.Fatal(err) } var eventFd, oomControlFd int - st := &State{ - CgroupPaths: map[string]string{ - "memory": memoryPath, - }, + paths := map[string]string{ + "memory": memoryPath, } - ooms, err := NotifyOnOOM(st) + ooms, err := notifyOnOOM(paths) if err != nil { t.Fatal("expected no error, got:", err) } diff --git a/vendor/src/github.com/docker/libcontainer/namespaces/nsenter/README.md b/vendor/src/github.com/docker/libcontainer/nsenter/README.md similarity index 100% rename from vendor/src/github.com/docker/libcontainer/namespaces/nsenter/README.md rename to vendor/src/github.com/docker/libcontainer/nsenter/README.md diff --git a/vendor/src/github.com/docker/libcontainer/nsenter/nsenter.go b/vendor/src/github.com/docker/libcontainer/nsenter/nsenter.go new file mode 100644 index 0000000000..1c09c77e6e --- /dev/null +++ b/vendor/src/github.com/docker/libcontainer/nsenter/nsenter.go @@ -0,0 +1,25 @@ +// +build linux + +package nsenter + +/* +#cgo CFLAGS: -Wall +extern void nsexec(); +void __attribute__((constructor)) init() { + nsexec(); +} +*/ +import "C" + +// AlwaysFalse is here to stay false +// (and be exported so the compiler doesn't optimize out its reference) +var AlwaysFalse bool + +func init() { + if AlwaysFalse { + // by referencing this C init() in a noop test, it will ensure the compiler + // links in the C function. + // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=65134 + C.init() + } +} diff --git a/vendor/src/github.com/docker/libcontainer/namespaces/nsenter/nsenter_test.go b/vendor/src/github.com/docker/libcontainer/nsenter/nsenter_test.go similarity index 60% rename from vendor/src/github.com/docker/libcontainer/namespaces/nsenter/nsenter_test.go rename to vendor/src/github.com/docker/libcontainer/nsenter/nsenter_test.go index 14870c457e..34e1f52118 100644 --- a/vendor/src/github.com/docker/libcontainer/namespaces/nsenter/nsenter_test.go +++ b/vendor/src/github.com/docker/libcontainer/nsenter/nsenter_test.go @@ -1,17 +1,20 @@ package nsenter import ( + "encoding/json" "fmt" "os" "os/exec" - "os/signal" "strings" - "syscall" "testing" ) +type pid struct { + Pid int `json:"Pid"` +} + func TestNsenterAlivePid(t *testing.T) { - args := []string{"nsenter-exec", "--nspid", fmt.Sprintf("%d", os.Getpid())} + args := []string{"nsenter-exec"} r, w, err := os.Pipe() if err != nil { t.Fatalf("failed to create pipe %v", err) @@ -20,30 +23,39 @@ func TestNsenterAlivePid(t *testing.T) { cmd := &exec.Cmd{ Path: os.Args[0], Args: args, - ExtraFiles: []*os.File{r}, + ExtraFiles: []*os.File{w}, + Env: []string{fmt.Sprintf("_LIBCONTAINER_INITPID=%d", os.Getpid())}, } if err := cmd.Start(); err != nil { t.Fatalf("nsenter failed to start %v", err) } - r.Close() + w.Close() - // unblock the child process - if _, err := w.WriteString("1"); err != nil { - t.Fatalf("parent failed to write synchronization data %v", err) + decoder := json.NewDecoder(r) + var pid *pid + + if err := decoder.Decode(&pid); err != nil { + t.Fatalf("%v", err) } if err := cmd.Wait(); err != nil { t.Fatalf("nsenter exits with a non-zero exit status") } + p, err := os.FindProcess(pid.Pid) + if err != nil { + t.Fatalf("%v", err) + } + p.Wait() } func TestNsenterInvalidPid(t *testing.T) { - args := []string{"nsenter-exec", "--nspid", "-1"} + args := []string{"nsenter-exec"} cmd := &exec.Cmd{ Path: os.Args[0], Args: args, + Env: []string{"_LIBCONTAINER_INITPID=-1"}, } err := cmd.Run() @@ -53,21 +65,16 @@ func TestNsenterInvalidPid(t *testing.T) { } func TestNsenterDeadPid(t *testing.T) { - - c := make(chan os.Signal) - signal.Notify(c, syscall.SIGCHLD) dead_cmd := exec.Command("true") - if err := dead_cmd.Start(); err != nil { + if err := dead_cmd.Run(); err != nil { t.Fatal(err) } - defer dead_cmd.Wait() - <-c // dead_cmd is zombie - - args := []string{"nsenter-exec", "--nspid", fmt.Sprintf("%d", dead_cmd.Process.Pid)} + args := []string{"nsenter-exec"} cmd := &exec.Cmd{ Path: os.Args[0], Args: args, + Env: []string{fmt.Sprintf("_LIBCONTAINER_INITPID=%d", dead_cmd.Process.Pid)}, } err := cmd.Run() diff --git a/vendor/src/github.com/docker/libcontainer/namespaces/nsenter/nsenter_unsupported.go b/vendor/src/github.com/docker/libcontainer/nsenter/nsenter_unsupported.go similarity index 100% rename from vendor/src/github.com/docker/libcontainer/namespaces/nsenter/nsenter_unsupported.go rename to vendor/src/github.com/docker/libcontainer/nsenter/nsenter_unsupported.go diff --git a/vendor/src/github.com/docker/libcontainer/nsenter/nsexec.c b/vendor/src/github.com/docker/libcontainer/nsenter/nsexec.c new file mode 100644 index 0000000000..e7658f3856 --- /dev/null +++ b/vendor/src/github.com/docker/libcontainer/nsenter/nsexec.c @@ -0,0 +1,168 @@ +#define _GNU_SOURCE +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* All arguments should be above stack, because it grows down */ +struct clone_arg { + /* + * Reserve some space for clone() to locate arguments + * and retcode in this place + */ + char stack[4096] __attribute__ ((aligned(8))); + char stack_ptr[0]; + jmp_buf *env; +}; + +#define pr_perror(fmt, ...) fprintf(stderr, "nsenter: " fmt ": %m\n", ##__VA_ARGS__) + +static int child_func(void *_arg) +{ + struct clone_arg *arg = (struct clone_arg *)_arg; + longjmp(*arg->env, 1); +} + +// Use raw setns syscall for versions of glibc that don't include it (namely glibc-2.12) +#if __GLIBC__ == 2 && __GLIBC_MINOR__ < 14 +#define _GNU_SOURCE +#include "syscall.h" +#ifdef SYS_setns +int setns(int fd, int nstype) +{ + return syscall(SYS_setns, fd, nstype); +} +#endif +#endif + +static int clone_parent(jmp_buf * env) __attribute__ ((noinline)); +static int clone_parent(jmp_buf * env) +{ + struct clone_arg ca; + int child; + + ca.env = env; + child = clone(child_func, ca.stack_ptr, CLONE_PARENT | SIGCHLD, &ca); + + return child; +} + +void nsexec() +{ + char *namespaces[] = { "ipc", "uts", "net", "pid", "mnt" }; + const int num = sizeof(namespaces) / sizeof(char *); + jmp_buf env; + char buf[PATH_MAX], *val; + int i, tfd, child, len, consolefd = -1; + pid_t pid; + char *console; + + val = getenv("_LIBCONTAINER_INITPID"); + if (val == NULL) + return; + + pid = atoi(val); + snprintf(buf, sizeof(buf), "%d", pid); + if (strcmp(val, buf)) { + pr_perror("Unable to parse _LIBCONTAINER_INITPID"); + exit(1); + } + + console = getenv("_LIBCONTAINER_CONSOLE_PATH"); + if (console != NULL) { + consolefd = open(console, O_RDWR); + if (consolefd < 0) { + pr_perror("Failed to open console %s", console); + exit(1); + } + } + + /* Check that the specified process exists */ + snprintf(buf, PATH_MAX - 1, "/proc/%d/ns", pid); + tfd = open(buf, O_DIRECTORY | O_RDONLY); + if (tfd == -1) { + pr_perror("Failed to open \"%s\"", buf); + exit(1); + } + + for (i = 0; i < num; i++) { + struct stat st; + int fd; + + /* Symlinks on all namespaces exist for dead processes, but they can't be opened */ + if (fstatat(tfd, namespaces[i], &st, AT_SYMLINK_NOFOLLOW) == -1) { + // Ignore nonexistent namespaces. + if (errno == ENOENT) + continue; + } + + fd = openat(tfd, namespaces[i], O_RDONLY); + if (fd == -1) { + pr_perror("Failed to open ns file %s for ns %s", buf, + namespaces[i]); + exit(1); + } + // Set the namespace. + if (setns(fd, 0) == -1) { + pr_perror("Failed to setns for %s", namespaces[i]); + exit(1); + } + close(fd); + } + + if (setjmp(env) == 1) { + if (setsid() == -1) { + pr_perror("setsid failed"); + exit(1); + } + if (consolefd != -1) { + if (ioctl(consolefd, TIOCSCTTY, 0) == -1) { + pr_perror("ioctl TIOCSCTTY failed"); + exit(1); + } + if (dup2(consolefd, STDIN_FILENO) != STDIN_FILENO) { + pr_perror("Failed to dup 0"); + exit(1); + } + if (dup2(consolefd, STDOUT_FILENO) != STDOUT_FILENO) { + pr_perror("Failed to dup 1"); + exit(1); + } + if (dup2(consolefd, STDERR_FILENO) != STDERR_FILENO) { + pr_perror("Failed to dup 2"); + exit(1); + } + } + // Finish executing, let the Go runtime take over. + return; + } + + child = clone_parent(&env); + if (child < 0) { + pr_perror("Unable to fork"); + exit(1); + } + + len = snprintf(buf, sizeof(buf), "{ \"pid\" : %d }\n", child); + + if (write(3, buf, len) != len) { + pr_perror("Unable to send a child pid"); + kill(child, SIGKILL); + exit(1); + } + + exit(0); +} diff --git a/vendor/src/github.com/docker/libcontainer/nsinit/Makefile b/vendor/src/github.com/docker/libcontainer/nsinit/Makefile new file mode 100644 index 0000000000..57adf154d8 --- /dev/null +++ b/vendor/src/github.com/docker/libcontainer/nsinit/Makefile @@ -0,0 +1,2 @@ +all: + go build -o nsinit . diff --git a/vendor/src/github.com/docker/libcontainer/nsinit/config.go b/vendor/src/github.com/docker/libcontainer/nsinit/config.go index 74c7b3c09f..2ef1aee52f 100644 --- a/vendor/src/github.com/docker/libcontainer/nsinit/config.go +++ b/vendor/src/github.com/docker/libcontainer/nsinit/config.go @@ -1,29 +1,266 @@ package main import ( + "bytes" "encoding/json" - "fmt" - "log" + "io" + "math" + "os" + "path/filepath" + "strings" + "syscall" + "github.com/Sirupsen/logrus" "github.com/codegangsta/cli" + "github.com/docker/libcontainer/configs" + "github.com/docker/libcontainer/utils" ) +const defaultMountFlags = syscall.MS_NOEXEC | syscall.MS_NOSUID | syscall.MS_NODEV + +var createFlags = []cli.Flag{ + cli.IntFlag{Name: "parent-death-signal", Usage: "set the signal that will be delivered to the process in case the parent dies"}, + cli.BoolFlag{Name: "read-only", Usage: "set the container's rootfs as read-only"}, + cli.StringSliceFlag{Name: "bind", Value: &cli.StringSlice{}, Usage: "add bind mounts to the container"}, + cli.StringSliceFlag{Name: "tmpfs", Value: &cli.StringSlice{}, Usage: "add tmpfs mounts to the container"}, + cli.IntFlag{Name: "cpushares", Usage: "set the cpushares for the container"}, + cli.IntFlag{Name: "memory-limit", Usage: "set the memory limit for the container"}, + cli.IntFlag{Name: "memory-swap", Usage: "set the memory swap limit for the container"}, + cli.StringFlag{Name: "cpuset-cpus", Usage: "set the cpuset cpus"}, + cli.StringFlag{Name: "cpuset-mems", Usage: "set the cpuset mems"}, + cli.StringFlag{Name: "apparmor-profile", Usage: "set the apparmor profile"}, + cli.StringFlag{Name: "process-label", Usage: "set the process label"}, + cli.StringFlag{Name: "mount-label", Usage: "set the mount label"}, + cli.StringFlag{Name: "rootfs", Usage: "set the rootfs"}, + cli.IntFlag{Name: "userns-root-uid", Usage: "set the user namespace root uid"}, + cli.StringFlag{Name: "hostname", Value: "nsinit", Usage: "hostname value for the container"}, + cli.StringFlag{Name: "net", Value: "", Usage: "network namespace"}, + cli.StringFlag{Name: "ipc", Value: "", Usage: "ipc namespace"}, + cli.StringFlag{Name: "pid", Value: "", Usage: "pid namespace"}, + cli.StringFlag{Name: "uts", Value: "", Usage: "uts namespace"}, + cli.StringFlag{Name: "mnt", Value: "", Usage: "mount namespace"}, + cli.StringFlag{Name: "veth-bridge", Usage: "veth bridge"}, + cli.StringFlag{Name: "veth-address", Usage: "veth ip address"}, + cli.StringFlag{Name: "veth-gateway", Usage: "veth gateway address"}, + cli.IntFlag{Name: "veth-mtu", Usage: "veth mtu"}, +} + var configCommand = cli.Command{ - Name: "config", - Usage: "display the container configuration", - Action: configAction, + Name: "config", + Usage: "generate a standard configuration file for a container", + Flags: append([]cli.Flag{ + cli.StringFlag{Name: "file,f", Value: "stdout", Usage: "write the configuration to the specified file"}, + }, createFlags...), + Action: func(context *cli.Context) { + template := getTemplate() + modify(template, context) + data, err := json.MarshalIndent(template, "", "\t") + if err != nil { + fatal(err) + } + var f *os.File + filePath := context.String("file") + switch filePath { + case "stdout", "": + f = os.Stdout + default: + if f, err = os.Create(filePath); err != nil { + fatal(err) + } + defer f.Close() + } + if _, err := io.Copy(f, bytes.NewBuffer(data)); err != nil { + fatal(err) + } + }, } -func configAction(context *cli.Context) { - container, err := loadConfig() - if err != nil { - log.Fatal(err) +func modify(config *configs.Config, context *cli.Context) { + config.ParentDeathSignal = context.Int("parent-death-signal") + config.Readonlyfs = context.Bool("read-only") + config.Cgroups.CpusetCpus = context.String("cpuset-cpus") + config.Cgroups.CpusetMems = context.String("cpuset-mems") + config.Cgroups.CpuShares = int64(context.Int("cpushares")) + config.Cgroups.Memory = int64(context.Int("memory-limit")) + config.Cgroups.MemorySwap = int64(context.Int("memory-swap")) + config.AppArmorProfile = context.String("apparmor-profile") + config.ProcessLabel = context.String("process-label") + config.MountLabel = context.String("mount-label") + + rootfs := context.String("rootfs") + if rootfs != "" { + config.Rootfs = rootfs } - data, err := json.MarshalIndent(container, "", "\t") - if err != nil { - log.Fatal(err) + userns_uid := context.Int("userns-root-uid") + if userns_uid != 0 { + config.Namespaces.Add(configs.NEWUSER, "") + config.UidMappings = []configs.IDMap{ + {ContainerID: 0, HostID: userns_uid, Size: 1}, + {ContainerID: 1, HostID: 1, Size: userns_uid - 1}, + {ContainerID: userns_uid + 1, HostID: userns_uid + 1, Size: math.MaxInt32 - userns_uid}, + } + config.GidMappings = []configs.IDMap{ + {ContainerID: 0, HostID: userns_uid, Size: 1}, + {ContainerID: 1, HostID: 1, Size: userns_uid - 1}, + {ContainerID: userns_uid + 1, HostID: userns_uid + 1, Size: math.MaxInt32 - userns_uid}, + } + for _, node := range config.Devices { + node.Uid = uint32(userns_uid) + node.Gid = uint32(userns_uid) + } + } + for _, rawBind := range context.StringSlice("bind") { + mount := &configs.Mount{ + Device: "bind", + Flags: syscall.MS_BIND | syscall.MS_REC, + } + parts := strings.SplitN(rawBind, ":", 3) + switch len(parts) { + default: + logrus.Fatalf("invalid bind mount %s", rawBind) + case 2: + mount.Source, mount.Destination = parts[0], parts[1] + case 3: + mount.Source, mount.Destination = parts[0], parts[1] + switch parts[2] { + case "ro": + mount.Flags |= syscall.MS_RDONLY + case "rw": + default: + logrus.Fatalf("invalid bind mount mode %s", parts[2]) + } + } + config.Mounts = append(config.Mounts, mount) + } + for _, tmpfs := range context.StringSlice("tmpfs") { + config.Mounts = append(config.Mounts, &configs.Mount{ + Device: "tmpfs", + Destination: tmpfs, + Flags: syscall.MS_NOEXEC | syscall.MS_NOSUID | syscall.MS_NODEV, + }) + } + for flag, value := range map[string]configs.NamespaceType{ + "net": configs.NEWNET, + "mnt": configs.NEWNS, + "pid": configs.NEWPID, + "ipc": configs.NEWIPC, + "uts": configs.NEWUTS, + } { + switch v := context.String(flag); v { + case "host": + config.Namespaces.Remove(value) + case "", "private": + if !config.Namespaces.Contains(value) { + config.Namespaces.Add(value, "") + } + if flag == "net" { + config.Networks = []*configs.Network{ + { + Type: "loopback", + Address: "127.0.0.1/0", + Gateway: "localhost", + }, + } + } + if flag == "uts" { + config.Hostname = context.String("hostname") + } + default: + config.Namespaces.Remove(value) + config.Namespaces.Add(value, v) + } + } + if bridge := context.String("veth-bridge"); bridge != "" { + hostName, err := utils.GenerateRandomName("veth", 7) + if err != nil { + logrus.Fatal(err) + } + network := &configs.Network{ + Type: "veth", + Name: "eth0", + Bridge: bridge, + Address: context.String("veth-address"), + Gateway: context.String("veth-gateway"), + Mtu: context.Int("veth-mtu"), + HostInterfaceName: hostName, + } + config.Networks = append(config.Networks, network) } - - fmt.Printf("%s", data) +} + +func getTemplate() *configs.Config { + cwd, err := os.Getwd() + if err != nil { + panic(err) + } + return &configs.Config{ + Rootfs: cwd, + ParentDeathSignal: int(syscall.SIGKILL), + Capabilities: []string{ + "CHOWN", + "DAC_OVERRIDE", + "FSETID", + "FOWNER", + "MKNOD", + "NET_RAW", + "SETGID", + "SETUID", + "SETFCAP", + "SETPCAP", + "NET_BIND_SERVICE", + "SYS_CHROOT", + "KILL", + "AUDIT_WRITE", + }, + Namespaces: configs.Namespaces([]configs.Namespace{ + {Type: configs.NEWNS}, + {Type: configs.NEWUTS}, + {Type: configs.NEWIPC}, + {Type: configs.NEWPID}, + {Type: configs.NEWNET}, + }), + Cgroups: &configs.Cgroup{ + Name: filepath.Base(cwd), + Parent: "nsinit", + AllowAllDevices: false, + AllowedDevices: configs.DefaultAllowedDevices, + }, + Devices: configs.DefaultAutoCreatedDevices, + MaskPaths: []string{ + "/proc/kcore", + }, + ReadonlyPaths: []string{ + "/proc/sys", "/proc/sysrq-trigger", "/proc/irq", "/proc/bus", + }, + Mounts: []*configs.Mount{ + { + Device: "tmpfs", + Source: "shm", + Destination: "/dev/shm", + Data: "mode=1777,size=65536k", + Flags: defaultMountFlags, + }, + { + Source: "mqueue", + Destination: "/dev/mqueue", + Device: "mqueue", + Flags: defaultMountFlags, + }, + { + Source: "sysfs", + Destination: "/sys", + Device: "sysfs", + Flags: defaultMountFlags | syscall.MS_RDONLY, + }, + }, + Rlimits: []configs.Rlimit{ + { + Type: syscall.RLIMIT_NOFILE, + Hard: 1024, + Soft: 1024, + }, + }, + } + } diff --git a/vendor/src/github.com/docker/libcontainer/nsinit/exec.go b/vendor/src/github.com/docker/libcontainer/nsinit/exec.go index 6fc553b8f9..c2b9b0b0f7 100644 --- a/vendor/src/github.com/docker/libcontainer/nsinit/exec.go +++ b/vendor/src/github.com/docker/libcontainer/nsinit/exec.go @@ -1,208 +1,117 @@ package main import ( - "fmt" - "io" - "log" "os" "os/exec" "os/signal" "syscall" - "text/tabwriter" "github.com/codegangsta/cli" - "github.com/docker/docker/pkg/term" "github.com/docker/libcontainer" - consolepkg "github.com/docker/libcontainer/console" - "github.com/docker/libcontainer/namespaces" + "github.com/docker/libcontainer/utils" ) +var standardEnvironment = &cli.StringSlice{ + "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", + "HOSTNAME=nsinit", + "TERM=xterm", +} + var execCommand = cli.Command{ Name: "exec", Usage: "execute a new command inside a container", Action: execAction, - Flags: []cli.Flag{ - cli.BoolFlag{Name: "list", Usage: "list all registered exec functions"}, - cli.StringFlag{Name: "func", Value: "exec", Usage: "function name to exec inside a container"}, - }, + Flags: append([]cli.Flag{ + cli.BoolFlag{Name: "tty,t", Usage: "allocate a TTY to the container"}, + cli.StringFlag{Name: "id", Value: "nsinit", Usage: "specify the ID for a container"}, + cli.StringFlag{Name: "config", Value: "container.json", Usage: "path to the configuration file"}, + cli.BoolFlag{Name: "create", Usage: "create the container's configuration on the fly with arguments"}, + cli.StringFlag{Name: "user,u", Value: "root", Usage: "set the user, uid, and/or gid for the process"}, + cli.StringFlag{Name: "cwd", Value: "", Usage: "set the current working dir"}, + cli.StringSliceFlag{Name: "env", Value: standardEnvironment, Usage: "set environment variables for the process"}, + }, createFlags...), } func execAction(context *cli.Context) { - if context.Bool("list") { - w := tabwriter.NewWriter(os.Stdout, 10, 1, 3, ' ', 0) - fmt.Fprint(w, "NAME\tUSAGE\n") - - for k, f := range argvs { - fmt.Fprintf(w, "%s\t%s\n", k, f.Usage) - } - - w.Flush() - - return - } - - var exitCode int - - container, err := loadConfig() + factory, err := loadFactory(context) if err != nil { - log.Fatal(err) + fatal(err) } - - state, err := libcontainer.GetState(dataPath) - if err != nil && !os.IsNotExist(err) { - log.Fatalf("unable to read state.json: %s", err) - } - - if state != nil { - exitCode, err = startInExistingContainer(container, state, context.String("func"), context) - } else { - exitCode, err = startContainer(container, dataPath, []string(context.Args())) - } - + config, err := loadConfig(context) if err != nil { - log.Fatalf("failed to exec: %s", err) + fatal(err) + } + created := false + container, err := factory.Load(context.String("id")) + if err != nil { + created = true + if container, err = factory.Create(context.String("id"), config); err != nil { + fatal(err) + } + } + process := &libcontainer.Process{ + Args: context.Args(), + Env: context.StringSlice("env"), + User: context.String("user"), + Cwd: context.String("cwd"), + Stdin: os.Stdin, + Stdout: os.Stdout, + Stderr: os.Stderr, + } + rootuid, err := config.HostUID() + if err != nil { + fatal(err) + } + tty, err := newTty(context, process, rootuid) + if err != nil { + fatal(err) + } + if err := tty.attach(process); err != nil { + fatal(err) + } + go handleSignals(process, tty) + err = container.Start(process) + if err != nil { + tty.Close() + if created { + container.Destroy() + } + fatal(err) } - os.Exit(exitCode) -} - -// the process for execing a new process inside an existing container is that we have to exec ourself -// with the nsenter argument so that the C code can setns an the namespaces that we require. Then that -// code path will drop us into the path that we can do the final setup of the namespace and exec the users -// application. -func startInExistingContainer(config *libcontainer.Config, state *libcontainer.State, action string, context *cli.Context) (int, error) { - var ( - master *os.File - console string - err error - - sigc = make(chan os.Signal, 10) - - stdin = os.Stdin - stdout = os.Stdout - stderr = os.Stderr - ) - signal.Notify(sigc) - - if config.Tty { - stdin = nil - stdout = nil - stderr = nil - - master, console, err = consolepkg.CreateMasterAndConsole() - if err != nil { - return -1, err - } - - go io.Copy(master, os.Stdin) - go io.Copy(os.Stdout, master) - - state, err := term.SetRawTerminal(os.Stdin.Fd()) - if err != nil { - return -1, err - } - - defer term.RestoreTerminal(os.Stdin.Fd(), state) - } - - startCallback := func(cmd *exec.Cmd) { - go func() { - resizeTty(master) - - for sig := range sigc { - switch sig { - case syscall.SIGWINCH: - resizeTty(master) - default: - cmd.Process.Signal(sig) - } + status, err := process.Wait() + if err != nil { + exitError, ok := err.(*exec.ExitError) + if ok { + status = exitError.ProcessState + } else { + tty.Close() + if created { + container.Destroy() } - }() + fatal(err) + } } - - return namespaces.ExecIn(config, state, context.Args(), os.Args[0], action, stdin, stdout, stderr, console, startCallback) + if created { + if err := container.Destroy(); err != nil { + tty.Close() + fatal(err) + } + } + tty.Close() + os.Exit(utils.ExitStatus(status.Sys().(syscall.WaitStatus))) } -// startContainer starts the container. Returns the exit status or -1 and an -// error. -// -// Signals sent to the current process will be forwarded to container. -func startContainer(container *libcontainer.Config, dataPath string, args []string) (int, error) { - var ( - cmd *exec.Cmd - sigc = make(chan os.Signal, 10) - ) - +func handleSignals(container *libcontainer.Process, tty *tty) { + sigc := make(chan os.Signal, 10) signal.Notify(sigc) - - createCommand := func(container *libcontainer.Config, console, dataPath, init string, pipe *os.File, args []string) *exec.Cmd { - cmd = namespaces.DefaultCreateCommand(container, console, dataPath, init, pipe, args) - if logPath != "" { - cmd.Env = append(cmd.Env, fmt.Sprintf("log=%s", logPath)) + tty.resize() + for sig := range sigc { + switch sig { + case syscall.SIGWINCH: + tty.resize() + default: + container.Signal(sig) } - return cmd - } - - var ( - master *os.File - console string - err error - - stdin = os.Stdin - stdout = os.Stdout - stderr = os.Stderr - ) - - if container.Tty { - stdin = nil - stdout = nil - stderr = nil - - master, console, err = consolepkg.CreateMasterAndConsole() - if err != nil { - return -1, err - } - - go io.Copy(master, os.Stdin) - go io.Copy(os.Stdout, master) - - state, err := term.SetRawTerminal(os.Stdin.Fd()) - if err != nil { - return -1, err - } - - defer term.RestoreTerminal(os.Stdin.Fd(), state) - } - - startCallback := func() { - go func() { - resizeTty(master) - - for sig := range sigc { - switch sig { - case syscall.SIGWINCH: - resizeTty(master) - default: - cmd.Process.Signal(sig) - } - } - }() - } - - return namespaces.Exec(container, stdin, stdout, stderr, console, dataPath, args, createCommand, startCallback) -} - -func resizeTty(master *os.File) { - if master == nil { - return - } - - ws, err := term.GetWinsize(os.Stdin.Fd()) - if err != nil { - return - } - - if err := term.SetWinsize(master.Fd(), ws); err != nil { - return } } diff --git a/vendor/src/github.com/docker/libcontainer/nsinit/init.go b/vendor/src/github.com/docker/libcontainer/nsinit/init.go index 6df9b1d894..7b2cf1935d 100644 --- a/vendor/src/github.com/docker/libcontainer/nsinit/init.go +++ b/vendor/src/github.com/docker/libcontainer/nsinit/init.go @@ -1,47 +1,28 @@ package main import ( - "log" - "os" "runtime" - "strconv" + log "github.com/Sirupsen/logrus" "github.com/codegangsta/cli" - "github.com/docker/libcontainer/namespaces" + "github.com/docker/libcontainer" + _ "github.com/docker/libcontainer/nsenter" ) -var ( - dataPath = os.Getenv("data_path") - console = os.Getenv("console") - rawPipeFd = os.Getenv("pipe") - - initCommand = cli.Command{ - Name: "init", - Usage: "runs the init process inside the namespace", - Action: initAction, - } -) - -func initAction(context *cli.Context) { - runtime.LockOSThread() - - container, err := loadConfig() - if err != nil { - log.Fatal(err) - } - - rootfs, err := os.Getwd() - if err != nil { - log.Fatal(err) - } - - pipeFd, err := strconv.Atoi(rawPipeFd) - if err != nil { - log.Fatal(err) - } - - pipe := os.NewFile(uintptr(pipeFd), "pipe") - if err := namespaces.Init(container, rootfs, console, pipe, []string(context.Args())); err != nil { - log.Fatalf("unable to initialize for container: %s", err) - } +var initCommand = cli.Command{ + Name: "init", + Usage: "runs the init process inside the namespace", + Action: func(context *cli.Context) { + log.SetLevel(log.DebugLevel) + runtime.GOMAXPROCS(1) + runtime.LockOSThread() + factory, err := libcontainer.New("") + if err != nil { + fatal(err) + } + if err := factory.StartInitialization(3); err != nil { + fatal(err) + } + panic("This line should never been executed") + }, } diff --git a/vendor/src/github.com/docker/libcontainer/nsinit/main.go b/vendor/src/github.com/docker/libcontainer/nsinit/main.go index 53625ca82c..f8ccd62ef5 100644 --- a/vendor/src/github.com/docker/libcontainer/nsinit/main.go +++ b/vendor/src/github.com/docker/libcontainer/nsinit/main.go @@ -1,57 +1,22 @@ package main import ( - "log" "os" - "strings" + log "github.com/Sirupsen/logrus" "github.com/codegangsta/cli" ) -var ( - logPath = os.Getenv("log") - argvs = make(map[string]*rFunc) -) - -func init() { - argvs["exec"] = &rFunc{ - Usage: "execute a process inside an existing container", - Action: nsenterExec, - } - - argvs["mknod"] = &rFunc{ - Usage: "mknod a device inside an existing container", - Action: nsenterMknod, - } - - argvs["ip"] = &rFunc{ - Usage: "display the container's network interfaces", - Action: nsenterIp, - } -} - func main() { - // we need to check our argv 0 for any registred functions to run instead of the - // normal cli code path - f, exists := argvs[strings.TrimPrefix(os.Args[0], "nsenter-")] - if exists { - runFunc(f) - - return - } - app := cli.NewApp() - app.Name = "nsinit" - app.Version = "0.1" + app.Version = "2" app.Author = "libcontainer maintainers" app.Flags = []cli.Flag{ - cli.StringFlag{Name: "nspid"}, - cli.StringFlag{Name: "console"}, + cli.StringFlag{Name: "root", Value: ".", Usage: "root directory for containers"}, + cli.StringFlag{Name: "log-file", Value: "nsinit-debug.log", Usage: "set the log file to output logs to"}, + cli.BoolFlag{Name: "debug", Usage: "enable debug output in the logs"}, } - - app.Before = preload - app.Commands = []cli.Command{ configCommand, execCommand, @@ -60,8 +25,21 @@ func main() { pauseCommand, statsCommand, unpauseCommand, + stateCommand, + } + app.Before = func(context *cli.Context) error { + if context.GlobalBool("debug") { + log.SetLevel(log.DebugLevel) + } + if path := context.GlobalString("log-file"); path != "" { + f, err := os.Create(path) + if err != nil { + return err + } + log.SetOutput(f) + } + return nil } - if err := app.Run(os.Args); err != nil { log.Fatal(err) } diff --git a/vendor/src/github.com/docker/libcontainer/nsinit/nsenter.go b/vendor/src/github.com/docker/libcontainer/nsinit/nsenter.go deleted file mode 100644 index 8dc149f4fb..0000000000 --- a/vendor/src/github.com/docker/libcontainer/nsinit/nsenter.go +++ /dev/null @@ -1,84 +0,0 @@ -package main - -import ( - "fmt" - "log" - "net" - "os" - "strconv" - "strings" - "text/tabwriter" - - "github.com/docker/libcontainer" - "github.com/docker/libcontainer/devices" - "github.com/docker/libcontainer/mount/nodes" - "github.com/docker/libcontainer/namespaces" - _ "github.com/docker/libcontainer/namespaces/nsenter" -) - -// nsenterExec exec's a process inside an existing container -func nsenterExec(config *libcontainer.Config, args []string) { - if err := namespaces.FinalizeSetns(config, args); err != nil { - log.Fatalf("failed to nsenter: %s", err) - } -} - -// nsenterMknod runs mknod inside an existing container -// -// mknod -func nsenterMknod(config *libcontainer.Config, args []string) { - if len(args) != 4 { - log.Fatalf("expected mknod to have 4 arguments not %d", len(args)) - } - - t := rune(args[1][0]) - - major, err := strconv.Atoi(args[2]) - if err != nil { - log.Fatal(err) - } - - minor, err := strconv.Atoi(args[3]) - if err != nil { - log.Fatal(err) - } - - n := &devices.Device{ - Path: args[0], - Type: t, - MajorNumber: int64(major), - MinorNumber: int64(minor), - } - - if err := nodes.CreateDeviceNode("/", n); err != nil { - log.Fatal(err) - } -} - -// nsenterIp displays the network interfaces inside a container's net namespace -func nsenterIp(config *libcontainer.Config, args []string) { - interfaces, err := net.Interfaces() - if err != nil { - log.Fatal(err) - } - - w := tabwriter.NewWriter(os.Stdout, 10, 1, 3, ' ', 0) - fmt.Fprint(w, "NAME\tMTU\tMAC\tFLAG\tADDRS\n") - - for _, iface := range interfaces { - addrs, err := iface.Addrs() - if err != nil { - log.Fatal(err) - } - - o := []string{} - - for _, a := range addrs { - o = append(o, a.String()) - } - - fmt.Fprintf(w, "%s\t%d\t%s\t%s\t%s\n", iface.Name, iface.MTU, iface.HardwareAddr, iface.Flags, strings.Join(o, ",")) - } - - w.Flush() -} diff --git a/vendor/src/github.com/docker/libcontainer/nsinit/oom.go b/vendor/src/github.com/docker/libcontainer/nsinit/oom.go index 106abeb268..a59b753336 100644 --- a/vendor/src/github.com/docker/libcontainer/nsinit/oom.go +++ b/vendor/src/github.com/docker/libcontainer/nsinit/oom.go @@ -4,25 +4,27 @@ import ( "log" "github.com/codegangsta/cli" - "github.com/docker/libcontainer" ) var oomCommand = cli.Command{ - Name: "oom", - Usage: "display oom notifications for a container", - Action: oomAction, -} - -func oomAction(context *cli.Context) { - state, err := libcontainer.GetState(dataPath) - if err != nil { - log.Fatal(err) - } - n, err := libcontainer.NotifyOnOOM(state) - if err != nil { - log.Fatal(err) - } - for _ = range n { - log.Printf("OOM notification received") - } + Name: "oom", + Usage: "display oom notifications for a container", + Flags: []cli.Flag{ + cli.StringFlag{Name: "id", Value: "nsinit", Usage: "specify the ID for a container"}, + }, + Action: func(context *cli.Context) { + container, err := getContainer(context) + if err != nil { + log.Fatal(err) + } + n, err := container.NotifyOOM() + if err != nil { + log.Fatal(err) + } + for x := range n { + // hack for calm down go1.4 gofmt + _ = x + log.Printf("OOM notification received") + } + }, } diff --git a/vendor/src/github.com/docker/libcontainer/nsinit/pause.go b/vendor/src/github.com/docker/libcontainer/nsinit/pause.go index ada24250c1..89af0b6f73 100644 --- a/vendor/src/github.com/docker/libcontainer/nsinit/pause.go +++ b/vendor/src/github.com/docker/libcontainer/nsinit/pause.go @@ -4,46 +4,38 @@ import ( "log" "github.com/codegangsta/cli" - "github.com/docker/libcontainer/cgroups" - "github.com/docker/libcontainer/cgroups/fs" - "github.com/docker/libcontainer/cgroups/systemd" ) var pauseCommand = cli.Command{ - Name: "pause", - Usage: "pause the container's processes", - Action: pauseAction, + Name: "pause", + Usage: "pause the container's processes", + Flags: []cli.Flag{ + cli.StringFlag{Name: "id", Value: "nsinit", Usage: "specify the ID for a container"}, + }, + Action: func(context *cli.Context) { + container, err := getContainer(context) + if err != nil { + log.Fatal(err) + } + if err = container.Pause(); err != nil { + log.Fatal(err) + } + }, } var unpauseCommand = cli.Command{ - Name: "unpause", - Usage: "unpause the container's processes", - Action: unpauseAction, -} - -func pauseAction(context *cli.Context) { - if err := toggle(cgroups.Frozen); err != nil { - log.Fatal(err) - } -} - -func unpauseAction(context *cli.Context) { - if err := toggle(cgroups.Thawed); err != nil { - log.Fatal(err) - } -} - -func toggle(state cgroups.FreezerState) error { - container, err := loadConfig() - if err != nil { - return err - } - - if systemd.UseSystemd() { - err = systemd.Freeze(container.Cgroups, state) - } else { - err = fs.Freeze(container.Cgroups, state) - } - - return err + Name: "unpause", + Usage: "unpause the container's processes", + Flags: []cli.Flag{ + cli.StringFlag{Name: "id", Value: "nsinit", Usage: "specify the ID for a container"}, + }, + Action: func(context *cli.Context) { + container, err := getContainer(context) + if err != nil { + log.Fatal(err) + } + if err = container.Resume(); err != nil { + log.Fatal(err) + } + }, } diff --git a/vendor/src/github.com/docker/libcontainer/nsinit/state.go b/vendor/src/github.com/docker/libcontainer/nsinit/state.go new file mode 100644 index 0000000000..46981bb799 --- /dev/null +++ b/vendor/src/github.com/docker/libcontainer/nsinit/state.go @@ -0,0 +1,31 @@ +package main + +import ( + "encoding/json" + "fmt" + + "github.com/codegangsta/cli" +) + +var stateCommand = cli.Command{ + Name: "state", + Usage: "get the container's current state", + Flags: []cli.Flag{ + cli.StringFlag{Name: "id", Value: "nsinit", Usage: "specify the ID for a container"}, + }, + Action: func(context *cli.Context) { + container, err := getContainer(context) + if err != nil { + fatal(err) + } + state, err := container.State() + if err != nil { + fatal(err) + } + data, err := json.MarshalIndent(state, "", "\t") + if err != nil { + fatal(err) + } + fmt.Printf("%s", data) + }, +} diff --git a/vendor/src/github.com/docker/libcontainer/nsinit/stats.go b/vendor/src/github.com/docker/libcontainer/nsinit/stats.go index 612b4a4bae..49087fa236 100644 --- a/vendor/src/github.com/docker/libcontainer/nsinit/stats.go +++ b/vendor/src/github.com/docker/libcontainer/nsinit/stats.go @@ -3,37 +3,29 @@ package main import ( "encoding/json" "fmt" - "log" "github.com/codegangsta/cli" - "github.com/docker/libcontainer" ) var statsCommand = cli.Command{ - Name: "stats", - Usage: "display statistics for the container", - Action: statsAction, -} - -func statsAction(context *cli.Context) { - container, err := loadConfig() - if err != nil { - log.Fatal(err) - } - - state, err := libcontainer.GetState(dataPath) - if err != nil { - log.Fatal(err) - } - - stats, err := libcontainer.GetStats(container, state) - if err != nil { - log.Fatal(err) - } - data, err := json.MarshalIndent(stats, "", "\t") - if err != nil { - log.Fatal(err) - } - - fmt.Printf("%s", data) + Name: "stats", + Usage: "display statistics for the container", + Flags: []cli.Flag{ + cli.StringFlag{Name: "id", Value: "nsinit", Usage: "specify the ID for a container"}, + }, + Action: func(context *cli.Context) { + container, err := getContainer(context) + if err != nil { + fatal(err) + } + stats, err := container.Stats() + if err != nil { + fatal(err) + } + data, err := json.MarshalIndent(stats, "", "\t") + if err != nil { + fatal(err) + } + fmt.Printf("%s", data) + }, } diff --git a/vendor/src/github.com/docker/libcontainer/nsinit/tty.go b/vendor/src/github.com/docker/libcontainer/nsinit/tty.go new file mode 100644 index 0000000000..668939745a --- /dev/null +++ b/vendor/src/github.com/docker/libcontainer/nsinit/tty.go @@ -0,0 +1,65 @@ +package main + +import ( + "io" + "os" + + "github.com/codegangsta/cli" + "github.com/docker/docker/pkg/term" + "github.com/docker/libcontainer" +) + +func newTty(context *cli.Context, p *libcontainer.Process, rootuid int) (*tty, error) { + if context.Bool("tty") { + console, err := p.NewConsole(rootuid) + if err != nil { + return nil, err + } + return &tty{ + console: console, + }, nil + } + return &tty{}, nil +} + +type tty struct { + console libcontainer.Console + state *term.State +} + +func (t *tty) Close() error { + if t.console != nil { + t.console.Close() + } + if t.state != nil { + term.RestoreTerminal(os.Stdin.Fd(), t.state) + } + return nil +} + +func (t *tty) attach(process *libcontainer.Process) error { + if t.console != nil { + go io.Copy(t.console, os.Stdin) + go io.Copy(os.Stdout, t.console) + state, err := term.SetRawTerminal(os.Stdin.Fd()) + if err != nil { + return err + } + t.state = state + process.Stderr = nil + process.Stdout = nil + process.Stdin = nil + } + return nil +} + +func (t *tty) resize() error { + if t.console == nil { + return nil + } + ws, err := term.GetWinsize(os.Stdin.Fd()) + if err != nil { + return err + } + return term.SetWinsize(t.console.Fd(), ws) +} diff --git a/vendor/src/github.com/docker/libcontainer/nsinit/utils.go b/vendor/src/github.com/docker/libcontainer/nsinit/utils.go index 6a8aafbf17..dad75aec86 100644 --- a/vendor/src/github.com/docker/libcontainer/nsinit/utils.go +++ b/vendor/src/github.com/docker/libcontainer/nsinit/utils.go @@ -2,89 +2,58 @@ package main import ( "encoding/json" - "log" + "fmt" "os" - "path/filepath" "github.com/codegangsta/cli" "github.com/docker/libcontainer" + "github.com/docker/libcontainer/configs" ) -// rFunc is a function registration for calling after an execin -type rFunc struct { - Usage string - Action func(*libcontainer.Config, []string) -} - -func loadConfig() (*libcontainer.Config, error) { - f, err := os.Open(filepath.Join(dataPath, "container.json")) +func loadConfig(context *cli.Context) (*configs.Config, error) { + if context.Bool("create") { + config := getTemplate() + modify(config, context) + return config, nil + } + f, err := os.Open(context.String("config")) if err != nil { return nil, err } defer f.Close() - - var container *libcontainer.Config - if err := json.NewDecoder(f).Decode(&container); err != nil { - return nil, err - } - - return container, nil -} - -func openLog(name string) error { - f, err := os.OpenFile(name, os.O_CREATE|os.O_RDWR|os.O_APPEND, 0755) - if err != nil { - return err - } - - log.SetOutput(f) - - return nil -} - -func findUserArgs() []string { - i := 0 - for _, a := range os.Args { - i++ - - if a == "--" { - break - } - } - - return os.Args[i:] -} - -// loadConfigFromFd loads a container's config from the sync pipe that is provided by -// fd 3 when running a process -func loadConfigFromFd() (*libcontainer.Config, error) { - pipe := os.NewFile(3, "pipe") - defer pipe.Close() - - var config *libcontainer.Config - if err := json.NewDecoder(pipe).Decode(&config); err != nil { + var config *configs.Config + if err := json.NewDecoder(f).Decode(&config); err != nil { return nil, err } return config, nil } -func preload(context *cli.Context) error { - if logPath != "" { - if err := openLog(logPath); err != nil { - return err - } - } - - return nil +func loadFactory(context *cli.Context) (libcontainer.Factory, error) { + return libcontainer.New(context.GlobalString("root"), libcontainer.Cgroupfs) } -func runFunc(f *rFunc) { - userArgs := findUserArgs() - - config, err := loadConfigFromFd() +func getContainer(context *cli.Context) (libcontainer.Container, error) { + factory, err := loadFactory(context) if err != nil { - log.Fatalf("unable to receive config from sync pipe: %s", err) + return nil, err } - - f.Action(config, userArgs) + container, err := factory.Load(context.String("id")) + if err != nil { + return nil, err + } + return container, nil +} + +func fatal(err error) { + if lerr, ok := err.(libcontainer.Error); ok { + lerr.Detail(os.Stderr) + os.Exit(1) + } + fmt.Fprintln(os.Stderr, err) + os.Exit(1) +} + +func fatalf(t string, v ...interface{}) { + fmt.Fprintf(os.Stderr, t, v...) + os.Exit(1) } diff --git a/vendor/src/github.com/docker/libcontainer/process.go b/vendor/src/github.com/docker/libcontainer/process.go index 489666a587..12f90daf79 100644 --- a/vendor/src/github.com/docker/libcontainer/process.go +++ b/vendor/src/github.com/docker/libcontainer/process.go @@ -1,27 +1,82 @@ package libcontainer -import "io" +import ( + "fmt" + "io" + "math" + "os" +) -// Configuration for a process to be run inside a container. -type ProcessConfig struct { +type processOperations interface { + wait() (*os.ProcessState, error) + signal(sig os.Signal) error + pid() int +} + +// Process specifies the configuration and IO for a process inside +// a container. +type Process struct { // The command to be run followed by any arguments. Args []string - // Map of environment variables to their values. + // Env specifies the environment variables for the process. Env []string + // User will set the uid and gid of the executing process running inside the container + // local to the contaienr's user and group configuration. + User string + + // Cwd will change the processes current working directory inside the container's rootfs. + Cwd string + // Stdin is a pointer to a reader which provides the standard input stream. + Stdin io.Reader + // Stdout is a pointer to a writer which receives the standard output stream. + Stdout io.Writer + // Stderr is a pointer to a writer which receives the standard error stream. - // - // If a reader or writer is nil, the input stream is assumed to be empty and the output is - // discarded. - // - // The readers and writers, if supplied, are closed when the process terminates. Their Close - // methods should be idempotent. - // - // Stdout and Stderr may refer to the same writer in which case the output is interspersed. - Stdin io.ReadCloser - Stdout io.WriteCloser - Stderr io.WriteCloser + Stderr io.Writer + + // consolePath is the path to the console allocated to the container. + consolePath string + + ops processOperations +} + +// Wait waits for the process to exit. +// Wait releases any resources associated with the Process +func (p Process) Wait() (*os.ProcessState, error) { + if p.ops == nil { + return nil, newGenericError(fmt.Errorf("invalid process"), ProcessNotExecuted) + } + return p.ops.wait() +} + +// Pid returns the process ID +func (p Process) Pid() (int, error) { + // math.MinInt32 is returned here, because it's invalid value + // for the kill() system call. + if p.ops == nil { + return math.MinInt32, newGenericError(fmt.Errorf("invalid process"), ProcessNotExecuted) + } + return p.ops.pid(), nil +} + +// Signal sends a signal to the Process. +func (p Process) Signal(sig os.Signal) error { + if p.ops == nil { + return newGenericError(fmt.Errorf("invalid process"), ProcessNotExecuted) + } + return p.ops.signal(sig) +} + +// NewConsole creates new console for process and returns it +func (p *Process) NewConsole(rootuid int) (Console, error) { + console, err := newConsole(rootuid, rootuid) + if err != nil { + return nil, err + } + p.consolePath = console.Path() + return console, nil } diff --git a/vendor/src/github.com/docker/libcontainer/process_linux.go b/vendor/src/github.com/docker/libcontainer/process_linux.go new file mode 100644 index 0000000000..5aab5a7f55 --- /dev/null +++ b/vendor/src/github.com/docker/libcontainer/process_linux.go @@ -0,0 +1,240 @@ +// +build linux + +package libcontainer + +import ( + "encoding/json" + "io" + "os" + "os/exec" + "syscall" + + "github.com/docker/libcontainer/cgroups" + "github.com/docker/libcontainer/system" +) + +type parentProcess interface { + // pid returns the pid for the running process. + pid() int + + // start starts the process execution. + start() error + + // send a SIGKILL to the process and wait for the exit. + terminate() error + + // wait waits on the process returning the process state. + wait() (*os.ProcessState, error) + + // startTime return's the process start time. + startTime() (string, error) + + signal(os.Signal) error +} + +type setnsProcess struct { + cmd *exec.Cmd + parentPipe *os.File + childPipe *os.File + cgroupPaths map[string]string + config *initConfig +} + +func (p *setnsProcess) startTime() (string, error) { + return system.GetProcessStartTime(p.pid()) +} + +func (p *setnsProcess) signal(s os.Signal) error { + return p.cmd.Process.Signal(s) +} + +func (p *setnsProcess) start() (err error) { + defer p.parentPipe.Close() + if err = p.execSetns(); err != nil { + return newSystemError(err) + } + if len(p.cgroupPaths) > 0 { + if err := cgroups.EnterPid(p.cgroupPaths, p.cmd.Process.Pid); err != nil { + return newSystemError(err) + } + } + if err := json.NewEncoder(p.parentPipe).Encode(p.config); err != nil { + return newSystemError(err) + } + if err := syscall.Shutdown(int(p.parentPipe.Fd()), syscall.SHUT_WR); err != nil { + return newSystemError(err) + } + // wait for the child process to fully complete and receive an error message + // if one was encoutered + var ierr *genericError + if err := json.NewDecoder(p.parentPipe).Decode(&ierr); err != nil && err != io.EOF { + return newSystemError(err) + } + if ierr != nil { + return newSystemError(ierr) + } + + return nil +} + +// execSetns runs the process that executes C code to perform the setns calls +// because setns support requires the C process to fork off a child and perform the setns +// before the go runtime boots, we wait on the process to die and receive the child's pid +// over the provided pipe. +func (p *setnsProcess) execSetns() error { + err := p.cmd.Start() + p.childPipe.Close() + if err != nil { + return newSystemError(err) + } + status, err := p.cmd.Process.Wait() + if err != nil { + p.cmd.Wait() + return newSystemError(err) + } + if !status.Success() { + p.cmd.Wait() + return newSystemError(&exec.ExitError{ProcessState: status}) + } + var pid *pid + if err := json.NewDecoder(p.parentPipe).Decode(&pid); err != nil { + p.cmd.Wait() + return newSystemError(err) + } + + process, err := os.FindProcess(pid.Pid) + if err != nil { + return err + } + + p.cmd.Process = process + return nil +} + +// terminate sends a SIGKILL to the forked process for the setns routine then waits to +// avoid the process becomming a zombie. +func (p *setnsProcess) terminate() error { + err := p.cmd.Process.Kill() + if _, werr := p.wait(); err == nil { + err = werr + } + return err +} + +func (p *setnsProcess) wait() (*os.ProcessState, error) { + err := p.cmd.Wait() + if err != nil { + return p.cmd.ProcessState, err + } + + return p.cmd.ProcessState, nil +} + +func (p *setnsProcess) pid() int { + return p.cmd.Process.Pid +} + +type initProcess struct { + cmd *exec.Cmd + parentPipe *os.File + childPipe *os.File + config *initConfig + manager cgroups.Manager +} + +func (p *initProcess) pid() int { + return p.cmd.Process.Pid +} + +func (p *initProcess) start() error { + defer p.parentPipe.Close() + err := p.cmd.Start() + p.childPipe.Close() + if err != nil { + return newSystemError(err) + } + // Do this before syncing with child so that no children + // can escape the cgroup + if err := p.manager.Apply(p.pid()); err != nil { + return newSystemError(err) + } + defer func() { + if err != nil { + // TODO: should not be the responsibility to call here + p.manager.Destroy() + } + }() + if err := p.createNetworkInterfaces(); err != nil { + return newSystemError(err) + } + if err := p.sendConfig(); err != nil { + return newSystemError(err) + } + // wait for the child process to fully complete and receive an error message + // if one was encoutered + var ierr *genericError + if err := json.NewDecoder(p.parentPipe).Decode(&ierr); err != nil && err != io.EOF { + return newSystemError(err) + } + if ierr != nil { + return newSystemError(ierr) + } + return nil +} + +func (p *initProcess) wait() (*os.ProcessState, error) { + err := p.cmd.Wait() + if err != nil { + return p.cmd.ProcessState, err + } + // we should kill all processes in cgroup when init is died if we use host PID namespace + if p.cmd.SysProcAttr.Cloneflags&syscall.CLONE_NEWPID == 0 { + killCgroupProcesses(p.manager) + } + return p.cmd.ProcessState, nil +} + +func (p *initProcess) terminate() error { + if p.cmd.Process == nil { + return nil + } + err := p.cmd.Process.Kill() + if _, werr := p.wait(); err == nil { + err = werr + } + return err +} + +func (p *initProcess) startTime() (string, error) { + return system.GetProcessStartTime(p.pid()) +} + +func (p *initProcess) sendConfig() error { + // send the state to the container's init process then shutdown writes for the parent + if err := json.NewEncoder(p.parentPipe).Encode(p.config); err != nil { + return err + } + // shutdown writes for the parent side of the pipe + return syscall.Shutdown(int(p.parentPipe.Fd()), syscall.SHUT_WR) +} + +func (p *initProcess) createNetworkInterfaces() error { + for _, config := range p.config.Config.Networks { + strategy, err := getStrategy(config.Type) + if err != nil { + return err + } + n := &network{ + Network: *config, + } + if err := strategy.create(n, p.pid()); err != nil { + return err + } + p.config.Networks = append(p.config.Networks, n) + } + return nil +} + +func (p *initProcess) signal(s os.Signal) error { + return p.cmd.Process.Signal(s) +} diff --git a/vendor/src/github.com/docker/libcontainer/rootfs_linux.go b/vendor/src/github.com/docker/libcontainer/rootfs_linux.go new file mode 100644 index 0000000000..42d7d17cc7 --- /dev/null +++ b/vendor/src/github.com/docker/libcontainer/rootfs_linux.go @@ -0,0 +1,363 @@ +// +build linux + +package libcontainer + +import ( + "fmt" + "io/ioutil" + "os" + "path/filepath" + "strings" + "syscall" + "time" + + "github.com/docker/libcontainer/configs" + "github.com/docker/libcontainer/label" +) + +const defaultMountFlags = syscall.MS_NOEXEC | syscall.MS_NOSUID | syscall.MS_NODEV + +var baseMounts = []*configs.Mount{ + { + Source: "proc", + Destination: "/proc", + Device: "proc", + Flags: defaultMountFlags, + }, + { + Source: "tmpfs", + Destination: "/dev", + Device: "tmpfs", + Flags: syscall.MS_NOSUID | syscall.MS_STRICTATIME, + Data: "mode=755", + }, + { + Source: "devpts", + Destination: "/dev/pts", + Device: "devpts", + Flags: syscall.MS_NOSUID | syscall.MS_NOEXEC, + Data: "newinstance,ptmxmode=0666,mode=0620,gid=5", + }, +} + +// setupRootfs sets up the devices, mount points, and filesystems for use inside a +// new mount namespace. +func setupRootfs(config *configs.Config, console *linuxConsole) (err error) { + if err := prepareRoot(config); err != nil { + return newSystemError(err) + } + for _, m := range append(baseMounts, config.Mounts...) { + if err := mount(m, config.Rootfs, config.MountLabel); err != nil { + return newSystemError(err) + } + } + if err := createDevices(config); err != nil { + return newSystemError(err) + } + if err := setupPtmx(config, console); err != nil { + return newSystemError(err) + } + // stdin, stdout and stderr could be pointing to /dev/null from parent namespace. + // re-open them inside this namespace. + if err := reOpenDevNull(config.Rootfs); err != nil { + return newSystemError(err) + } + if err := setupDevSymlinks(config.Rootfs); err != nil { + return newSystemError(err) + } + if err := syscall.Chdir(config.Rootfs); err != nil { + return newSystemError(err) + } + if config.NoPivotRoot { + err = msMoveRoot(config.Rootfs) + } else { + err = pivotRoot(config.Rootfs, config.PivotDir) + } + if err != nil { + return newSystemError(err) + } + if config.Readonlyfs { + if err := setReadonly(); err != nil { + return newSystemError(err) + } + } + syscall.Umask(0022) + return nil +} + +func mount(m *configs.Mount, rootfs, mountLabel string) error { + var ( + dest = m.Destination + data = label.FormatMountLabel(m.Data, mountLabel) + ) + if !strings.HasPrefix(dest, rootfs) { + dest = filepath.Join(rootfs, dest) + } + + switch m.Device { + case "proc": + if err := os.MkdirAll(dest, 0755); err != nil && !os.IsExist(err) { + return err + } + return syscall.Mount(m.Source, dest, m.Device, uintptr(m.Flags), "") + case "tmpfs", "mqueue", "devpts", "sysfs": + if err := os.MkdirAll(dest, 0755); err != nil && !os.IsExist(err) { + return err + } + return syscall.Mount(m.Source, dest, m.Device, uintptr(m.Flags), data) + case "bind": + stat, err := os.Stat(m.Source) + if err != nil { + // error out if the source of a bind mount does not exist as we will be + // unable to bind anything to it. + return err + } + if err := createIfNotExists(dest, stat.IsDir()); err != nil { + return err + } + if err := syscall.Mount(m.Source, dest, m.Device, uintptr(m.Flags), data); err != nil { + return err + } + if m.Flags&syscall.MS_RDONLY != 0 { + if err := syscall.Mount(m.Source, dest, m.Device, uintptr(m.Flags|syscall.MS_REMOUNT), ""); err != nil { + return err + } + } + if m.Relabel != "" { + if err := label.Relabel(m.Source, mountLabel, m.Relabel); err != nil { + return err + } + } + if m.Flags&syscall.MS_PRIVATE != 0 { + if err := syscall.Mount("", dest, "none", uintptr(syscall.MS_PRIVATE), ""); err != nil { + return err + } + } + default: + return fmt.Errorf("unknown mount device %q to %q", m.Device, m.Destination) + } + return nil +} + +func setupDevSymlinks(rootfs string) error { + var links = [][2]string{ + {"/proc/self/fd", "/dev/fd"}, + {"/proc/self/fd/0", "/dev/stdin"}, + {"/proc/self/fd/1", "/dev/stdout"}, + {"/proc/self/fd/2", "/dev/stderr"}, + } + // kcore support can be toggled with CONFIG_PROC_KCORE; only create a symlink + // in /dev if it exists in /proc. + if _, err := os.Stat("/proc/kcore"); err == nil { + links = append(links, [2]string{"/proc/kcore", "/dev/kcore"}) + } + for _, link := range links { + var ( + src = link[0] + dst = filepath.Join(rootfs, link[1]) + ) + if err := os.Symlink(src, dst); err != nil && !os.IsExist(err) { + return fmt.Errorf("symlink %s %s %s", src, dst, err) + } + } + return nil +} + +// If stdin, stdout or stderr are pointing to '/dev/null' in the global mount namespace, +// this method will make them point to '/dev/null' in this namespace. +func reOpenDevNull(rootfs string) error { + var stat, devNullStat syscall.Stat_t + file, err := os.Open(filepath.Join(rootfs, "/dev/null")) + if err != nil { + return fmt.Errorf("Failed to open /dev/null - %s", err) + } + defer file.Close() + if err := syscall.Fstat(int(file.Fd()), &devNullStat); err != nil { + return err + } + for fd := 0; fd < 3; fd++ { + if err := syscall.Fstat(fd, &stat); err != nil { + return err + } + if stat.Rdev == devNullStat.Rdev { + // Close and re-open the fd. + if err := syscall.Dup2(int(file.Fd()), fd); err != nil { + return err + } + } + } + return nil +} + +// Create the device nodes in the container. +func createDevices(config *configs.Config) error { + oldMask := syscall.Umask(0000) + for _, node := range config.Devices { + if err := createDeviceNode(config.Rootfs, node); err != nil { + syscall.Umask(oldMask) + return err + } + } + syscall.Umask(oldMask) + return nil +} + +// Creates the device node in the rootfs of the container. +func createDeviceNode(rootfs string, node *configs.Device) error { + dest := filepath.Join(rootfs, node.Path) + if err := os.MkdirAll(filepath.Dir(dest), 0755); err != nil { + return err + } + if err := mknodDevice(dest, node); err != nil { + if os.IsExist(err) { + return nil + } + if err != syscall.EPERM { + return err + } + // containers running in a user namespace are not allowed to mknod + // devices so we can just bind mount it from the host. + f, err := os.Create(dest) + if err != nil && !os.IsExist(err) { + return err + } + if f != nil { + f.Close() + } + return syscall.Mount(node.Path, dest, "bind", syscall.MS_BIND, "") + } + return nil +} + +func mknodDevice(dest string, node *configs.Device) error { + fileMode := node.FileMode + switch node.Type { + case 'c': + fileMode |= syscall.S_IFCHR + case 'b': + fileMode |= syscall.S_IFBLK + default: + return fmt.Errorf("%c is not a valid device type for device %s", node.Type, node.Path) + } + if err := syscall.Mknod(dest, uint32(fileMode), node.Mkdev()); err != nil { + return err + } + return syscall.Chown(dest, int(node.Uid), int(node.Gid)) +} + +func prepareRoot(config *configs.Config) error { + flag := syscall.MS_PRIVATE | syscall.MS_REC + if config.NoPivotRoot { + flag = syscall.MS_SLAVE | syscall.MS_REC + } + if err := syscall.Mount("", "/", "", uintptr(flag), ""); err != nil { + return err + } + return syscall.Mount(config.Rootfs, config.Rootfs, "bind", syscall.MS_BIND|syscall.MS_REC, "") +} + +func setReadonly() error { + return syscall.Mount("/", "/", "bind", syscall.MS_BIND|syscall.MS_REMOUNT|syscall.MS_RDONLY|syscall.MS_REC, "") +} + +func setupPtmx(config *configs.Config, console *linuxConsole) error { + ptmx := filepath.Join(config.Rootfs, "dev/ptmx") + if err := os.Remove(ptmx); err != nil && !os.IsNotExist(err) { + return err + } + if err := os.Symlink("pts/ptmx", ptmx); err != nil { + return fmt.Errorf("symlink dev ptmx %s", err) + } + if console != nil { + return console.mount(config.Rootfs, config.MountLabel, 0, 0) + } + return nil +} + +func pivotRoot(rootfs, pivotBaseDir string) error { + if pivotBaseDir == "" { + pivotBaseDir = "/" + } + tmpDir := filepath.Join(rootfs, pivotBaseDir) + if err := os.MkdirAll(tmpDir, 0755); err != nil { + return fmt.Errorf("can't create tmp dir %s, error %v", tmpDir, err) + } + pivotDir, err := ioutil.TempDir(tmpDir, ".pivot_root") + if err != nil { + return fmt.Errorf("can't create pivot_root dir %s, error %v", pivotDir, err) + } + if err := syscall.PivotRoot(rootfs, pivotDir); err != nil { + return fmt.Errorf("pivot_root %s", err) + } + if err := syscall.Chdir("/"); err != nil { + return fmt.Errorf("chdir / %s", err) + } + // path to pivot dir now changed, update + pivotDir = filepath.Join(pivotBaseDir, filepath.Base(pivotDir)) + if err := syscall.Unmount(pivotDir, syscall.MNT_DETACH); err != nil { + return fmt.Errorf("unmount pivot_root dir %s", err) + } + return os.Remove(pivotDir) +} + +func msMoveRoot(rootfs string) error { + if err := syscall.Mount(rootfs, "/", "", syscall.MS_MOVE, ""); err != nil { + return err + } + if err := syscall.Chroot("."); err != nil { + return err + } + return syscall.Chdir("/") +} + +// createIfNotExists creates a file or a directory only if it does not already exist. +func createIfNotExists(path string, isDir bool) error { + if _, err := os.Stat(path); err != nil { + if os.IsNotExist(err) { + if isDir { + return os.MkdirAll(path, 0755) + } + if err := os.MkdirAll(filepath.Dir(path), 0755); err != nil { + return err + } + f, err := os.OpenFile(path, os.O_CREATE, 0755) + if err != nil { + return err + } + f.Close() + } + } + return nil +} + +// remountReadonly will bind over the top of an existing path and ensure that it is read-only. +func remountReadonly(path string) error { + for i := 0; i < 5; i++ { + if err := syscall.Mount("", path, "", syscall.MS_REMOUNT|syscall.MS_RDONLY, ""); err != nil && !os.IsNotExist(err) { + switch err { + case syscall.EINVAL: + // Probably not a mountpoint, use bind-mount + if err := syscall.Mount(path, path, "", syscall.MS_BIND, ""); err != nil { + return err + } + return syscall.Mount(path, path, "", syscall.MS_BIND|syscall.MS_REMOUNT|syscall.MS_RDONLY|syscall.MS_REC|defaultMountFlags, "") + case syscall.EBUSY: + time.Sleep(100 * time.Millisecond) + continue + default: + return err + } + } + return nil + } + return fmt.Errorf("unable to mount %s as readonly max retries reached", path) +} + +// maskFile bind mounts /dev/null over the top of the specified path inside a container +// to avoid security issues from processes reading information from non-namespace aware mounts ( proc/kcore ). +func maskFile(path string) error { + if err := syscall.Mount("/dev/null", path, "", syscall.MS_BIND, ""); err != nil && !os.IsNotExist(err) { + return err + } + return nil +} diff --git a/vendor/src/github.com/docker/libcontainer/sample_configs/apparmor.json b/vendor/src/github.com/docker/libcontainer/sample_configs/apparmor.json index 96f73cb794..843c2c61ea 100644 --- a/vendor/src/github.com/docker/libcontainer/sample_configs/apparmor.json +++ b/vendor/src/github.com/docker/libcontainer/sample_configs/apparmor.json @@ -1,196 +1,340 @@ { - "capabilities": [ - "CHOWN", - "DAC_OVERRIDE", - "FOWNER", - "MKNOD", - "NET_RAW", - "SETGID", - "SETUID", - "SETFCAP", - "SETPCAP", - "NET_BIND_SERVICE", - "SYS_CHROOT", - "KILL" - ], - "cgroups": { - "allowed_devices": [ - { - "cgroup_permissions": "m", - "major_number": -1, - "minor_number": -1, - "type": 99 - }, - { - "cgroup_permissions": "m", - "major_number": -1, - "minor_number": -1, - "type": 98 - }, - { - "cgroup_permissions": "rwm", - "major_number": 5, - "minor_number": 1, - "path": "/dev/console", - "type": 99 - }, - { - "cgroup_permissions": "rwm", - "major_number": 4, - "path": "/dev/tty0", - "type": 99 - }, - { - "cgroup_permissions": "rwm", - "major_number": 4, - "minor_number": 1, - "path": "/dev/tty1", - "type": 99 - }, - { - "cgroup_permissions": "rwm", - "major_number": 136, - "minor_number": -1, - "type": 99 - }, - { - "cgroup_permissions": "rwm", - "major_number": 5, - "minor_number": 2, - "type": 99 - }, - { - "cgroup_permissions": "rwm", - "major_number": 10, - "minor_number": 200, - "type": 99 - }, - { - "cgroup_permissions": "rwm", - "file_mode": 438, - "major_number": 1, - "minor_number": 3, - "path": "/dev/null", - "type": 99 - }, - { - "cgroup_permissions": "rwm", - "file_mode": 438, - "major_number": 1, - "minor_number": 5, - "path": "/dev/zero", - "type": 99 - }, - { - "cgroup_permissions": "rwm", - "file_mode": 438, - "major_number": 1, - "minor_number": 7, - "path": "/dev/full", - "type": 99 - }, - { - "cgroup_permissions": "rwm", - "file_mode": 438, - "major_number": 5, - "path": "/dev/tty", - "type": 99 - }, - { - "cgroup_permissions": "rwm", - "file_mode": 438, - "major_number": 1, - "minor_number": 9, - "path": "/dev/urandom", - "type": 99 - }, - { - "cgroup_permissions": "rwm", - "file_mode": 438, - "major_number": 1, - "minor_number": 8, - "path": "/dev/random", - "type": 99 - } - ], - "name": "docker-koye", - "parent": "docker" - }, - "restrict_sys": true, - "apparmor_profile": "docker-default", - "mount_config": { - "device_nodes": [ - { - "cgroup_permissions": "rwm", - "file_mode": 438, - "major_number": 1, - "minor_number": 3, - "path": "/dev/null", - "type": 99 - }, - { - "cgroup_permissions": "rwm", - "file_mode": 438, - "major_number": 1, - "minor_number": 5, - "path": "/dev/zero", - "type": 99 - }, - { - "cgroup_permissions": "rwm", - "file_mode": 438, - "major_number": 1, - "minor_number": 7, - "path": "/dev/full", - "type": 99 - }, - { - "cgroup_permissions": "rwm", - "file_mode": 438, - "major_number": 5, - "path": "/dev/tty", - "type": 99 - }, - { - "cgroup_permissions": "rwm", - "file_mode": 438, - "major_number": 1, - "minor_number": 9, - "path": "/dev/urandom", - "type": 99 - }, - { - "cgroup_permissions": "rwm", - "file_mode": 438, - "major_number": 1, - "minor_number": 8, - "path": "/dev/random", - "type": 99 - } - ] - }, - "environment": [ - "HOME=/", - "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", - "HOSTNAME=koye", - "TERM=xterm" - ], - "hostname": "koye", - "namespaces": [ - {"type":"NEWIPC"}, - {"type": "NEWNET"}, - {"type": "NEWNS"}, - {"type": "NEWPID"}, - {"type": "NEWUTS"} - ], - "networks": [ - { - "address": "127.0.0.1/0", - "gateway": "localhost", - "mtu": 1500, - "type": "loopback" - } - ], - "tty": true, - "user": "daemon" + "no_pivot_root": false, + "parent_death_signal": 0, + "pivot_dir": "", + "rootfs": "/rootfs/jessie", + "readonlyfs": false, + "mounts": [ + { + "source": "shm", + "destination": "/dev/shm", + "device": "tmpfs", + "flags": 14, + "data": "mode=1777,size=65536k", + "relabel": "" + }, + { + "source": "mqueue", + "destination": "/dev/mqueue", + "device": "mqueue", + "flags": 14, + "data": "", + "relabel": "" + }, + { + "source": "sysfs", + "destination": "/sys", + "device": "sysfs", + "flags": 15, + "data": "", + "relabel": "" + } + ], + "devices": [ + { + "type": 99, + "path": "/dev/fuse", + "major": 10, + "minor": 229, + "permissions": "rwm", + "file_mode": 0, + "uid": 0, + "gid": 0 + }, + { + "type": 99, + "path": "/dev/null", + "major": 1, + "minor": 3, + "permissions": "rwm", + "file_mode": 438, + "uid": 0, + "gid": 0 + }, + { + "type": 99, + "path": "/dev/zero", + "major": 1, + "minor": 5, + "permissions": "rwm", + "file_mode": 438, + "uid": 0, + "gid": 0 + }, + { + "type": 99, + "path": "/dev/full", + "major": 1, + "minor": 7, + "permissions": "rwm", + "file_mode": 438, + "uid": 0, + "gid": 0 + }, + { + "type": 99, + "path": "/dev/tty", + "major": 5, + "minor": 0, + "permissions": "rwm", + "file_mode": 438, + "uid": 0, + "gid": 0 + }, + { + "type": 99, + "path": "/dev/urandom", + "major": 1, + "minor": 9, + "permissions": "rwm", + "file_mode": 438, + "uid": 0, + "gid": 0 + }, + { + "type": 99, + "path": "/dev/random", + "major": 1, + "minor": 8, + "permissions": "rwm", + "file_mode": 438, + "uid": 0, + "gid": 0 + } + ], + "mount_label": "", + "hostname": "nsinit", + "namespaces": [ + { + "type": "NEWNS", + "path": "" + }, + { + "type": "NEWUTS", + "path": "" + }, + { + "type": "NEWIPC", + "path": "" + }, + { + "type": "NEWPID", + "path": "" + }, + { + "type": "NEWNET", + "path": "" + } + ], + "capabilities": [ + "CHOWN", + "DAC_OVERRIDE", + "FSETID", + "FOWNER", + "MKNOD", + "NET_RAW", + "SETGID", + "SETUID", + "SETFCAP", + "SETPCAP", + "NET_BIND_SERVICE", + "SYS_CHROOT", + "KILL", + "AUDIT_WRITE" + ], + "networks": [ + { + "type": "loopback", + "name": "", + "bridge": "", + "mac_address": "", + "address": "127.0.0.1/0", + "gateway": "localhost", + "ipv6_address": "", + "ipv6_gateway": "", + "mtu": 0, + "txqueuelen": 0, + "host_interface_name": "" + } + ], + "routes": null, + "cgroups": { + "name": "libcontainer", + "parent": "nsinit", + "allow_all_devices": false, + "allowed_devices": [ + { + "type": 99, + "path": "", + "major": -1, + "minor": -1, + "permissions": "m", + "file_mode": 0, + "uid": 0, + "gid": 0 + }, + { + "type": 98, + "path": "", + "major": -1, + "minor": -1, + "permissions": "m", + "file_mode": 0, + "uid": 0, + "gid": 0 + }, + { + "type": 99, + "path": "/dev/console", + "major": 5, + "minor": 1, + "permissions": "rwm", + "file_mode": 0, + "uid": 0, + "gid": 0 + }, + { + "type": 99, + "path": "/dev/tty0", + "major": 4, + "minor": 0, + "permissions": "rwm", + "file_mode": 0, + "uid": 0, + "gid": 0 + }, + { + "type": 99, + "path": "/dev/tty1", + "major": 4, + "minor": 1, + "permissions": "rwm", + "file_mode": 0, + "uid": 0, + "gid": 0 + }, + { + "type": 99, + "path": "", + "major": 136, + "minor": -1, + "permissions": "rwm", + "file_mode": 0, + "uid": 0, + "gid": 0 + }, + { + "type": 99, + "path": "", + "major": 5, + "minor": 2, + "permissions": "rwm", + "file_mode": 0, + "uid": 0, + "gid": 0 + }, + { + "type": 99, + "path": "", + "major": 10, + "minor": 200, + "permissions": "rwm", + "file_mode": 0, + "uid": 0, + "gid": 0 + }, + { + "type": 99, + "path": "/dev/null", + "major": 1, + "minor": 3, + "permissions": "rwm", + "file_mode": 438, + "uid": 0, + "gid": 0 + }, + { + "type": 99, + "path": "/dev/zero", + "major": 1, + "minor": 5, + "permissions": "rwm", + "file_mode": 438, + "uid": 0, + "gid": 0 + }, + { + "type": 99, + "path": "/dev/full", + "major": 1, + "minor": 7, + "permissions": "rwm", + "file_mode": 438, + "uid": 0, + "gid": 0 + }, + { + "type": 99, + "path": "/dev/tty", + "major": 5, + "minor": 0, + "permissions": "rwm", + "file_mode": 438, + "uid": 0, + "gid": 0 + }, + { + "type": 99, + "path": "/dev/urandom", + "major": 1, + "minor": 9, + "permissions": "rwm", + "file_mode": 438, + "uid": 0, + "gid": 0 + }, + { + "type": 99, + "path": "/dev/random", + "major": 1, + "minor": 8, + "permissions": "rwm", + "file_mode": 438, + "uid": 0, + "gid": 0 + } + ], + "memory": 0, + "memory_reservation": 0, + "memory_swap": 0, + "cpu_shares": 0, + "cpu_quota": 0, + "cpu_period": 0, + "cpuset_cpus": "", + "cpuset_mems": "", + "blkio_weight": 0, + "freezer": "", + "slice": "" + }, + "apparmor_profile": "docker-default", + "process_label": "", + "rlimits": [ + { + "type": 7, + "hard": 1024, + "soft": 1024 + } + ], + "additional_groups": null, + "uid_mappings": null, + "gid_mappings": null, + "mask_paths": [ + "/proc/kcore" + ], + "readonly_paths": [ + "/proc/sys", + "/proc/sysrq-trigger", + "/proc/irq", + "/proc/bus" + ] } diff --git a/vendor/src/github.com/docker/libcontainer/sample_configs/attach_to_bridge.json b/vendor/src/github.com/docker/libcontainer/sample_configs/attach_to_bridge.json index e5c03a7ef4..11335b25fe 100644 --- a/vendor/src/github.com/docker/libcontainer/sample_configs/attach_to_bridge.json +++ b/vendor/src/github.com/docker/libcontainer/sample_configs/attach_to_bridge.json @@ -1,202 +1,353 @@ { - "capabilities": [ - "CHOWN", - "DAC_OVERRIDE", - "FOWNER", - "MKNOD", - "NET_RAW", - "SETGID", - "SETUID", - "SETFCAP", - "SETPCAP", - "NET_BIND_SERVICE", - "SYS_CHROOT", - "KILL" - ], - "cgroups": { - "allowed_devices": [ - { - "cgroup_permissions": "m", - "major_number": -1, - "minor_number": -1, - "type": 99 - }, - { - "cgroup_permissions": "m", - "major_number": -1, - "minor_number": -1, - "type": 98 - }, - { - "cgroup_permissions": "rwm", - "major_number": 5, - "minor_number": 1, - "path": "/dev/console", - "type": 99 - }, - { - "cgroup_permissions": "rwm", - "major_number": 4, - "path": "/dev/tty0", - "type": 99 - }, - { - "cgroup_permissions": "rwm", - "major_number": 4, - "minor_number": 1, - "path": "/dev/tty1", - "type": 99 - }, - { - "cgroup_permissions": "rwm", - "major_number": 136, - "minor_number": -1, - "type": 99 - }, - { - "cgroup_permissions": "rwm", - "major_number": 5, - "minor_number": 2, - "type": 99 - }, - { - "cgroup_permissions": "rwm", - "major_number": 10, - "minor_number": 200, - "type": 99 - }, - { - "cgroup_permissions": "rwm", - "file_mode": 438, - "major_number": 1, - "minor_number": 3, - "path": "/dev/null", - "type": 99 - }, - { - "cgroup_permissions": "rwm", - "file_mode": 438, - "major_number": 1, - "minor_number": 5, - "path": "/dev/zero", - "type": 99 - }, - { - "cgroup_permissions": "rwm", - "file_mode": 438, - "major_number": 1, - "minor_number": 7, - "path": "/dev/full", - "type": 99 - }, - { - "cgroup_permissions": "rwm", - "file_mode": 438, - "major_number": 5, - "path": "/dev/tty", - "type": 99 - }, - { - "cgroup_permissions": "rwm", - "file_mode": 438, - "major_number": 1, - "minor_number": 9, - "path": "/dev/urandom", - "type": 99 - }, - { - "cgroup_permissions": "rwm", - "file_mode": 438, - "major_number": 1, - "minor_number": 8, - "path": "/dev/random", - "type": 99 - } - ], - "name": "docker-koye", - "parent": "docker" - }, - "restrict_sys": true, - "mount_config": { - "device_nodes": [ - { - "cgroup_permissions": "rwm", - "file_mode": 438, - "major_number": 1, - "minor_number": 3, - "path": "/dev/null", - "type": 99 - }, - { - "cgroup_permissions": "rwm", - "file_mode": 438, - "major_number": 1, - "minor_number": 5, - "path": "/dev/zero", - "type": 99 - }, - { - "cgroup_permissions": "rwm", - "file_mode": 438, - "major_number": 1, - "minor_number": 7, - "path": "/dev/full", - "type": 99 - }, - { - "cgroup_permissions": "rwm", - "file_mode": 438, - "major_number": 5, - "path": "/dev/tty", - "type": 99 - }, - { - "cgroup_permissions": "rwm", - "file_mode": 438, - "major_number": 1, - "minor_number": 9, - "path": "/dev/urandom", - "type": 99 - }, - { - "cgroup_permissions": "rwm", - "file_mode": 438, - "major_number": 1, - "minor_number": 8, - "path": "/dev/random", - "type": 99 - } - ] - }, - "environment": [ - "HOME=/", - "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", - "HOSTNAME=koye", - "TERM=xterm" - ], - "hostname": "koye", - "namespaces": [ - {"type": "NEWIPC"}, - {"type": "NEWNET"}, - {"type": "NEWNS"}, - {"type": "NEWPID"}, - {"type": "NEWUTS"} - ], - "networks": [ + "no_pivot_root": false, + "parent_death_signal": 0, + "pivot_dir": "", + "rootfs": "/rootfs/jessie", + "readonlyfs": false, + "mounts": [ + { + "source": "shm", + "destination": "/dev/shm", + "device": "tmpfs", + "flags": 14, + "data": "mode=1777,size=65536k", + "relabel": "" + }, + { + "source": "mqueue", + "destination": "/dev/mqueue", + "device": "mqueue", + "flags": 14, + "data": "", + "relabel": "" + }, + { + "source": "sysfs", + "destination": "/sys", + "device": "sysfs", + "flags": 15, + "data": "", + "relabel": "" + } + ], + "devices": [ + { + "type": 99, + "path": "/dev/fuse", + "major": 10, + "minor": 229, + "permissions": "rwm", + "file_mode": 0, + "uid": 0, + "gid": 0 + }, + { + "type": 99, + "path": "/dev/null", + "major": 1, + "minor": 3, + "permissions": "rwm", + "file_mode": 438, + "uid": 0, + "gid": 0 + }, + { + "type": 99, + "path": "/dev/zero", + "major": 1, + "minor": 5, + "permissions": "rwm", + "file_mode": 438, + "uid": 0, + "gid": 0 + }, + { + "type": 99, + "path": "/dev/full", + "major": 1, + "minor": 7, + "permissions": "rwm", + "file_mode": 438, + "uid": 0, + "gid": 0 + }, + { + "type": 99, + "path": "/dev/tty", + "major": 5, + "minor": 0, + "permissions": "rwm", + "file_mode": 438, + "uid": 0, + "gid": 0 + }, + { + "type": 99, + "path": "/dev/urandom", + "major": 1, + "minor": 9, + "permissions": "rwm", + "file_mode": 438, + "uid": 0, + "gid": 0 + }, + { + "type": 99, + "path": "/dev/random", + "major": 1, + "minor": 8, + "permissions": "rwm", + "file_mode": 438, + "uid": 0, + "gid": 0 + } + ], + "mount_label": "", + "hostname": "koye", + "namespaces": [ + { + "type": "NEWNS", + "path": "" + }, + { + "type": "NEWUTS", + "path": "" + }, + { + "type": "NEWIPC", + "path": "" + }, + { + "type": "NEWPID", + "path": "" + }, + { + "type": "NEWNET", + "path": "" + } + ], + "capabilities": [ + "CHOWN", + "DAC_OVERRIDE", + "FSETID", + "FOWNER", + "MKNOD", + "NET_RAW", + "SETGID", + "SETUID", + "SETFCAP", + "SETPCAP", + "NET_BIND_SERVICE", + "SYS_CHROOT", + "KILL", + "AUDIT_WRITE" + ], + "networks": [ + { + "type": "loopback", + "name": "", + "bridge": "", + "mac_address": "", + "address": "127.0.0.1/0", + "gateway": "localhost", + "ipv6_address": "", + "ipv6_gateway": "", + "mtu": 0, + "txqueuelen": 0, + "host_interface_name": "" + }, { - "address": "127.0.0.1/0", - "gateway": "localhost", - "mtu": 1500, - "type": "loopback" - }, - { - "address": "172.17.0.101/16", - "bridge": "docker0", - "veth_prefix": "veth", - "gateway": "172.17.42.1", - "mtu": 1500, - "type": "veth" - } - ], - "tty": true + "type": "veth", + "name": "eth0", + "bridge": "docker0", + "mac_address": "", + "address": "172.17.0.101/16", + "gateway": "172.17.42.1", + "ipv6_address": "", + "ipv6_gateway": "", + "mtu": 1500, + "txqueuelen": 0, + "host_interface_name": "vethnsinit" + } + ], + "routes": null, + "cgroups": { + "name": "libcontainer", + "parent": "nsinit", + "allow_all_devices": false, + "allowed_devices": [ + { + "type": 99, + "path": "", + "major": -1, + "minor": -1, + "permissions": "m", + "file_mode": 0, + "uid": 0, + "gid": 0 + }, + { + "type": 98, + "path": "", + "major": -1, + "minor": -1, + "permissions": "m", + "file_mode": 0, + "uid": 0, + "gid": 0 + }, + { + "type": 99, + "path": "/dev/console", + "major": 5, + "minor": 1, + "permissions": "rwm", + "file_mode": 0, + "uid": 0, + "gid": 0 + }, + { + "type": 99, + "path": "/dev/tty0", + "major": 4, + "minor": 0, + "permissions": "rwm", + "file_mode": 0, + "uid": 0, + "gid": 0 + }, + { + "type": 99, + "path": "/dev/tty1", + "major": 4, + "minor": 1, + "permissions": "rwm", + "file_mode": 0, + "uid": 0, + "gid": 0 + }, + { + "type": 99, + "path": "", + "major": 136, + "minor": -1, + "permissions": "rwm", + "file_mode": 0, + "uid": 0, + "gid": 0 + }, + { + "type": 99, + "path": "", + "major": 5, + "minor": 2, + "permissions": "rwm", + "file_mode": 0, + "uid": 0, + "gid": 0 + }, + { + "type": 99, + "path": "", + "major": 10, + "minor": 200, + "permissions": "rwm", + "file_mode": 0, + "uid": 0, + "gid": 0 + }, + { + "type": 99, + "path": "/dev/null", + "major": 1, + "minor": 3, + "permissions": "rwm", + "file_mode": 438, + "uid": 0, + "gid": 0 + }, + { + "type": 99, + "path": "/dev/zero", + "major": 1, + "minor": 5, + "permissions": "rwm", + "file_mode": 438, + "uid": 0, + "gid": 0 + }, + { + "type": 99, + "path": "/dev/full", + "major": 1, + "minor": 7, + "permissions": "rwm", + "file_mode": 438, + "uid": 0, + "gid": 0 + }, + { + "type": 99, + "path": "/dev/tty", + "major": 5, + "minor": 0, + "permissions": "rwm", + "file_mode": 438, + "uid": 0, + "gid": 0 + }, + { + "type": 99, + "path": "/dev/urandom", + "major": 1, + "minor": 9, + "permissions": "rwm", + "file_mode": 438, + "uid": 0, + "gid": 0 + }, + { + "type": 99, + "path": "/dev/random", + "major": 1, + "minor": 8, + "permissions": "rwm", + "file_mode": 438, + "uid": 0, + "gid": 0 + } + ], + "memory": 0, + "memory_reservation": 0, + "memory_swap": 0, + "cpu_shares": 0, + "cpu_quota": 0, + "cpu_period": 0, + "cpuset_cpus": "", + "cpuset_mems": "", + "blkio_weight": 0, + "freezer": "", + "slice": "" + }, + "apparmor_profile": "", + "process_label": "", + "rlimits": [ + { + "type": 7, + "hard": 1024, + "soft": 1024 + } + ], + "additional_groups": null, + "uid_mappings": null, + "gid_mappings": null, + "mask_paths": [ + "/proc/kcore" + ], + "readonly_paths": [ + "/proc/sys", + "/proc/sysrq-trigger", + "/proc/irq", + "/proc/bus" + ] } diff --git a/vendor/src/github.com/docker/libcontainer/sample_configs/host-pid.json b/vendor/src/github.com/docker/libcontainer/sample_configs/host-pid.json index f47af930e6..bf46150443 100644 --- a/vendor/src/github.com/docker/libcontainer/sample_configs/host-pid.json +++ b/vendor/src/github.com/docker/libcontainer/sample_configs/host-pid.json @@ -1,200 +1,336 @@ { - "capabilities": [ - "CHOWN", - "DAC_OVERRIDE", - "FOWNER", - "MKNOD", - "NET_RAW", - "SETGID", - "SETUID", - "SETFCAP", - "SETPCAP", - "NET_BIND_SERVICE", - "SYS_CHROOT", - "KILL" - ], - "cgroups": { - "allowed_devices": [ - { - "cgroup_permissions": "m", - "major_number": -1, - "minor_number": -1, - "type": 99 - }, - { - "cgroup_permissions": "m", - "major_number": -1, - "minor_number": -1, - "type": 98 - }, - { - "cgroup_permissions": "rwm", - "major_number": 5, - "minor_number": 1, - "path": "/dev/console", - "type": 99 - }, - { - "cgroup_permissions": "rwm", - "major_number": 4, - "path": "/dev/tty0", - "type": 99 - }, - { - "cgroup_permissions": "rwm", - "major_number": 4, - "minor_number": 1, - "path": "/dev/tty1", - "type": 99 - }, - { - "cgroup_permissions": "rwm", - "major_number": 136, - "minor_number": -1, - "type": 99 - }, - { - "cgroup_permissions": "rwm", - "major_number": 5, - "minor_number": 2, - "type": 99 - }, - { - "cgroup_permissions": "rwm", - "major_number": 10, - "minor_number": 200, - "type": 99 - }, - { - "cgroup_permissions": "rwm", - "file_mode": 438, - "major_number": 1, - "minor_number": 3, - "path": "/dev/null", - "type": 99 - }, - { - "cgroup_permissions": "rwm", - "file_mode": 438, - "major_number": 1, - "minor_number": 5, - "path": "/dev/zero", - "type": 99 - }, - { - "cgroup_permissions": "rwm", - "file_mode": 438, - "major_number": 1, - "minor_number": 7, - "path": "/dev/full", - "type": 99 - }, - { - "cgroup_permissions": "rwm", - "file_mode": 438, - "major_number": 5, - "path": "/dev/tty", - "type": 99 - }, - { - "cgroup_permissions": "rwm", - "file_mode": 438, - "major_number": 1, - "minor_number": 9, - "path": "/dev/urandom", - "type": 99 - }, - { - "cgroup_permissions": "rwm", - "file_mode": 438, - "major_number": 1, - "minor_number": 8, - "path": "/dev/random", - "type": 99 - } - ], - "name": "docker-koye", - "parent": "docker" - }, - "restrict_sys": true, - "mount_config": { - "device_nodes": [ - { - "cgroup_permissions": "rwm", - "file_mode": 438, - "major_number": 1, - "minor_number": 3, - "path": "/dev/null", - "type": 99 - }, - { - "cgroup_permissions": "rwm", - "file_mode": 438, - "major_number": 1, - "minor_number": 5, - "path": "/dev/zero", - "type": 99 - }, - { - "cgroup_permissions": "rwm", - "file_mode": 438, - "major_number": 1, - "minor_number": 7, - "path": "/dev/full", - "type": 99 - }, - { - "cgroup_permissions": "rwm", - "file_mode": 438, - "major_number": 5, - "path": "/dev/tty", - "type": 99 - }, - { - "cgroup_permissions": "rwm", - "file_mode": 438, - "major_number": 1, - "minor_number": 9, - "path": "/dev/urandom", - "type": 99 - }, - { - "cgroup_permissions": "rwm", - "file_mode": 438, - "major_number": 1, - "minor_number": 8, - "path": "/dev/random", - "type": 99 - } - ], - "mounts": [ - { - "type": "tmpfs", - "destination": "/tmp" - } - ] - }, - "environment": [ - "HOME=/", - "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", - "HOSTNAME=koye", - "TERM=xterm" - ], - "hostname": "koye", - "namespaces": [ - {"type": "NEWIPC"}, - {"type": "NEWNET"}, - {"type": "NEWNS"}, - {"type": "NEWUTS"} - ], - "networks": [ + "no_pivot_root": false, + "parent_death_signal": 0, + "pivot_dir": "", + "rootfs": "/rootfs/jessie", + "readonlyfs": false, + "mounts": [ + { + "source": "shm", + "destination": "/dev/shm", + "device": "tmpfs", + "flags": 14, + "data": "mode=1777,size=65536k", + "relabel": "" + }, + { + "source": "mqueue", + "destination": "/dev/mqueue", + "device": "mqueue", + "flags": 14, + "data": "", + "relabel": "" + }, + { + "source": "sysfs", + "destination": "/sys", + "device": "sysfs", + "flags": 15, + "data": "", + "relabel": "" + } + ], + "devices": [ + { + "type": 99, + "path": "/dev/fuse", + "major": 10, + "minor": 229, + "permissions": "rwm", + "file_mode": 0, + "uid": 0, + "gid": 0 + }, + { + "type": 99, + "path": "/dev/null", + "major": 1, + "minor": 3, + "permissions": "rwm", + "file_mode": 438, + "uid": 0, + "gid": 0 + }, + { + "type": 99, + "path": "/dev/zero", + "major": 1, + "minor": 5, + "permissions": "rwm", + "file_mode": 438, + "uid": 0, + "gid": 0 + }, + { + "type": 99, + "path": "/dev/full", + "major": 1, + "minor": 7, + "permissions": "rwm", + "file_mode": 438, + "uid": 0, + "gid": 0 + }, + { + "type": 99, + "path": "/dev/tty", + "major": 5, + "minor": 0, + "permissions": "rwm", + "file_mode": 438, + "uid": 0, + "gid": 0 + }, + { + "type": 99, + "path": "/dev/urandom", + "major": 1, + "minor": 9, + "permissions": "rwm", + "file_mode": 438, + "uid": 0, + "gid": 0 + }, + { + "type": 99, + "path": "/dev/random", + "major": 1, + "minor": 8, + "permissions": "rwm", + "file_mode": 438, + "uid": 0, + "gid": 0 + } + ], + "mount_label": "", + "hostname": "nsinit", + "namespaces": [ + { + "type": "NEWNS", + "path": "" + }, + { + "type": "NEWUTS", + "path": "" + }, + { + "type": "NEWIPC", + "path": "" + }, { - "address": "127.0.0.1/0", - "gateway": "localhost", - "mtu": 1500, - "type": "loopback" - } - ], - "tty": true, - "user": "daemon" + "type": "NEWNET", + "path": "" + } + ], + "capabilities": [ + "CHOWN", + "DAC_OVERRIDE", + "FSETID", + "FOWNER", + "MKNOD", + "NET_RAW", + "SETGID", + "SETUID", + "SETFCAP", + "SETPCAP", + "NET_BIND_SERVICE", + "SYS_CHROOT", + "KILL", + "AUDIT_WRITE" + ], + "networks": [ + { + "type": "loopback", + "name": "", + "bridge": "", + "mac_address": "", + "address": "127.0.0.1/0", + "gateway": "localhost", + "ipv6_address": "", + "ipv6_gateway": "", + "mtu": 0, + "txqueuelen": 0, + "host_interface_name": "" + } + ], + "routes": null, + "cgroups": { + "name": "libcontainer", + "parent": "nsinit", + "allow_all_devices": false, + "allowed_devices": [ + { + "type": 99, + "path": "", + "major": -1, + "minor": -1, + "permissions": "m", + "file_mode": 0, + "uid": 0, + "gid": 0 + }, + { + "type": 98, + "path": "", + "major": -1, + "minor": -1, + "permissions": "m", + "file_mode": 0, + "uid": 0, + "gid": 0 + }, + { + "type": 99, + "path": "/dev/console", + "major": 5, + "minor": 1, + "permissions": "rwm", + "file_mode": 0, + "uid": 0, + "gid": 0 + }, + { + "type": 99, + "path": "/dev/tty0", + "major": 4, + "minor": 0, + "permissions": "rwm", + "file_mode": 0, + "uid": 0, + "gid": 0 + }, + { + "type": 99, + "path": "/dev/tty1", + "major": 4, + "minor": 1, + "permissions": "rwm", + "file_mode": 0, + "uid": 0, + "gid": 0 + }, + { + "type": 99, + "path": "", + "major": 136, + "minor": -1, + "permissions": "rwm", + "file_mode": 0, + "uid": 0, + "gid": 0 + }, + { + "type": 99, + "path": "", + "major": 5, + "minor": 2, + "permissions": "rwm", + "file_mode": 0, + "uid": 0, + "gid": 0 + }, + { + "type": 99, + "path": "", + "major": 10, + "minor": 200, + "permissions": "rwm", + "file_mode": 0, + "uid": 0, + "gid": 0 + }, + { + "type": 99, + "path": "/dev/null", + "major": 1, + "minor": 3, + "permissions": "rwm", + "file_mode": 438, + "uid": 0, + "gid": 0 + }, + { + "type": 99, + "path": "/dev/zero", + "major": 1, + "minor": 5, + "permissions": "rwm", + "file_mode": 438, + "uid": 0, + "gid": 0 + }, + { + "type": 99, + "path": "/dev/full", + "major": 1, + "minor": 7, + "permissions": "rwm", + "file_mode": 438, + "uid": 0, + "gid": 0 + }, + { + "type": 99, + "path": "/dev/tty", + "major": 5, + "minor": 0, + "permissions": "rwm", + "file_mode": 438, + "uid": 0, + "gid": 0 + }, + { + "type": 99, + "path": "/dev/urandom", + "major": 1, + "minor": 9, + "permissions": "rwm", + "file_mode": 438, + "uid": 0, + "gid": 0 + }, + { + "type": 99, + "path": "/dev/random", + "major": 1, + "minor": 8, + "permissions": "rwm", + "file_mode": 438, + "uid": 0, + "gid": 0 + } + ], + "memory": 0, + "memory_reservation": 0, + "memory_swap": 0, + "cpu_shares": 0, + "cpu_quota": 0, + "cpu_period": 0, + "cpuset_cpus": "", + "cpuset_mems": "", + "blkio_weight": 0, + "freezer": "", + "slice": "" + }, + "apparmor_profile": "", + "process_label": "", + "rlimits": [ + { + "type": 7, + "hard": 1024, + "soft": 1024 + } + ], + "additional_groups": null, + "uid_mappings": null, + "gid_mappings": null, + "mask_paths": [ + "/proc/kcore" + ], + "readonly_paths": [ + "/proc/sys", + "/proc/sysrq-trigger", + "/proc/irq", + "/proc/bus" + ] } diff --git a/vendor/src/github.com/docker/libcontainer/sample_configs/minimal.json b/vendor/src/github.com/docker/libcontainer/sample_configs/minimal.json index 01de467468..c2f7410956 100644 --- a/vendor/src/github.com/docker/libcontainer/sample_configs/minimal.json +++ b/vendor/src/github.com/docker/libcontainer/sample_configs/minimal.json @@ -1,201 +1,340 @@ { - "capabilities": [ - "CHOWN", - "DAC_OVERRIDE", - "FOWNER", - "MKNOD", - "NET_RAW", - "SETGID", - "SETUID", - "SETFCAP", - "SETPCAP", - "NET_BIND_SERVICE", - "SYS_CHROOT", - "KILL" - ], - "cgroups": { - "allowed_devices": [ - { - "cgroup_permissions": "m", - "major_number": -1, - "minor_number": -1, - "type": 99 - }, - { - "cgroup_permissions": "m", - "major_number": -1, - "minor_number": -1, - "type": 98 - }, - { - "cgroup_permissions": "rwm", - "major_number": 5, - "minor_number": 1, - "path": "/dev/console", - "type": 99 - }, - { - "cgroup_permissions": "rwm", - "major_number": 4, - "path": "/dev/tty0", - "type": 99 - }, - { - "cgroup_permissions": "rwm", - "major_number": 4, - "minor_number": 1, - "path": "/dev/tty1", - "type": 99 - }, - { - "cgroup_permissions": "rwm", - "major_number": 136, - "minor_number": -1, - "type": 99 - }, - { - "cgroup_permissions": "rwm", - "major_number": 5, - "minor_number": 2, - "type": 99 - }, - { - "cgroup_permissions": "rwm", - "major_number": 10, - "minor_number": 200, - "type": 99 - }, - { - "cgroup_permissions": "rwm", - "file_mode": 438, - "major_number": 1, - "minor_number": 3, - "path": "/dev/null", - "type": 99 - }, - { - "cgroup_permissions": "rwm", - "file_mode": 438, - "major_number": 1, - "minor_number": 5, - "path": "/dev/zero", - "type": 99 - }, - { - "cgroup_permissions": "rwm", - "file_mode": 438, - "major_number": 1, - "minor_number": 7, - "path": "/dev/full", - "type": 99 - }, - { - "cgroup_permissions": "rwm", - "file_mode": 438, - "major_number": 5, - "path": "/dev/tty", - "type": 99 - }, - { - "cgroup_permissions": "rwm", - "file_mode": 438, - "major_number": 1, - "minor_number": 9, - "path": "/dev/urandom", - "type": 99 - }, - { - "cgroup_permissions": "rwm", - "file_mode": 438, - "major_number": 1, - "minor_number": 8, - "path": "/dev/random", - "type": 99 - } - ], - "name": "docker-koye", - "parent": "docker" - }, - "restrict_sys": true, - "mount_config": { - "device_nodes": [ - { - "cgroup_permissions": "rwm", - "file_mode": 438, - "major_number": 1, - "minor_number": 3, - "path": "/dev/null", - "type": 99 - }, - { - "cgroup_permissions": "rwm", - "file_mode": 438, - "major_number": 1, - "minor_number": 5, - "path": "/dev/zero", - "type": 99 - }, - { - "cgroup_permissions": "rwm", - "file_mode": 438, - "major_number": 1, - "minor_number": 7, - "path": "/dev/full", - "type": 99 - }, - { - "cgroup_permissions": "rwm", - "file_mode": 438, - "major_number": 5, - "path": "/dev/tty", - "type": 99 - }, - { - "cgroup_permissions": "rwm", - "file_mode": 438, - "major_number": 1, - "minor_number": 9, - "path": "/dev/urandom", - "type": 99 - }, - { - "cgroup_permissions": "rwm", - "file_mode": 438, - "major_number": 1, - "minor_number": 8, - "path": "/dev/random", - "type": 99 - } - ], - "mounts": [ - { - "type": "tmpfs", - "destination": "/tmp" - } - ] - }, - "environment": [ - "HOME=/", - "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", - "HOSTNAME=koye", - "TERM=xterm" - ], - "hostname": "koye", - "namespaces": [ - {"type": "NEWIPC"}, - {"type": "NEWNET"}, - {"type": "NEWNS"}, - {"type": "NEWPID"}, - {"type": "NEWUTS"} - ], - "networks": [ - { - "address": "127.0.0.1/0", - "gateway": "localhost", - "mtu": 1500, - "type": "loopback" - } - ], - "tty": true, - "user": "daemon" + "no_pivot_root": false, + "parent_death_signal": 0, + "pivot_dir": "", + "rootfs": "/home/michael/development/gocode/src/github.com/docker/libcontainer", + "readonlyfs": false, + "mounts": [ + { + "source": "shm", + "destination": "/dev/shm", + "device": "tmpfs", + "flags": 14, + "data": "mode=1777,size=65536k", + "relabel": "" + }, + { + "source": "mqueue", + "destination": "/dev/mqueue", + "device": "mqueue", + "flags": 14, + "data": "", + "relabel": "" + }, + { + "source": "sysfs", + "destination": "/sys", + "device": "sysfs", + "flags": 15, + "data": "", + "relabel": "" + } + ], + "devices": [ + { + "type": 99, + "path": "/dev/fuse", + "major": 10, + "minor": 229, + "permissions": "rwm", + "file_mode": 0, + "uid": 0, + "gid": 0 + }, + { + "type": 99, + "path": "/dev/null", + "major": 1, + "minor": 3, + "permissions": "rwm", + "file_mode": 438, + "uid": 0, + "gid": 0 + }, + { + "type": 99, + "path": "/dev/zero", + "major": 1, + "minor": 5, + "permissions": "rwm", + "file_mode": 438, + "uid": 0, + "gid": 0 + }, + { + "type": 99, + "path": "/dev/full", + "major": 1, + "minor": 7, + "permissions": "rwm", + "file_mode": 438, + "uid": 0, + "gid": 0 + }, + { + "type": 99, + "path": "/dev/tty", + "major": 5, + "minor": 0, + "permissions": "rwm", + "file_mode": 438, + "uid": 0, + "gid": 0 + }, + { + "type": 99, + "path": "/dev/urandom", + "major": 1, + "minor": 9, + "permissions": "rwm", + "file_mode": 438, + "uid": 0, + "gid": 0 + }, + { + "type": 99, + "path": "/dev/random", + "major": 1, + "minor": 8, + "permissions": "rwm", + "file_mode": 438, + "uid": 0, + "gid": 0 + } + ], + "mount_label": "", + "hostname": "nsinit", + "namespaces": [ + { + "type": "NEWNS", + "path": "" + }, + { + "type": "NEWUTS", + "path": "" + }, + { + "type": "NEWIPC", + "path": "" + }, + { + "type": "NEWPID", + "path": "" + }, + { + "type": "NEWNET", + "path": "" + } + ], + "capabilities": [ + "CHOWN", + "DAC_OVERRIDE", + "FSETID", + "FOWNER", + "MKNOD", + "NET_RAW", + "SETGID", + "SETUID", + "SETFCAP", + "SETPCAP", + "NET_BIND_SERVICE", + "SYS_CHROOT", + "KILL", + "AUDIT_WRITE" + ], + "networks": [ + { + "type": "loopback", + "name": "", + "bridge": "", + "mac_address": "", + "address": "127.0.0.1/0", + "gateway": "localhost", + "ipv6_address": "", + "ipv6_gateway": "", + "mtu": 0, + "txqueuelen": 0, + "host_interface_name": "" + } + ], + "routes": null, + "cgroups": { + "name": "libcontainer", + "parent": "nsinit", + "allow_all_devices": false, + "allowed_devices": [ + { + "type": 99, + "path": "", + "major": -1, + "minor": -1, + "permissions": "m", + "file_mode": 0, + "uid": 0, + "gid": 0 + }, + { + "type": 98, + "path": "", + "major": -1, + "minor": -1, + "permissions": "m", + "file_mode": 0, + "uid": 0, + "gid": 0 + }, + { + "type": 99, + "path": "/dev/console", + "major": 5, + "minor": 1, + "permissions": "rwm", + "file_mode": 0, + "uid": 0, + "gid": 0 + }, + { + "type": 99, + "path": "/dev/tty0", + "major": 4, + "minor": 0, + "permissions": "rwm", + "file_mode": 0, + "uid": 0, + "gid": 0 + }, + { + "type": 99, + "path": "/dev/tty1", + "major": 4, + "minor": 1, + "permissions": "rwm", + "file_mode": 0, + "uid": 0, + "gid": 0 + }, + { + "type": 99, + "path": "", + "major": 136, + "minor": -1, + "permissions": "rwm", + "file_mode": 0, + "uid": 0, + "gid": 0 + }, + { + "type": 99, + "path": "", + "major": 5, + "minor": 2, + "permissions": "rwm", + "file_mode": 0, + "uid": 0, + "gid": 0 + }, + { + "type": 99, + "path": "", + "major": 10, + "minor": 200, + "permissions": "rwm", + "file_mode": 0, + "uid": 0, + "gid": 0 + }, + { + "type": 99, + "path": "/dev/null", + "major": 1, + "minor": 3, + "permissions": "rwm", + "file_mode": 438, + "uid": 0, + "gid": 0 + }, + { + "type": 99, + "path": "/dev/zero", + "major": 1, + "minor": 5, + "permissions": "rwm", + "file_mode": 438, + "uid": 0, + "gid": 0 + }, + { + "type": 99, + "path": "/dev/full", + "major": 1, + "minor": 7, + "permissions": "rwm", + "file_mode": 438, + "uid": 0, + "gid": 0 + }, + { + "type": 99, + "path": "/dev/tty", + "major": 5, + "minor": 0, + "permissions": "rwm", + "file_mode": 438, + "uid": 0, + "gid": 0 + }, + { + "type": 99, + "path": "/dev/urandom", + "major": 1, + "minor": 9, + "permissions": "rwm", + "file_mode": 438, + "uid": 0, + "gid": 0 + }, + { + "type": 99, + "path": "/dev/random", + "major": 1, + "minor": 8, + "permissions": "rwm", + "file_mode": 438, + "uid": 0, + "gid": 0 + } + ], + "memory": 0, + "memory_reservation": 0, + "memory_swap": 0, + "cpu_shares": 0, + "cpu_quota": 0, + "cpu_period": 0, + "cpuset_cpus": "", + "cpuset_mems": "", + "blkio_weight": 0, + "freezer": "", + "slice": "" + }, + "apparmor_profile": "", + "process_label": "", + "rlimits": [ + { + "type": 7, + "hard": 1024, + "soft": 1024 + } + ], + "additional_groups": null, + "uid_mappings": null, + "gid_mappings": null, + "mask_paths": [ + "/proc/kcore" + ], + "readonly_paths": [ + "/proc/sys", + "/proc/sysrq-trigger", + "/proc/irq", + "/proc/bus" + ] } diff --git a/vendor/src/github.com/docker/libcontainer/sample_configs/route_source_address_selection.json b/vendor/src/github.com/docker/libcontainer/sample_configs/route_source_address_selection.json deleted file mode 100644 index 9c62045a4b..0000000000 --- a/vendor/src/github.com/docker/libcontainer/sample_configs/route_source_address_selection.json +++ /dev/null @@ -1,209 +0,0 @@ -{ - "capabilities": [ - "CHOWN", - "DAC_OVERRIDE", - "FOWNER", - "MKNOD", - "NET_RAW", - "SETGID", - "SETUID", - "SETFCAP", - "SETPCAP", - "NET_BIND_SERVICE", - "SYS_CHROOT", - "KILL" - ], - "cgroups": { - "allowed_devices": [ - { - "cgroup_permissions": "m", - "major_number": -1, - "minor_number": -1, - "type": 99 - }, - { - "cgroup_permissions": "m", - "major_number": -1, - "minor_number": -1, - "type": 98 - }, - { - "cgroup_permissions": "rwm", - "major_number": 5, - "minor_number": 1, - "path": "/dev/console", - "type": 99 - }, - { - "cgroup_permissions": "rwm", - "major_number": 4, - "path": "/dev/tty0", - "type": 99 - }, - { - "cgroup_permissions": "rwm", - "major_number": 4, - "minor_number": 1, - "path": "/dev/tty1", - "type": 99 - }, - { - "cgroup_permissions": "rwm", - "major_number": 136, - "minor_number": -1, - "type": 99 - }, - { - "cgroup_permissions": "rwm", - "major_number": 5, - "minor_number": 2, - "type": 99 - }, - { - "cgroup_permissions": "rwm", - "major_number": 10, - "minor_number": 200, - "type": 99 - }, - { - "cgroup_permissions": "rwm", - "file_mode": 438, - "major_number": 1, - "minor_number": 3, - "path": "/dev/null", - "type": 99 - }, - { - "cgroup_permissions": "rwm", - "file_mode": 438, - "major_number": 1, - "minor_number": 5, - "path": "/dev/zero", - "type": 99 - }, - { - "cgroup_permissions": "rwm", - "file_mode": 438, - "major_number": 1, - "minor_number": 7, - "path": "/dev/full", - "type": 99 - }, - { - "cgroup_permissions": "rwm", - "file_mode": 438, - "major_number": 5, - "path": "/dev/tty", - "type": 99 - }, - { - "cgroup_permissions": "rwm", - "file_mode": 438, - "major_number": 1, - "minor_number": 9, - "path": "/dev/urandom", - "type": 99 - }, - { - "cgroup_permissions": "rwm", - "file_mode": 438, - "major_number": 1, - "minor_number": 8, - "path": "/dev/random", - "type": 99 - } - ], - "name": "docker-koye", - "parent": "docker" - }, - "restrict_sys": true, - "mount_config": { - "device_nodes": [ - { - "cgroup_permissions": "rwm", - "file_mode": 438, - "major_number": 1, - "minor_number": 3, - "path": "/dev/null", - "type": 99 - }, - { - "cgroup_permissions": "rwm", - "file_mode": 438, - "major_number": 1, - "minor_number": 5, - "path": "/dev/zero", - "type": 99 - }, - { - "cgroup_permissions": "rwm", - "file_mode": 438, - "major_number": 1, - "minor_number": 7, - "path": "/dev/full", - "type": 99 - }, - { - "cgroup_permissions": "rwm", - "file_mode": 438, - "major_number": 5, - "path": "/dev/tty", - "type": 99 - }, - { - "cgroup_permissions": "rwm", - "file_mode": 438, - "major_number": 1, - "minor_number": 9, - "path": "/dev/urandom", - "type": 99 - }, - { - "cgroup_permissions": "rwm", - "file_mode": 438, - "major_number": 1, - "minor_number": 8, - "path": "/dev/random", - "type": 99 - } - ] - }, - "environment": [ - "HOME=/", - "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", - "HOSTNAME=koye", - "TERM=xterm" - ], - "hostname": "koye", - "namespaces": [ - {"type": "NEWIPC"}, - {"type": "NEWNET"}, - {"type": "NEWNS"}, - {"type": "NEWPID"}, - {"type": "NEWUTS"} - ], - "networks": [ - { - "address": "127.0.0.1/0", - "gateway": "localhost", - "mtu": 1500, - "type": "loopback" - }, - { - "address": "172.17.0.101/16", - "bridge": "docker0", - "veth_prefix": "veth", - "mtu": 1500, - "type": "veth" - } - ], - "routes": [ - { - "destination": "0.0.0.0/0", - "source": "172.17.0.101", - "gateway": "172.17.42.1", - "interface_name": "eth0" - } - ], - "tty": true -} diff --git a/vendor/src/github.com/docker/libcontainer/sample_configs/selinux.json b/vendor/src/github.com/docker/libcontainer/sample_configs/selinux.json index 15556488a2..dddfdf1440 100644 --- a/vendor/src/github.com/docker/libcontainer/sample_configs/selinux.json +++ b/vendor/src/github.com/docker/libcontainer/sample_configs/selinux.json @@ -1,197 +1,340 @@ { - "capabilities": [ - "CHOWN", - "DAC_OVERRIDE", - "FOWNER", - "MKNOD", - "NET_RAW", - "SETGID", - "SETUID", - "SETFCAP", - "SETPCAP", - "NET_BIND_SERVICE", - "SYS_CHROOT", - "KILL" - ], - "cgroups": { - "allowed_devices": [ - { - "cgroup_permissions": "m", - "major_number": -1, - "minor_number": -1, - "type": 99 - }, - { - "cgroup_permissions": "m", - "major_number": -1, - "minor_number": -1, - "type": 98 - }, - { - "cgroup_permissions": "rwm", - "major_number": 5, - "minor_number": 1, - "path": "/dev/console", - "type": 99 - }, - { - "cgroup_permissions": "rwm", - "major_number": 4, - "path": "/dev/tty0", - "type": 99 - }, - { - "cgroup_permissions": "rwm", - "major_number": 4, - "minor_number": 1, - "path": "/dev/tty1", - "type": 99 - }, - { - "cgroup_permissions": "rwm", - "major_number": 136, - "minor_number": -1, - "type": 99 - }, - { - "cgroup_permissions": "rwm", - "major_number": 5, - "minor_number": 2, - "type": 99 - }, - { - "cgroup_permissions": "rwm", - "major_number": 10, - "minor_number": 200, - "type": 99 - }, - { - "cgroup_permissions": "rwm", - "file_mode": 438, - "major_number": 1, - "minor_number": 3, - "path": "/dev/null", - "type": 99 - }, - { - "cgroup_permissions": "rwm", - "file_mode": 438, - "major_number": 1, - "minor_number": 5, - "path": "/dev/zero", - "type": 99 - }, - { - "cgroup_permissions": "rwm", - "file_mode": 438, - "major_number": 1, - "minor_number": 7, - "path": "/dev/full", - "type": 99 - }, - { - "cgroup_permissions": "rwm", - "file_mode": 438, - "major_number": 5, - "path": "/dev/tty", - "type": 99 - }, - { - "cgroup_permissions": "rwm", - "file_mode": 438, - "major_number": 1, - "minor_number": 9, - "path": "/dev/urandom", - "type": 99 - }, - { - "cgroup_permissions": "rwm", - "file_mode": 438, - "major_number": 1, - "minor_number": 8, - "path": "/dev/random", - "type": 99 - } - ], - "name": "docker-koye", - "parent": "docker" - }, - "restrict_sys": true, - "process_label": "system_u:system_r:svirt_lxc_net_t:s0:c164,c475", - "mount_config": { - "mount_label": "system_u:system_r:svirt_lxc_net_t:s0:c164,c475", - "device_nodes": [ - { - "cgroup_permissions": "rwm", - "file_mode": 438, - "major_number": 1, - "minor_number": 3, - "path": "/dev/null", - "type": 99 - }, - { - "cgroup_permissions": "rwm", - "file_mode": 438, - "major_number": 1, - "minor_number": 5, - "path": "/dev/zero", - "type": 99 - }, - { - "cgroup_permissions": "rwm", - "file_mode": 438, - "major_number": 1, - "minor_number": 7, - "path": "/dev/full", - "type": 99 - }, - { - "cgroup_permissions": "rwm", - "file_mode": 438, - "major_number": 5, - "path": "/dev/tty", - "type": 99 - }, - { - "cgroup_permissions": "rwm", - "file_mode": 438, - "major_number": 1, - "minor_number": 9, - "path": "/dev/urandom", - "type": 99 - }, - { - "cgroup_permissions": "rwm", - "file_mode": 438, - "major_number": 1, - "minor_number": 8, - "path": "/dev/random", - "type": 99 - } - ] - }, - "environment": [ - "HOME=/", - "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", - "HOSTNAME=koye", - "TERM=xterm" - ], - "hostname": "koye", - "namespaces": [ - {"type": "NEWIPC"}, - {"type": "NEWNET"}, - {"type": "NEWNS"}, - {"type": "NEWPID"}, - {"type": "NEWUTS"} - ], - "networks": [ - { - "address": "127.0.0.1/0", - "gateway": "localhost", - "mtu": 1500, - "type": "loopback" - } - ], - "tty": true, - "user": "daemon" + "no_pivot_root": false, + "parent_death_signal": 0, + "pivot_dir": "", + "rootfs": "/rootfs/jessie", + "readonlyfs": false, + "mounts": [ + { + "source": "shm", + "destination": "/dev/shm", + "device": "tmpfs", + "flags": 14, + "data": "mode=1777,size=65536k", + "relabel": "" + }, + { + "source": "mqueue", + "destination": "/dev/mqueue", + "device": "mqueue", + "flags": 14, + "data": "", + "relabel": "" + }, + { + "source": "sysfs", + "destination": "/sys", + "device": "sysfs", + "flags": 15, + "data": "", + "relabel": "" + } + ], + "devices": [ + { + "type": 99, + "path": "/dev/fuse", + "major": 10, + "minor": 229, + "permissions": "rwm", + "file_mode": 0, + "uid": 0, + "gid": 0 + }, + { + "type": 99, + "path": "/dev/null", + "major": 1, + "minor": 3, + "permissions": "rwm", + "file_mode": 438, + "uid": 0, + "gid": 0 + }, + { + "type": 99, + "path": "/dev/zero", + "major": 1, + "minor": 5, + "permissions": "rwm", + "file_mode": 438, + "uid": 0, + "gid": 0 + }, + { + "type": 99, + "path": "/dev/full", + "major": 1, + "minor": 7, + "permissions": "rwm", + "file_mode": 438, + "uid": 0, + "gid": 0 + }, + { + "type": 99, + "path": "/dev/tty", + "major": 5, + "minor": 0, + "permissions": "rwm", + "file_mode": 438, + "uid": 0, + "gid": 0 + }, + { + "type": 99, + "path": "/dev/urandom", + "major": 1, + "minor": 9, + "permissions": "rwm", + "file_mode": 438, + "uid": 0, + "gid": 0 + }, + { + "type": 99, + "path": "/dev/random", + "major": 1, + "minor": 8, + "permissions": "rwm", + "file_mode": 438, + "uid": 0, + "gid": 0 + } + ], + "mount_label": "system_u:system_r:svirt_lxc_net_t:s0:c164,c475", + "hostname": "nsinit", + "namespaces": [ + { + "type": "NEWNS", + "path": "" + }, + { + "type": "NEWUTS", + "path": "" + }, + { + "type": "NEWIPC", + "path": "" + }, + { + "type": "NEWPID", + "path": "" + }, + { + "type": "NEWNET", + "path": "" + } + ], + "capabilities": [ + "CHOWN", + "DAC_OVERRIDE", + "FSETID", + "FOWNER", + "MKNOD", + "NET_RAW", + "SETGID", + "SETUID", + "SETFCAP", + "SETPCAP", + "NET_BIND_SERVICE", + "SYS_CHROOT", + "KILL", + "AUDIT_WRITE" + ], + "networks": [ + { + "type": "loopback", + "name": "", + "bridge": "", + "mac_address": "", + "address": "127.0.0.1/0", + "gateway": "localhost", + "ipv6_address": "", + "ipv6_gateway": "", + "mtu": 0, + "txqueuelen": 0, + "host_interface_name": "" + } + ], + "routes": null, + "cgroups": { + "name": "libcontainer", + "parent": "nsinit", + "allow_all_devices": false, + "allowed_devices": [ + { + "type": 99, + "path": "", + "major": -1, + "minor": -1, + "permissions": "m", + "file_mode": 0, + "uid": 0, + "gid": 0 + }, + { + "type": 98, + "path": "", + "major": -1, + "minor": -1, + "permissions": "m", + "file_mode": 0, + "uid": 0, + "gid": 0 + }, + { + "type": 99, + "path": "/dev/console", + "major": 5, + "minor": 1, + "permissions": "rwm", + "file_mode": 0, + "uid": 0, + "gid": 0 + }, + { + "type": 99, + "path": "/dev/tty0", + "major": 4, + "minor": 0, + "permissions": "rwm", + "file_mode": 0, + "uid": 0, + "gid": 0 + }, + { + "type": 99, + "path": "/dev/tty1", + "major": 4, + "minor": 1, + "permissions": "rwm", + "file_mode": 0, + "uid": 0, + "gid": 0 + }, + { + "type": 99, + "path": "", + "major": 136, + "minor": -1, + "permissions": "rwm", + "file_mode": 0, + "uid": 0, + "gid": 0 + }, + { + "type": 99, + "path": "", + "major": 5, + "minor": 2, + "permissions": "rwm", + "file_mode": 0, + "uid": 0, + "gid": 0 + }, + { + "type": 99, + "path": "", + "major": 10, + "minor": 200, + "permissions": "rwm", + "file_mode": 0, + "uid": 0, + "gid": 0 + }, + { + "type": 99, + "path": "/dev/null", + "major": 1, + "minor": 3, + "permissions": "rwm", + "file_mode": 438, + "uid": 0, + "gid": 0 + }, + { + "type": 99, + "path": "/dev/zero", + "major": 1, + "minor": 5, + "permissions": "rwm", + "file_mode": 438, + "uid": 0, + "gid": 0 + }, + { + "type": 99, + "path": "/dev/full", + "major": 1, + "minor": 7, + "permissions": "rwm", + "file_mode": 438, + "uid": 0, + "gid": 0 + }, + { + "type": 99, + "path": "/dev/tty", + "major": 5, + "minor": 0, + "permissions": "rwm", + "file_mode": 438, + "uid": 0, + "gid": 0 + }, + { + "type": 99, + "path": "/dev/urandom", + "major": 1, + "minor": 9, + "permissions": "rwm", + "file_mode": 438, + "uid": 0, + "gid": 0 + }, + { + "type": 99, + "path": "/dev/random", + "major": 1, + "minor": 8, + "permissions": "rwm", + "file_mode": 438, + "uid": 0, + "gid": 0 + } + ], + "memory": 0, + "memory_reservation": 0, + "memory_swap": 0, + "cpu_shares": 0, + "cpu_quota": 0, + "cpu_period": 0, + "cpuset_cpus": "", + "cpuset_mems": "", + "blkio_weight": 0, + "freezer": "", + "slice": "" + }, + "apparmor_profile": "", + "process_label": "system_u:system_r:svirt_lxc_net_t:s0:c164,c475", + "rlimits": [ + { + "type": 7, + "hard": 1024, + "soft": 1024 + } + ], + "additional_groups": null, + "uid_mappings": null, + "gid_mappings": null, + "mask_paths": [ + "/proc/kcore" + ], + "readonly_paths": [ + "/proc/sys", + "/proc/sysrq-trigger", + "/proc/irq", + "/proc/bus" + ] } diff --git a/vendor/src/github.com/docker/libcontainer/sample_configs/userns.json b/vendor/src/github.com/docker/libcontainer/sample_configs/userns.json new file mode 100644 index 0000000000..2b1fb90b4a --- /dev/null +++ b/vendor/src/github.com/docker/libcontainer/sample_configs/userns.json @@ -0,0 +1,376 @@ +{ + "no_pivot_root": false, + "parent_death_signal": 0, + "pivot_dir": "", + "rootfs": "/rootfs/jessie", + "readonlyfs": false, + "mounts": [ + { + "source": "shm", + "destination": "/dev/shm", + "device": "tmpfs", + "flags": 14, + "data": "mode=1777,size=65536k", + "relabel": "" + }, + { + "source": "mqueue", + "destination": "/dev/mqueue", + "device": "mqueue", + "flags": 14, + "data": "", + "relabel": "" + }, + { + "source": "sysfs", + "destination": "/sys", + "device": "sysfs", + "flags": 15, + "data": "", + "relabel": "" + } + ], + "devices": [ + { + "type": 99, + "path": "/dev/fuse", + "major": 10, + "minor": 229, + "permissions": "rwm", + "file_mode": 0, + "uid": 0, + "gid": 0 + }, + { + "type": 99, + "path": "/dev/null", + "major": 1, + "minor": 3, + "permissions": "rwm", + "file_mode": 438, + "uid": 0, + "gid": 0 + }, + { + "type": 99, + "path": "/dev/zero", + "major": 1, + "minor": 5, + "permissions": "rwm", + "file_mode": 438, + "uid": 0, + "gid": 0 + }, + { + "type": 99, + "path": "/dev/full", + "major": 1, + "minor": 7, + "permissions": "rwm", + "file_mode": 438, + "uid": 0, + "gid": 0 + }, + { + "type": 99, + "path": "/dev/tty", + "major": 5, + "minor": 0, + "permissions": "rwm", + "file_mode": 438, + "uid": 0, + "gid": 0 + }, + { + "type": 99, + "path": "/dev/urandom", + "major": 1, + "minor": 9, + "permissions": "rwm", + "file_mode": 438, + "uid": 0, + "gid": 0 + }, + { + "type": 99, + "path": "/dev/random", + "major": 1, + "minor": 8, + "permissions": "rwm", + "file_mode": 438, + "uid": 0, + "gid": 0 + } + ], + "mount_label": "", + "hostname": "nsinit", + "namespaces": [ + { + "type": "NEWNS", + "path": "" + }, + { + "type": "NEWUTS", + "path": "" + }, + { + "type": "NEWIPC", + "path": "" + }, + { + "type": "NEWPID", + "path": "" + }, + { + "type": "NEWNET", + "path": "" + }, + { + "type": "NEWUSER", + "path": "" + } + ], + "capabilities": [ + "CHOWN", + "DAC_OVERRIDE", + "FSETID", + "FOWNER", + "MKNOD", + "NET_RAW", + "SETGID", + "SETUID", + "SETFCAP", + "SETPCAP", + "NET_BIND_SERVICE", + "SYS_CHROOT", + "KILL", + "AUDIT_WRITE" + ], + "networks": [ + { + "type": "loopback", + "name": "", + "bridge": "", + "mac_address": "", + "address": "127.0.0.1/0", + "gateway": "localhost", + "ipv6_address": "", + "ipv6_gateway": "", + "mtu": 0, + "txqueuelen": 0, + "host_interface_name": "" + } + ], + "routes": null, + "cgroups": { + "name": "libcontainer", + "parent": "nsinit", + "allow_all_devices": false, + "allowed_devices": [ + { + "type": 99, + "path": "", + "major": -1, + "minor": -1, + "permissions": "m", + "file_mode": 0, + "uid": 0, + "gid": 0 + }, + { + "type": 98, + "path": "", + "major": -1, + "minor": -1, + "permissions": "m", + "file_mode": 0, + "uid": 0, + "gid": 0 + }, + { + "type": 99, + "path": "/dev/console", + "major": 5, + "minor": 1, + "permissions": "rwm", + "file_mode": 0, + "uid": 0, + "gid": 0 + }, + { + "type": 99, + "path": "/dev/tty0", + "major": 4, + "minor": 0, + "permissions": "rwm", + "file_mode": 0, + "uid": 0, + "gid": 0 + }, + { + "type": 99, + "path": "/dev/tty1", + "major": 4, + "minor": 1, + "permissions": "rwm", + "file_mode": 0, + "uid": 0, + "gid": 0 + }, + { + "type": 99, + "path": "", + "major": 136, + "minor": -1, + "permissions": "rwm", + "file_mode": 0, + "uid": 0, + "gid": 0 + }, + { + "type": 99, + "path": "", + "major": 5, + "minor": 2, + "permissions": "rwm", + "file_mode": 0, + "uid": 0, + "gid": 0 + }, + { + "type": 99, + "path": "", + "major": 10, + "minor": 200, + "permissions": "rwm", + "file_mode": 0, + "uid": 0, + "gid": 0 + }, + { + "type": 99, + "path": "/dev/null", + "major": 1, + "minor": 3, + "permissions": "rwm", + "file_mode": 438, + "uid": 0, + "gid": 0 + }, + { + "type": 99, + "path": "/dev/zero", + "major": 1, + "minor": 5, + "permissions": "rwm", + "file_mode": 438, + "uid": 0, + "gid": 0 + }, + { + "type": 99, + "path": "/dev/full", + "major": 1, + "minor": 7, + "permissions": "rwm", + "file_mode": 438, + "uid": 0, + "gid": 0 + }, + { + "type": 99, + "path": "/dev/tty", + "major": 5, + "minor": 0, + "permissions": "rwm", + "file_mode": 438, + "uid": 0, + "gid": 0 + }, + { + "type": 99, + "path": "/dev/urandom", + "major": 1, + "minor": 9, + "permissions": "rwm", + "file_mode": 438, + "uid": 0, + "gid": 0 + }, + { + "type": 99, + "path": "/dev/random", + "major": 1, + "minor": 8, + "permissions": "rwm", + "file_mode": 438, + "uid": 0, + "gid": 0 + } + ], + "memory": 0, + "memory_reservation": 0, + "memory_swap": 0, + "cpu_shares": 0, + "cpu_quota": 0, + "cpu_period": 0, + "cpuset_cpus": "", + "cpuset_mems": "", + "blkio_weight": 0, + "freezer": "", + "slice": "" + }, + "apparmor_profile": "", + "process_label": "", + "rlimits": [ + { + "type": 7, + "hard": 1024, + "soft": 1024 + } + ], + "additional_groups": null, + "uid_mappings": [ + { + "container_id": 0, + "host_id": 1000, + "size": 1 + }, + { + "container_id": 1, + "host_id": 1, + "size": 999 + }, + { + "container_id": 1001, + "host_id": 1001, + "size": 2147482647 + } + ], + "gid_mappings": [ + { + "container_id": 0, + "host_id": 1000, + "size": 1 + }, + { + "container_id": 1, + "host_id": 1, + "size": 999 + }, + { + "container_id": 1001, + "host_id": 1001, + "size": 2147482647 + } + ], + "mask_paths": [ + "/proc/kcore" + ], + "readonly_paths": [ + "/proc/sys", + "/proc/sysrq-trigger", + "/proc/irq", + "/proc/bus" + ] +} diff --git a/vendor/src/github.com/docker/libcontainer/security/capabilities/capabilities.go b/vendor/src/github.com/docker/libcontainer/security/capabilities/capabilities.go deleted file mode 100644 index 7aef5fa67f..0000000000 --- a/vendor/src/github.com/docker/libcontainer/security/capabilities/capabilities.go +++ /dev/null @@ -1,56 +0,0 @@ -package capabilities - -import ( - "os" - - "github.com/syndtr/gocapability/capability" -) - -const allCapabilityTypes = capability.CAPS | capability.BOUNDS - -// DropBoundingSet drops the capability bounding set to those specified in the -// container configuration. -func DropBoundingSet(capabilities []string) error { - c, err := capability.NewPid(os.Getpid()) - if err != nil { - return err - } - - keep := getEnabledCapabilities(capabilities) - c.Clear(capability.BOUNDS) - c.Set(capability.BOUNDS, keep...) - - if err := c.Apply(capability.BOUNDS); err != nil { - return err - } - - return nil -} - -// DropCapabilities drops all capabilities for the current process except those specified in the container configuration. -func DropCapabilities(capList []string) error { - c, err := capability.NewPid(os.Getpid()) - if err != nil { - return err - } - - keep := getEnabledCapabilities(capList) - c.Clear(allCapabilityTypes) - c.Set(allCapabilityTypes, keep...) - - if err := c.Apply(allCapabilityTypes); err != nil { - return err - } - return nil -} - -// getEnabledCapabilities returns the capabilities that should not be dropped by the container. -func getEnabledCapabilities(capList []string) []capability.Cap { - keep := []capability.Cap{} - for _, capability := range capList { - if c := GetCapability(capability); c != nil { - keep = append(keep, c.Value) - } - } - return keep -} diff --git a/vendor/src/github.com/docker/libcontainer/security/capabilities/types.go b/vendor/src/github.com/docker/libcontainer/security/capabilities/types.go deleted file mode 100644 index a960b804c6..0000000000 --- a/vendor/src/github.com/docker/libcontainer/security/capabilities/types.go +++ /dev/null @@ -1,88 +0,0 @@ -package capabilities - -import "github.com/syndtr/gocapability/capability" - -type ( - CapabilityMapping struct { - Key string `json:"key,omitempty"` - Value capability.Cap `json:"value,omitempty"` - } - Capabilities []*CapabilityMapping -) - -func (c *CapabilityMapping) String() string { - return c.Key -} - -func GetCapability(key string) *CapabilityMapping { - for _, capp := range capabilityList { - if capp.Key == key { - cpy := *capp - return &cpy - } - } - return nil -} - -func GetAllCapabilities() []string { - output := make([]string, len(capabilityList)) - for i, capability := range capabilityList { - output[i] = capability.String() - } - return output -} - -// Contains returns true if the specified Capability is -// in the slice -func (c Capabilities) contains(capp string) bool { - return c.get(capp) != nil -} - -func (c Capabilities) get(capp string) *CapabilityMapping { - for _, cap := range c { - if cap.Key == capp { - return cap - } - } - return nil -} - -var capabilityList = Capabilities{ - {Key: "SETPCAP", Value: capability.CAP_SETPCAP}, - {Key: "SYS_MODULE", Value: capability.CAP_SYS_MODULE}, - {Key: "SYS_RAWIO", Value: capability.CAP_SYS_RAWIO}, - {Key: "SYS_PACCT", Value: capability.CAP_SYS_PACCT}, - {Key: "SYS_ADMIN", Value: capability.CAP_SYS_ADMIN}, - {Key: "SYS_NICE", Value: capability.CAP_SYS_NICE}, - {Key: "SYS_RESOURCE", Value: capability.CAP_SYS_RESOURCE}, - {Key: "SYS_TIME", Value: capability.CAP_SYS_TIME}, - {Key: "SYS_TTY_CONFIG", Value: capability.CAP_SYS_TTY_CONFIG}, - {Key: "MKNOD", Value: capability.CAP_MKNOD}, - {Key: "AUDIT_WRITE", Value: capability.CAP_AUDIT_WRITE}, - {Key: "AUDIT_CONTROL", Value: capability.CAP_AUDIT_CONTROL}, - {Key: "MAC_OVERRIDE", Value: capability.CAP_MAC_OVERRIDE}, - {Key: "MAC_ADMIN", Value: capability.CAP_MAC_ADMIN}, - {Key: "NET_ADMIN", Value: capability.CAP_NET_ADMIN}, - {Key: "SYSLOG", Value: capability.CAP_SYSLOG}, - {Key: "CHOWN", Value: capability.CAP_CHOWN}, - {Key: "NET_RAW", Value: capability.CAP_NET_RAW}, - {Key: "DAC_OVERRIDE", Value: capability.CAP_DAC_OVERRIDE}, - {Key: "FOWNER", Value: capability.CAP_FOWNER}, - {Key: "DAC_READ_SEARCH", Value: capability.CAP_DAC_READ_SEARCH}, - {Key: "FSETID", Value: capability.CAP_FSETID}, - {Key: "KILL", Value: capability.CAP_KILL}, - {Key: "SETGID", Value: capability.CAP_SETGID}, - {Key: "SETUID", Value: capability.CAP_SETUID}, - {Key: "LINUX_IMMUTABLE", Value: capability.CAP_LINUX_IMMUTABLE}, - {Key: "NET_BIND_SERVICE", Value: capability.CAP_NET_BIND_SERVICE}, - {Key: "NET_BROADCAST", Value: capability.CAP_NET_BROADCAST}, - {Key: "IPC_LOCK", Value: capability.CAP_IPC_LOCK}, - {Key: "IPC_OWNER", Value: capability.CAP_IPC_OWNER}, - {Key: "SYS_CHROOT", Value: capability.CAP_SYS_CHROOT}, - {Key: "SYS_PTRACE", Value: capability.CAP_SYS_PTRACE}, - {Key: "SYS_BOOT", Value: capability.CAP_SYS_BOOT}, - {Key: "LEASE", Value: capability.CAP_LEASE}, - {Key: "SETFCAP", Value: capability.CAP_SETFCAP}, - {Key: "WAKE_ALARM", Value: capability.CAP_WAKE_ALARM}, - {Key: "BLOCK_SUSPEND", Value: capability.CAP_BLOCK_SUSPEND}, -} diff --git a/vendor/src/github.com/docker/libcontainer/security/capabilities/types_test.go b/vendor/src/github.com/docker/libcontainer/security/capabilities/types_test.go deleted file mode 100644 index 06e8a2b01c..0000000000 --- a/vendor/src/github.com/docker/libcontainer/security/capabilities/types_test.go +++ /dev/null @@ -1,19 +0,0 @@ -package capabilities - -import ( - "testing" -) - -func TestCapabilitiesContains(t *testing.T) { - caps := Capabilities{ - GetCapability("MKNOD"), - GetCapability("SETPCAP"), - } - - if caps.contains("SYS_ADMIN") { - t.Fatal("capabilities should not contain SYS_ADMIN") - } - if !caps.contains("MKNOD") { - t.Fatal("capabilities should contain MKNOD but does not") - } -} diff --git a/vendor/src/github.com/docker/libcontainer/security/restrict/restrict.go b/vendor/src/github.com/docker/libcontainer/security/restrict/restrict.go deleted file mode 100644 index dd765b1f1b..0000000000 --- a/vendor/src/github.com/docker/libcontainer/security/restrict/restrict.go +++ /dev/null @@ -1,53 +0,0 @@ -// +build linux - -package restrict - -import ( - "fmt" - "os" - "syscall" - "time" -) - -const defaultMountFlags = syscall.MS_NOEXEC | syscall.MS_NOSUID | syscall.MS_NODEV - -func mountReadonly(path string) error { - for i := 0; i < 5; i++ { - if err := syscall.Mount("", path, "", syscall.MS_REMOUNT|syscall.MS_RDONLY, ""); err != nil && !os.IsNotExist(err) { - switch err { - case syscall.EINVAL: - // Probably not a mountpoint, use bind-mount - if err := syscall.Mount(path, path, "", syscall.MS_BIND, ""); err != nil { - return err - } - - return syscall.Mount(path, path, "", syscall.MS_BIND|syscall.MS_REMOUNT|syscall.MS_RDONLY|syscall.MS_REC|defaultMountFlags, "") - case syscall.EBUSY: - time.Sleep(100 * time.Millisecond) - continue - default: - return err - } - } - - return nil - } - - return fmt.Errorf("unable to mount %s as readonly max retries reached", path) -} - -// This has to be called while the container still has CAP_SYS_ADMIN (to be able to perform mounts). -// However, afterwards, CAP_SYS_ADMIN should be dropped (otherwise the user will be able to revert those changes). -func Restrict(mounts ...string) error { - for _, dest := range mounts { - if err := mountReadonly(dest); err != nil { - return fmt.Errorf("unable to remount %s readonly: %s", dest, err) - } - } - - if err := syscall.Mount("/dev/null", "/proc/kcore", "", syscall.MS_BIND, ""); err != nil && !os.IsNotExist(err) { - return fmt.Errorf("unable to bind-mount /dev/null over /proc/kcore: %s", err) - } - - return nil -} diff --git a/vendor/src/github.com/docker/libcontainer/security/restrict/unsupported.go b/vendor/src/github.com/docker/libcontainer/security/restrict/unsupported.go deleted file mode 100644 index 464e8d498d..0000000000 --- a/vendor/src/github.com/docker/libcontainer/security/restrict/unsupported.go +++ /dev/null @@ -1,9 +0,0 @@ -// +build !linux - -package restrict - -import "fmt" - -func Restrict() error { - return fmt.Errorf("not supported") -} diff --git a/vendor/src/github.com/docker/libcontainer/setns_init_linux.go b/vendor/src/github.com/docker/libcontainer/setns_init_linux.go new file mode 100644 index 0000000000..f77219d27a --- /dev/null +++ b/vendor/src/github.com/docker/libcontainer/setns_init_linux.go @@ -0,0 +1,35 @@ +// +build linux + +package libcontainer + +import ( + "os" + + "github.com/docker/libcontainer/apparmor" + "github.com/docker/libcontainer/label" + "github.com/docker/libcontainer/system" +) + +// linuxSetnsInit performs the container's initialization for running a new process +// inside an existing container. +type linuxSetnsInit struct { + config *initConfig +} + +func (l *linuxSetnsInit) Init() error { + if err := setupRlimits(l.config.Config); err != nil { + return err + } + if err := finalizeNamespace(l.config); err != nil { + return err + } + if err := apparmor.ApplyProfile(l.config.Config.AppArmorProfile); err != nil { + return err + } + if l.config.Config.ProcessLabel != "" { + if err := label.SetProcessLabel(l.config.Config.ProcessLabel); err != nil { + return err + } + } + return system.Execv(l.config.Args[0], l.config.Args[0:], os.Environ()) +} diff --git a/vendor/src/github.com/docker/libcontainer/stacktrace/capture.go b/vendor/src/github.com/docker/libcontainer/stacktrace/capture.go new file mode 100644 index 0000000000..15b3482ccc --- /dev/null +++ b/vendor/src/github.com/docker/libcontainer/stacktrace/capture.go @@ -0,0 +1,23 @@ +package stacktrace + +import "runtime" + +// Caputure captures a stacktrace for the current calling go program +// +// skip is the number of frames to skip +func Capture(userSkip int) Stacktrace { + var ( + skip = userSkip + 1 // add one for our own function + frames []Frame + ) + for i := skip; ; i++ { + pc, file, line, ok := runtime.Caller(i) + if !ok { + break + } + frames = append(frames, NewFrame(pc, file, line)) + } + return Stacktrace{ + Frames: frames, + } +} diff --git a/vendor/src/github.com/docker/libcontainer/stacktrace/capture_test.go b/vendor/src/github.com/docker/libcontainer/stacktrace/capture_test.go new file mode 100644 index 0000000000..3f435d51a6 --- /dev/null +++ b/vendor/src/github.com/docker/libcontainer/stacktrace/capture_test.go @@ -0,0 +1,27 @@ +package stacktrace + +import "testing" + +func captureFunc() Stacktrace { + return Capture(0) +} + +func TestCaptureTestFunc(t *testing.T) { + stack := captureFunc() + + if len(stack.Frames) == 0 { + t.Fatal("expected stack frames to be returned") + } + + // the first frame is the caller + frame := stack.Frames[0] + if expected := "captureFunc"; frame.Function != expected { + t.Fatalf("expteced function %q but recevied %q", expected, frame.Function) + } + if expected := "github.com/docker/libcontainer/stacktrace"; frame.Package != expected { + t.Fatalf("expected package %q but received %q", expected, frame.Package) + } + if expected := "capture_test.go"; frame.File != expected { + t.Fatalf("expected file %q but received %q", expected, frame.File) + } +} diff --git a/vendor/src/github.com/docker/libcontainer/stacktrace/frame.go b/vendor/src/github.com/docker/libcontainer/stacktrace/frame.go new file mode 100644 index 0000000000..5edea1b751 --- /dev/null +++ b/vendor/src/github.com/docker/libcontainer/stacktrace/frame.go @@ -0,0 +1,35 @@ +package stacktrace + +import ( + "path/filepath" + "runtime" + "strings" +) + +// NewFrame returns a new stack frame for the provided information +func NewFrame(pc uintptr, file string, line int) Frame { + fn := runtime.FuncForPC(pc) + pack, name := parseFunctionName(fn.Name()) + return Frame{ + Line: line, + File: filepath.Base(file), + Package: pack, + Function: name, + } +} + +func parseFunctionName(name string) (string, string) { + i := strings.LastIndex(name, ".") + if i == -1 { + return "", name + } + return name[:i], name[i+1:] +} + +// Frame contains all the information for a stack frame within a go program +type Frame struct { + File string + Function string + Package string + Line int +} diff --git a/vendor/src/github.com/docker/libcontainer/stacktrace/frame_test.go b/vendor/src/github.com/docker/libcontainer/stacktrace/frame_test.go new file mode 100644 index 0000000000..ae95ec4847 --- /dev/null +++ b/vendor/src/github.com/docker/libcontainer/stacktrace/frame_test.go @@ -0,0 +1,20 @@ +package stacktrace + +import "testing" + +func TestParsePackageName(t *testing.T) { + var ( + name = "github.com/docker/libcontainer/stacktrace.captureFunc" + expectedPackage = "github.com/docker/libcontainer/stacktrace" + expectedFunction = "captureFunc" + ) + + pack, funcName := parseFunctionName(name) + if pack != expectedPackage { + t.Fatalf("expected package %q but received %q", expectedPackage, pack) + } + + if funcName != expectedFunction { + t.Fatalf("expected function %q but received %q", expectedFunction, funcName) + } +} diff --git a/vendor/src/github.com/docker/libcontainer/stacktrace/stacktrace.go b/vendor/src/github.com/docker/libcontainer/stacktrace/stacktrace.go new file mode 100644 index 0000000000..5e8b58d2d2 --- /dev/null +++ b/vendor/src/github.com/docker/libcontainer/stacktrace/stacktrace.go @@ -0,0 +1,5 @@ +package stacktrace + +type Stacktrace struct { + Frames []Frame +} diff --git a/vendor/src/github.com/docker/libcontainer/standard_init_linux.go b/vendor/src/github.com/docker/libcontainer/standard_init_linux.go new file mode 100644 index 0000000000..29619d3cdc --- /dev/null +++ b/vendor/src/github.com/docker/libcontainer/standard_init_linux.go @@ -0,0 +1,94 @@ +// +build linux + +package libcontainer + +import ( + "os" + "syscall" + + "github.com/docker/libcontainer/apparmor" + "github.com/docker/libcontainer/configs" + "github.com/docker/libcontainer/label" + "github.com/docker/libcontainer/system" +) + +type linuxStandardInit struct { + config *initConfig +} + +func (l *linuxStandardInit) Init() error { + // join any namespaces via a path to the namespace fd if provided + if err := joinExistingNamespaces(l.config.Config.Namespaces); err != nil { + return err + } + var console *linuxConsole + if l.config.Console != "" { + console = newConsoleFromPath(l.config.Console) + if err := console.dupStdio(); err != nil { + return err + } + } + if _, err := syscall.Setsid(); err != nil { + return err + } + if console != nil { + if err := system.Setctty(); err != nil { + return err + } + } + if err := setupNetwork(l.config); err != nil { + return err + } + if err := setupRoute(l.config.Config); err != nil { + return err + } + if err := setupRlimits(l.config.Config); err != nil { + return err + } + label.Init() + // InitializeMountNamespace() can be executed only for a new mount namespace + if l.config.Config.Namespaces.Contains(configs.NEWNS) { + if err := setupRootfs(l.config.Config, console); err != nil { + return err + } + } + if hostname := l.config.Config.Hostname; hostname != "" { + if err := syscall.Sethostname([]byte(hostname)); err != nil { + return err + } + } + if err := apparmor.ApplyProfile(l.config.Config.AppArmorProfile); err != nil { + return err + } + if err := label.SetProcessLabel(l.config.Config.ProcessLabel); err != nil { + return err + } + for _, path := range l.config.Config.ReadonlyPaths { + if err := remountReadonly(path); err != nil { + return err + } + } + for _, path := range l.config.Config.MaskPaths { + if err := maskFile(path); err != nil { + return err + } + } + pdeath, err := system.GetParentDeathSignal() + if err != nil { + return err + } + if err := finalizeNamespace(l.config); err != nil { + return err + } + // finalizeNamespace can change user/group which clears the parent death + // signal, so we restore it here. + if err := pdeath.Restore(); err != nil { + return err + } + // Signal self if parent is already dead. Does nothing if running in a new + // PID namespace, as Getppid will always return 0. + if syscall.Getppid() == 1 { + return syscall.Kill(syscall.Getpid(), syscall.SIGKILL) + } + return system.Execv(l.config.Args[0], l.config.Args[0:], os.Environ()) +} diff --git a/vendor/src/github.com/docker/libcontainer/state.go b/vendor/src/github.com/docker/libcontainer/state.go deleted file mode 100644 index 208b4c6276..0000000000 --- a/vendor/src/github.com/docker/libcontainer/state.go +++ /dev/null @@ -1,77 +0,0 @@ -package libcontainer - -import ( - "encoding/json" - "os" - "path/filepath" - - "github.com/docker/libcontainer/network" -) - -// State represents a running container's state -type State struct { - // InitPid is the init process id in the parent namespace - InitPid int `json:"init_pid,omitempty"` - - // InitStartTime is the init process start time - InitStartTime string `json:"init_start_time,omitempty"` - - // Network runtime state. - NetworkState network.NetworkState `json:"network_state,omitempty"` - - // Path to all the cgroups setup for a container. Key is cgroup subsystem name. - CgroupPaths map[string]string `json:"cgroup_paths,omitempty"` -} - -// The running state of the container. -type RunState int - -const ( - // The name of the runtime state file - stateFile = "state.json" - - // The container exists and is running. - Running RunState = iota - - // The container exists, it is in the process of being paused. - Pausing - - // The container exists, but all its processes are paused. - Paused - - // The container does not exist. - Destroyed -) - -// SaveState writes the container's runtime state to a state.json file -// in the specified path -func SaveState(basePath string, state *State) error { - f, err := os.Create(filepath.Join(basePath, stateFile)) - if err != nil { - return err - } - defer f.Close() - - return json.NewEncoder(f).Encode(state) -} - -// GetState reads the state.json file for a running container -func GetState(basePath string) (*State, error) { - f, err := os.Open(filepath.Join(basePath, stateFile)) - if err != nil { - return nil, err - } - defer f.Close() - - var state *State - if err := json.NewDecoder(f).Decode(&state); err != nil { - return nil, err - } - - return state, nil -} - -// DeleteState deletes the state.json file -func DeleteState(basePath string) error { - return os.Remove(filepath.Join(basePath, stateFile)) -} diff --git a/vendor/src/github.com/docker/libcontainer/stats.go b/vendor/src/github.com/docker/libcontainer/stats.go new file mode 100644 index 0000000000..ba72a6fde9 --- /dev/null +++ b/vendor/src/github.com/docker/libcontainer/stats.go @@ -0,0 +1,22 @@ +package libcontainer + +import "github.com/docker/libcontainer/cgroups" + +type Stats struct { + Interfaces []*NetworkInterface + CgroupStats *cgroups.Stats +} + +type NetworkInterface struct { + // Name is the name of the network interface. + Name string + + RxBytes uint64 + RxPackets uint64 + RxErrors uint64 + RxDropped uint64 + TxBytes uint64 + TxPackets uint64 + TxErrors uint64 + TxDropped uint64 +} diff --git a/vendor/src/github.com/docker/libcontainer/system/linux.go b/vendor/src/github.com/docker/libcontainer/system/linux.go index c07ef1532d..2cc3ef803a 100644 --- a/vendor/src/github.com/docker/libcontainer/system/linux.go +++ b/vendor/src/github.com/docker/libcontainer/system/linux.go @@ -8,6 +8,26 @@ import ( "unsafe" ) +type ParentDeathSignal int + +func (p ParentDeathSignal) Restore() error { + if p == 0 { + return nil + } + current, err := GetParentDeathSignal() + if err != nil { + return err + } + if p == current { + return nil + } + return p.Set() +} + +func (p ParentDeathSignal) Set() error { + return SetParentDeathSignal(uintptr(p)) +} + func Execv(cmd string, args []string, env []string) error { name, err := exec.LookPath(cmd) if err != nil { @@ -17,23 +37,20 @@ func Execv(cmd string, args []string, env []string) error { return syscall.Exec(name, args, env) } -func ParentDeathSignal(sig uintptr) error { +func SetParentDeathSignal(sig uintptr) error { if _, _, err := syscall.RawSyscall(syscall.SYS_PRCTL, syscall.PR_SET_PDEATHSIG, sig, 0); err != 0 { return err } return nil } -func GetParentDeathSignal() (int, error) { +func GetParentDeathSignal() (ParentDeathSignal, error) { var sig int - _, _, err := syscall.RawSyscall(syscall.SYS_PRCTL, syscall.PR_GET_PDEATHSIG, uintptr(unsafe.Pointer(&sig)), 0) - if err != 0 { return -1, err } - - return sig, nil + return ParentDeathSignal(sig), nil } func SetKeepCaps() error { diff --git a/vendor/src/github.com/docker/libcontainer/types.go b/vendor/src/github.com/docker/libcontainer/types.go deleted file mode 100644 index c341137ec8..0000000000 --- a/vendor/src/github.com/docker/libcontainer/types.go +++ /dev/null @@ -1,11 +0,0 @@ -package libcontainer - -import ( - "github.com/docker/libcontainer/cgroups" - "github.com/docker/libcontainer/network" -) - -type ContainerStats struct { - NetworkStats *network.NetworkStats `json:"network_stats,omitempty"` - CgroupStats *cgroups.Stats `json:"cgroup_stats,omitempty"` -} diff --git a/vendor/src/github.com/docker/libcontainer/update-vendor.sh b/vendor/src/github.com/docker/libcontainer/update-vendor.sh index df66a0a8d5..c48fb5f0ba 100755 --- a/vendor/src/github.com/docker/libcontainer/update-vendor.sh +++ b/vendor/src/github.com/docker/libcontainer/update-vendor.sh @@ -42,7 +42,8 @@ clone() { # the following lines are in sorted order, FYI clone git github.com/codegangsta/cli 1.1.0 clone git github.com/coreos/go-systemd v2 -clone git github.com/godbus/dbus v1 -clone git github.com/syndtr/gocapability 3c85049eae +clone git github.com/godbus/dbus v2 +clone git github.com/Sirupsen/logrus v0.6.0 +clone git github.com/syndtr/gocapability e55e583369 # intentionally not vendoring Docker itself... that'd be a circle :) diff --git a/vendor/src/github.com/docker/libcontainer/utils/utils.go b/vendor/src/github.com/docker/libcontainer/utils/utils.go index 76184ce00b..094bce5300 100644 --- a/vendor/src/github.com/docker/libcontainer/utils/utils.go +++ b/vendor/src/github.com/docker/libcontainer/utils/utils.go @@ -10,6 +10,10 @@ import ( "syscall" ) +const ( + exitSignalOffset = 128 +) + // GenerateRandomName returns a new name joined with a prefix. This size // specified is used to truncate the randomly generated value func GenerateRandomName(prefix string, size int) (string, error) { @@ -53,3 +57,12 @@ func CloseExecFrom(minFd int) error { } return nil } + +// ExitStatus returns the correct exit status for a process based on if it +// was signaled or existed cleanly. +func ExitStatus(status syscall.WaitStatus) int { + if status.Signaled() { + return exitSignalOffset + int(status.Signal()) + } + return status.ExitStatus() +} diff --git a/vendor/src/github.com/godbus/dbus/CONTRIBUTING.md b/vendor/src/github.com/godbus/dbus/CONTRIBUTING.md new file mode 100644 index 0000000000..c88f9b2bdd --- /dev/null +++ b/vendor/src/github.com/godbus/dbus/CONTRIBUTING.md @@ -0,0 +1,50 @@ +# How to Contribute + +## Getting Started + +- Fork the repository on GitHub +- Read the [README](README.markdown) for build and test instructions +- Play with the project, submit bugs, submit patches! + +## Contribution Flow + +This is a rough outline of what a contributor's workflow looks like: + +- Create a topic branch from where you want to base your work (usually master). +- Make commits of logical units. +- Make sure your commit messages are in the proper format (see below). +- Push your changes to a topic branch in your fork of the repository. +- Make sure the tests pass, and add any new tests as appropriate. +- Submit a pull request to the original repository. + +Thanks for your contributions! + +### Format of the Commit Message + +We follow a rough convention for commit messages that is designed to answer two +questions: what changed and why. The subject line should feature the what and +the body of the commit should describe the why. + +``` +scripts: add the test-cluster command + +this uses tmux to setup a test cluster that you can easily kill and +start for debugging. + +Fixes #38 +``` + +The format can be described more formally as follows: + +``` +: + + + +