summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorunclejack <unclejack@users.noreply.github.com>2014-03-26 00:00:37 +0200
committerunclejack <unclejack@users.noreply.github.com>2014-03-26 00:00:37 +0200
commit867b2a90c228f62cdcd44907ceef279a2d8f1ac5 (patch)
treea41c506d3adefe00861f9e38155f5b21e1692ab4
parent143c9707a9fafc39e1d9747f528db97b2564f01e (diff)
parent3600720a36929b1a51a227699a337cc593e2534d (diff)
downloaddocker-release-0.9.tar.gz
Merge pull request #4831 from unclejack/final_bump_v0.9.1v0.9.1release-0.9hotfix-0.9.2
Bump to version 0.9.1
-rw-r--r--CHANGELOG.md40
-rw-r--r--VERSION2
-rw-r--r--api/client.go59
-rw-r--r--api/server.go7
-rw-r--r--container.go10
-rwxr-xr-xcontrib/init/sysvinit-debian/docker41
-rw-r--r--contrib/init/upstart/docker.conf33
-rw-r--r--docs/sources/articles/security.rst4
-rw-r--r--docs/sources/reference/commandline/cli.rst61
-rw-r--r--execdriver/driver.go10
-rw-r--r--execdriver/lxc/driver.go13
-rw-r--r--execdriver/lxc/info.go2
-rw-r--r--execdriver/lxc/lxc_template.go6
-rw-r--r--execdriver/lxc/lxc_template_unit_test.go8
-rw-r--r--execdriver/native/default_template.go33
-rw-r--r--graphdriver/devmapper/deviceset.go57
-rw-r--r--graphdriver/devmapper/driver.go7
-rwxr-xr-xhack/make.sh21
-rwxr-xr-x[-rw-r--r--]hack/make/binary4
-rw-r--r--hack/make/dynbinary6
-rw-r--r--hack/make/tgz2
-rwxr-xr-xhack/release.sh192
-rw-r--r--integration/commands_test.go31
-rw-r--r--integration/container_test.go69
-rw-r--r--integration/server_test.go57
-rw-r--r--pkg/libcontainer/apparmor/setup.go6
-rw-r--r--pkg/libcontainer/network/loopback.go24
-rw-r--r--pkg/libcontainer/network/strategy.go3
-rw-r--r--pkg/libcontainer/network/veth.go6
-rw-r--r--pkg/libcontainer/nsinit/execin.go9
-rw-r--r--pkg/libcontainer/nsinit/init.go10
-rw-r--r--pkg/libcontainer/nsinit/mount.go12
-rw-r--r--pkg/listenbuffer/buffer.go27
-rw-r--r--pkg/mflag/example/example.go3
-rw-r--r--pkg/mflag/flag.go15
-rw-r--r--pkg/signal/signal.go19
-rw-r--r--pkg/signal/signal_darwin.go40
-rw-r--r--pkg/signal/signal_freebsd.go44
-rw-r--r--pkg/signal/signal_linux.go43
-rw-r--r--pkg/signal/signal_unsupported.go9
-rw-r--r--pkg/system/calls_linux.go4
-rw-r--r--pkg/term/termios_darwin.go39
-rw-r--r--server.go116
-rw-r--r--utils/jsonmessage.go2
-rw-r--r--utils/signal.go11
-rw-r--r--utils/signal_darwin.go44
-rw-r--r--utils/signal_linux.go47
47 files changed, 901 insertions, 407 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 40ba3d32ac..c8ea94361b 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,45 @@
# Changelog
+## 0.9.1 (2014-03-24)
+
+#### Builder
+- Fix printing multiple messages on a single line. Fixes broken output during builds.
+
+#### Documentation
+- Fix external link on security of containers.
+
+#### Contrib
+- Fix init script cgroup mounting workarounds to be more similar to cgroupfs-mount and thus work properly.
+- Add variable for DOCKER_LOGFILE to sysvinit and use append instead of overwrite in opening the logfile.
+
+#### Hack
+- Generate md5 and sha256 hashes when building, and upload them via hack/release.sh.
+
+#### Remote API
+- Fix content-type detection in `docker cp`.
+
+#### Runtime
+- Use BSD raw mode on Darwin. Fixes nano, tmux and others.
+- Only unshare the mount namespace for execin.
+- Retry to retrieve the layer metadata up to 5 times for `docker pull`.
+- Merge existing config when committing.
+- Fix panic in monitor.
+- Disable daemon startup timeout.
+- Fix issue #4681: add loopback interface when networking is disabled.
+- Add failing test case for issue #4681.
+- Send SIGTERM to child, instead of SIGKILL.
+- Show the driver and the kernel version in `docker info` even when not in debug mode.
+- Always symlink /dev/ptmx for libcontainer. This fixes console related problems.
+- Fix issue caused by the absence of /etc/apparmor.d.
+- Don't leave empty cidFile behind when failing to create the container.
+- Improve deprecation message.
+- Fix attach exit on darwin.
+- devicemapper: improve handling of devicemapper devices (add per device lock, increase sleep time, unlock while sleeping).
+- devicemapper: succeed immediately when removing non-existing devices.
+- devicemapper: increase timeout in waitClose to 10 seconds.
+- Remove goroutine leak on error.
+- Update parseLxcInfo to comply with new lxc1.0 format.
+
## 0.9.0 (2014-03-10)
#### Builder
diff --git a/VERSION b/VERSION
index ac39a106c4..f374f6662e 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-0.9.0
+0.9.1
diff --git a/api/client.go b/api/client.go
index 10075ae613..d7d126ec71 100644
--- a/api/client.go
+++ b/api/client.go
@@ -13,6 +13,7 @@ import (
"github.com/dotcloud/docker/engine"
"github.com/dotcloud/docker/nat"
flag "github.com/dotcloud/docker/pkg/mflag"
+ "github.com/dotcloud/docker/pkg/signal"
"github.com/dotcloud/docker/pkg/term"
"github.com/dotcloud/docker/registry"
"github.com/dotcloud/docker/runconfig"
@@ -24,11 +25,11 @@ import (
"net/http/httputil"
"net/url"
"os"
- "os/signal"
+ gosignal "os/signal"
"path"
"reflect"
"regexp"
- "runtime"
+ goruntime "runtime"
"strconv"
"strings"
"syscall"
@@ -367,7 +368,7 @@ func (cli *DockerCli) CmdVersion(args ...string) error {
if dockerversion.VERSION != "" {
fmt.Fprintf(cli.out, "Client version: %s\n", dockerversion.VERSION)
}
- fmt.Fprintf(cli.out, "Go version (client): %s\n", runtime.Version())
+ fmt.Fprintf(cli.out, "Go version (client): %s\n", goruntime.Version())
if dockerversion.GITCOMMIT != "" {
fmt.Fprintf(cli.out, "Git commit (client): %s\n", dockerversion.GITCOMMIT)
}
@@ -432,7 +433,7 @@ func (cli *DockerCli) CmdInfo(args ...string) error {
fmt.Fprintf(cli.out, "Containers: %d\n", remoteInfo.GetInt("Containers"))
fmt.Fprintf(cli.out, "Images: %d\n", remoteInfo.GetInt("Images"))
- fmt.Fprintf(cli.out, "Driver: %s\n", remoteInfo.Get("Driver"))
+ fmt.Fprintf(cli.out, "Storage Driver: %s\n", remoteInfo.Get("Driver"))
var driverStatus [][2]string
if err := remoteInfo.GetJson("DriverStatus", &driverStatus); err != nil {
return err
@@ -440,14 +441,15 @@ func (cli *DockerCli) CmdInfo(args ...string) error {
for _, pair := range driverStatus {
fmt.Fprintf(cli.out, " %s: %s\n", pair[0], pair[1])
}
+ fmt.Fprintf(cli.out, "Execution Driver: %s\n", remoteInfo.Get("ExecutionDriver"))
+ fmt.Fprintf(cli.out, "Kernel Version: %s\n", remoteInfo.Get("KernelVersion"))
+
if remoteInfo.GetBool("Debug") || os.Getenv("DEBUG") != "" {
fmt.Fprintf(cli.out, "Debug mode (server): %v\n", remoteInfo.GetBool("Debug"))
fmt.Fprintf(cli.out, "Debug mode (client): %v\n", os.Getenv("DEBUG") != "")
fmt.Fprintf(cli.out, "Fds: %d\n", remoteInfo.GetInt("NFd"))
fmt.Fprintf(cli.out, "Goroutines: %d\n", remoteInfo.GetInt("NGoroutines"))
- fmt.Fprintf(cli.out, "Execution Driver: %s\n", remoteInfo.Get("ExecutionDriver"))
fmt.Fprintf(cli.out, "EventsListeners: %d\n", remoteInfo.GetInt("NEventsListener"))
- fmt.Fprintf(cli.out, "Kernel Version: %s\n", remoteInfo.Get("KernelVersion"))
if initSha1 := remoteInfo.Get("InitSha1"); initSha1 != "" {
fmt.Fprintf(cli.out, "Init SHA1: %s\n", initSha1)
@@ -533,13 +535,23 @@ func (cli *DockerCli) CmdRestart(args ...string) error {
func (cli *DockerCli) forwardAllSignals(cid string) chan os.Signal {
sigc := make(chan os.Signal, 1)
- utils.CatchAll(sigc)
+ signal.CatchAll(sigc)
go func() {
for s := range sigc {
if s == syscall.SIGCHLD {
continue
}
- if _, _, err := readBody(cli.call("POST", fmt.Sprintf("/containers/%s/kill?signal=%d", cid, s), nil, false)); err != nil {
+ var sig string
+ for sigStr, sigN := range signal.SignalMap {
+ if sigN == s {
+ sig = sigStr
+ break
+ }
+ }
+ if sig == "" {
+ utils.Errorf("Unsupported signal: %d. Discarding.", s)
+ }
+ if _, _, err := readBody(cli.call("POST", fmt.Sprintf("/containers/%s/kill?signal=%s", cid, sig), nil, false)); err != nil {
utils.Debugf("Error sending signal: %s", err)
}
}
@@ -581,7 +593,7 @@ func (cli *DockerCli) CmdStart(args ...string) error {
if !container.Config.Tty {
sigc := cli.forwardAllSignals(cmd.Arg(0))
- defer utils.StopCatch(sigc)
+ defer signal.StopCatch(sigc)
}
var in io.ReadCloser
@@ -1614,7 +1626,7 @@ func (cli *DockerCli) CmdAttach(args ...string) error {
if *proxy && !container.Config.Tty {
sigc := cli.forwardAllSignals(cmd.Arg(0))
- defer utils.StopCatch(sigc)
+ defer signal.StopCatch(sigc)
}
if err := cli.hijack("POST", "/containers/"+cmd.Arg(0)+"/attach?"+v.Encode(), container.Config.Tty, in, cli.out, cli.err, nil); err != nil {
@@ -1753,7 +1765,21 @@ func (cli *DockerCli) CmdRun(args ...string) error {
if containerIDFile, err = os.Create(hostConfig.ContainerIDFile); err != nil {
return fmt.Errorf("Failed to create the container ID file: %s", err)
}
- defer containerIDFile.Close()
+ defer func() {
+ containerIDFile.Close()
+ var (
+ cidFileInfo os.FileInfo
+ err error
+ )
+ if cidFileInfo, err = os.Stat(hostConfig.ContainerIDFile); err != nil {
+ return
+ }
+ if cidFileInfo.Size() == 0 {
+ if err := os.Remove(hostConfig.ContainerIDFile); err != nil {
+ fmt.Printf("failed to remove CID file '%s': %s \n", hostConfig.ContainerIDFile, err)
+ }
+ }
+ }()
}
containerValues := url.Values{}
@@ -1818,7 +1844,7 @@ func (cli *DockerCli) CmdRun(args ...string) error {
if sigProxy {
sigc := cli.forwardAllSignals(runResult.Get("Id"))
- defer utils.StopCatch(sigc)
+ defer signal.StopCatch(sigc)
}
var (
@@ -2239,7 +2265,12 @@ func (cli *DockerCli) hijack(method, path string, setRawTerminal bool, in io.Rea
if setRawTerminal && cli.isTerminal {
term.RestoreTerminal(cli.terminalFd, oldState)
}
- in.Close()
+ // For some reason this Close call blocks on darwin..
+ // As the client exists right after, simply discard the close
+ // until we find a better solution.
+ if goruntime.GOOS != "darwin" {
+ in.Close()
+ }
}
}()
@@ -2320,7 +2351,7 @@ func (cli *DockerCli) monitorTtySize(id string) error {
cli.resizeTty(id)
sigchan := make(chan os.Signal, 1)
- signal.Notify(sigchan, syscall.SIGWINCH)
+ gosignal.Notify(sigchan, syscall.SIGWINCH)
go func() {
for _ = range sigchan {
cli.resizeTty(id)
diff --git a/api/server.go b/api/server.go
index 6fafe60f9f..771f3c6cd3 100644
--- a/api/server.go
+++ b/api/server.go
@@ -26,7 +26,6 @@ import (
"strconv"
"strings"
"syscall"
- "time"
)
var (
@@ -883,7 +882,7 @@ func postContainersCopy(eng *engine.Engine, version version.Version, w http.Resp
var copyData engine.Env
- if contentType := r.Header.Get("Content-Type"); contentType == "application/json" {
+ if contentType := r.Header.Get("Content-Type"); MatchesContentType(contentType, "application/json") {
if err := copyData.Decode(r.Body); err != nil {
return err
}
@@ -1130,7 +1129,7 @@ func changeGroup(addr string, nameOrGid string) error {
// ListenAndServe sets up the required http.Server and gets it listening for
// each addr passed in and does protocol specific checking.
-func ListenAndServe(proto, addr string, eng *engine.Engine, logging, enableCors bool, dockerVersion string, socketGroup string) error {
+func ListenAndServe(proto, addr string, eng *engine.Engine, logging, enableCors bool, dockerVersion, socketGroup string) error {
r, err := createRouter(eng, logging, enableCors, dockerVersion)
if err != nil {
@@ -1147,7 +1146,7 @@ func ListenAndServe(proto, addr string, eng *engine.Engine, logging, enableCors
}
}
- l, err := listenbuffer.NewListenBuffer(proto, addr, activationLock, 15*time.Minute)
+ l, err := listenbuffer.NewListenBuffer(proto, addr, activationLock)
if err != nil {
return err
}
diff --git a/container.go b/container.go
index 50332f27de..42b905a265 100644
--- a/container.go
+++ b/container.go
@@ -363,14 +363,18 @@ func populateCommand(c *Container) {
driverConfig []string
)
+ en = &execdriver.Network{
+ Mtu: c.runtime.config.Mtu,
+ Interface: nil,
+ }
+
if !c.Config.NetworkDisabled {
network := c.NetworkSettings
- en = &execdriver.Network{
+ en.Interface = &execdriver.NetworkInterface{
Gateway: network.Gateway,
Bridge: network.Bridge,
IPAddress: network.IPAddress,
IPPrefixLen: network.IPPrefixLen,
- Mtu: c.runtime.config.Mtu,
}
}
@@ -784,7 +788,7 @@ func (container *Container) monitor(callback execdriver.StartCallback) error {
utils.Errorf("Error running container: %s", err)
}
- if container.runtime.srv.IsRunning() {
+ if container.runtime != nil && container.runtime.srv != nil && container.runtime.srv.IsRunning() {
container.State.SetStopped(exitCode)
// FIXME: there is a race condition here which causes this to fail during the unit tests.
diff --git a/contrib/init/sysvinit-debian/docker b/contrib/init/sysvinit-debian/docker
index 510683a459..67f0d2807f 100755
--- a/contrib/init/sysvinit-debian/docker
+++ b/contrib/init/sysvinit-debian/docker
@@ -21,6 +21,7 @@ BASE=$(basename $0)
# modify these in /etc/default/$BASE (/etc/default/docker)
DOCKER=/usr/bin/$BASE
DOCKER_PIDFILE=/var/run/$BASE.pid
+DOCKER_LOGFILE=/var/log/$BASE.log
DOCKER_OPTS=
DOCKER_DESC="Docker"
@@ -50,23 +51,37 @@ fail_unless_root() {
fi
}
+cgroupfs_mount() {
+ # see also https://github.com/tianon/cgroupfs-mount/blob/master/cgroupfs-mount
+ if grep -v '^#' /etc/fstab | grep -q cgroup \
+ || [ ! -e /proc/cgroups ] \
+ || [ ! -d /sys/fs/cgroup ]; then
+ return
+ fi
+ if ! mountpoint -q /sys/fs/cgroup; then
+ mount -t tmpfs -o uid=0,gid=0,mode=0755 cgroup /sys/fs/cgroup
+ fi
+ (
+ cd /sys/fs/cgroup
+ for sys in $(awk '!/^#/ { if ($4 == 1) print $1 }' /proc/cgroups); do
+ mkdir -p $sys
+ if ! mountpoint -q $sys; then
+ if ! mount -n -t cgroup -o $sys cgroup $sys; then
+ rmdir $sys || true
+ fi
+ fi
+ done
+ )
+}
+
case "$1" in
start)
fail_unless_root
- if ! grep -q cgroup /proc/mounts; then
- # rough approximation of cgroupfs-mount
- mount -t tmpfs -o uid=0,gid=0,mode=0755 cgroup /sys/fs/cgroup
- for sys in $(cut -d' ' -f1 /proc/cgroups); do
- mkdir -p /sys/fs/cgroup/$sys
- if ! mount -n -t cgroup -o $sys cgroup /sys/fs/cgroup/$sys 2>/dev/null; then
- rmdir /sys/fs/cgroup/$sys 2>/dev/null || true
- fi
- done
- fi
+ cgroupfs_mount
- touch /var/log/docker.log
- chgrp docker /var/log/docker.log
+ touch "$DOCKER_LOGFILE"
+ chgrp docker "$DOCKER_LOGFILE"
log_begin_msg "Starting $DOCKER_DESC: $BASE"
start-stop-daemon --start --background \
@@ -76,7 +91,7 @@ case "$1" in
-- \
-d -p "$DOCKER_PIDFILE" \
$DOCKER_OPTS \
- > /var/log/docker.log 2>&1
+ >> "$DOCKER_LOGFILE" 2>&1
log_end_msg $?
;;
diff --git a/contrib/init/upstart/docker.conf b/contrib/init/upstart/docker.conf
index e2cc4536e1..047f21c092 100644
--- a/contrib/init/upstart/docker.conf
+++ b/contrib/init/upstart/docker.conf
@@ -5,6 +5,29 @@ stop on runlevel [!2345]
respawn
+pre-start script
+ # see also https://github.com/tianon/cgroupfs-mount/blob/master/cgroupfs-mount
+ if grep -v '^#' /etc/fstab | grep -q cgroup \
+ || [ ! -e /proc/cgroups ] \
+ || [ ! -d /sys/fs/cgroup ]; then
+ exit 0
+ fi
+ if ! mountpoint -q /sys/fs/cgroup; then
+ mount -t tmpfs -o uid=0,gid=0,mode=0755 cgroup /sys/fs/cgroup
+ fi
+ (
+ cd /sys/fs/cgroup
+ for sys in $(awk '!/^#/ { if ($4 == 1) print $1 }' /proc/cgroups); do
+ mkdir -p $sys
+ if ! mountpoint -q $sys; then
+ if ! mount -n -t cgroup -o $sys cgroup $sys; then
+ rmdir $sys || true
+ fi
+ fi
+ done
+ )
+end script
+
script
# modify these in /etc/default/$UPSTART_JOB (/etc/default/docker)
DOCKER=/usr/bin/$UPSTART_JOB
@@ -12,15 +35,5 @@ script
if [ -f /etc/default/$UPSTART_JOB ]; then
. /etc/default/$UPSTART_JOB
fi
- if ! grep -q cgroup /proc/mounts; then
- # rough approximation of cgroupfs-mount
- mount -t tmpfs -o uid=0,gid=0,mode=0755 cgroup /sys/fs/cgroup
- for sys in $(cut -d' ' -f1 /proc/cgroups); do
- mkdir -p /sys/fs/cgroup/$sys
- if ! mount -n -t cgroup -o $sys cgroup /sys/fs/cgroup/$sys 2>/dev/null; then
- rmdir /sys/fs/cgroup/$sys 2>/dev/null || true
- fi
- done
- fi
"$DOCKER" -d $DOCKER_OPTS
end script
diff --git a/docs/sources/articles/security.rst b/docs/sources/articles/security.rst
index 3dc5780e85..e738e9a847 100644
--- a/docs/sources/articles/security.rst
+++ b/docs/sources/articles/security.rst
@@ -7,7 +7,7 @@
Docker Security
===============
- *Adapted from* `Containers & Docker: How Secure are They? <blogsecurity>`_
+ *Adapted from* `Containers & Docker: How Secure are They? <blogsecurity_>`_
There are three major areas to consider when reviewing Docker security:
@@ -261,7 +261,7 @@ with Docker, since everything is provided by the kernel anyway.
For more context and especially for comparisons with VMs and other
container systems, please also see the `original blog post
-<blogsecurity>`_.
+<blogsecurity_>`_.
.. _blogsecurity: http://blog.docker.io/2013/08/containers-docker-how-secure-are-they/
diff --git a/docs/sources/reference/commandline/cli.rst b/docs/sources/reference/commandline/cli.rst
index 2e49cd5ca5..9e99671ba5 100644
--- a/docs/sources/reference/commandline/cli.rst
+++ b/docs/sources/reference/commandline/cli.rst
@@ -88,7 +88,7 @@ Commands
-v, --version=false: Print version information and quit
--mtu=0: Set the containers network MTU; if no value is provided: default to the default route MTU or 1500 if no default route is available
-The Docker daemon is the persistent process that manages containers. Docker uses the same binary for both the
+The Docker daemon is the persistent process that manages containers. Docker uses the same binary for both the
daemon and client. To run the daemon you provide the ``-d`` flag.
To force Docker to use devicemapper as the storage driver, use ``docker -d -s devicemapper``.
@@ -100,10 +100,10 @@ To run the daemon with debug output, use ``docker -d -D``.
To use lxc as the execution driver, use ``docker -d -e lxc``.
The docker client will also honor the ``DOCKER_HOST`` environment variable to set
-the ``-H`` flag for the client.
+the ``-H`` flag for the client.
::
-
+
docker -H tcp://0.0.0.0:4243 ps
# or
export DOCKER_HOST="tcp://0.0.0.0:4243"
@@ -141,7 +141,7 @@ TMPDIR and the data directory can be set like this:
You can detach from the container again (and leave it running) with
``CTRL-c`` (for a quiet exit) or ``CTRL-\`` to get a stacktrace of
-the Docker client when it quits. When you detach from the container's
+the Docker client when it quits. When you detach from the container's
process the exit code will be returned to the client.
To stop a container, use ``docker stop``.
@@ -303,7 +303,7 @@ by using the ``git://`` schema.
-m, --message="": Commit message
-a, --author="": Author (eg. "John Hannibal Smith <hannibal@a-team.com>"
- --run="": Configuration to be applied when the image is launched with `docker run`.
+ --run="": Configuration changes to be applied when the image is launched with `docker run`.
(ex: -run='{"Cmd": ["cat", "/world"], "PortSpecs": ["22"]}')
.. _cli_commit_examples:
@@ -315,14 +315,14 @@ Commit an existing container
$ sudo docker ps
ID IMAGE COMMAND CREATED STATUS PORTS
- c3f279d17e0a ubuntu:12.04 /bin/bash 7 days ago Up 25 hours
- 197387f1b436 ubuntu:12.04 /bin/bash 7 days ago Up 25 hours
+ c3f279d17e0a ubuntu:12.04 /bin/bash 7 days ago Up 25 hours
+ 197387f1b436 ubuntu:12.04 /bin/bash 7 days ago Up 25 hours
$ docker commit c3f279d17e0a SvenDowideit/testimage:version3
f5283438590d
$ docker images | head
REPOSITORY TAG ID CREATED VIRTUAL SIZE
SvenDowideit/testimage version3 f5283438590d 16 seconds ago 335.7 MB
-
+
Change the command that a container runs
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -344,11 +344,40 @@ run ``ls /etc``.
apt host.conf lsb-base rc2.d
...
+Merged configs example
+......................
+
+Say you have a Dockerfile like so:
+
+.. code-block:: bash
+
+ ENV MYVAR foobar
+ RUN apt-get install openssh
+ EXPOSE 22
+ CMD ["/usr/sbin/sshd -D"]
+ ...
+
+If you run that, make some changes, and then commit, Docker will merge the environment variable and exposed port configuration settings with any that you specify in the -run= option. This is a change from Docker 0.8.0 and prior where no attempt was made to preserve any existing configuration on commit.
+
+.. code-block:: bash
+
+ $ docker build -t me/foo .
+ $ docker run -t -i me/foo /bin/bash
+ foo-container$ [make changes in the container]
+ foo-container$ exit
+ $ docker commit -run='{"Cmd": ["ls"]}' [container-id] me/bar
+ ...
+
+The me/bar image will now have port 22 exposed, MYVAR env var set to 'foobar', and its default command will be ["ls"].
+
+Note that this is currently a shallow merge. So, for example, if you had specified a new port spec in the -run= config above, that would have clobbered the 'EXPOSE 22' setting from the parent container.
+
Full -run example
.................
The ``--run`` JSON hash changes the ``Config`` section when running ``docker inspect CONTAINERID``
-or ``config`` when running ``docker inspect IMAGEID``.
+or ``config`` when running ``docker inspect IMAGEID``. Existing configuration key-values that are
+not overridden in the JSON hash will be merged in.
(Multiline is okay within a single quote ``'``)
@@ -664,7 +693,7 @@ Displaying image hierarchy
Usage: docker import URL|- [REPOSITORY[:TAG]]
- Create an empty filesystem image and import the contents of the tarball
+ Create an empty filesystem image and import the contents of the tarball
(.tar, .tar.gz, .tgz, .bzip, .tar.xz, .txz) into it, then optionally tag it.
At this time, the URL must start with ``http`` and point to a single
@@ -942,7 +971,7 @@ Running ``docker ps`` showing 2 linked containers.
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
- 4c01db0b339c ubuntu:12.04 bash 17 seconds ago Up 16 seconds webapp
+ 4c01db0b339c ubuntu:12.04 bash 17 seconds ago Up 16 seconds webapp
d7886598dbe2 crosbymichael/redis:latest /redis-server --dir 33 minutes ago Up 33 minutes 6379/tcp redis,webapp/db
fd2645e2e2b5 busybox:latest top 10 days ago Ghost insane_ptolemy
@@ -1047,7 +1076,7 @@ containers will not be deleted.
Remove one or more images
-f, --force=false: Force
-
+
Removing tagged images
~~~~~~~~~~~~~~~~~~~~~~
@@ -1126,8 +1155,8 @@ Once the container is stopped it still exists and can be started back up. See `
The ``docker run`` command can be used in combination with ``docker commit`` to
:ref:`change the command that a container runs <cli_commit_examples>`.
-See :ref:`port_redirection` for more detailed information about the ``--expose``,
-``-p``, ``-P`` and ``--link`` parameters, and :ref:`working_with_links_names` for
+See :ref:`port_redirection` for more detailed information about the ``--expose``,
+``-p``, ``-P`` and ``--link`` parameters, and :ref:`working_with_links_names` for
specific examples using ``--link``.
Known Issues (run -volumes-from)
@@ -1207,8 +1236,8 @@ starting your container.
$ sudo docker run -t -i -v /var/run/docker.sock:/var/run/docker.sock -v ./static-docker:/usr/bin/docker busybox sh
-By bind-mounting the docker unix socket and statically linked docker binary
-(such as that provided by https://get.docker.io), you give the container
+By bind-mounting the docker unix socket and statically linked docker binary
+(such as that provided by https://get.docker.io), you give the container
the full access to create and manipulate the host's docker daemon.
.. code-block:: bash
diff --git a/execdriver/driver.go b/execdriver/driver.go
index ec8f48f52d..bb7aad272b 100644
--- a/execdriver/driver.go
+++ b/execdriver/driver.go
@@ -84,11 +84,15 @@ type Driver interface {
// Network settings of the container
type Network struct {
+ Interface *NetworkInterface `json:"interface"` // if interface is nil then networking is disabled
+ Mtu int `json:"mtu"`
+}
+
+type NetworkInterface struct {
Gateway string `json:"gateway"`
IPAddress string `json:"ip"`
Bridge string `json:"bridge"`
IPPrefixLen int `json:"ip_prefix_len"`
- Mtu int `json:"mtu"`
}
type Resources struct {
@@ -111,8 +115,8 @@ type Command struct {
WorkingDir string `json:"working_dir"`
ConfigPath string `json:"config_path"` // this should be able to be removed when the lxc template is moved into the driver
Tty bool `json:"tty"`
- Network *Network `json:"network"` // if network is nil then networking is disabled
- Config []string `json:"config"` // generic values that specific drivers can consume
+ Network *Network `json:"network"`
+ Config []string `json:"config"` // generic values that specific drivers can consume
Resources *Resources `json:"resources"`
Terminal Terminal `json:"-"` // standard or tty terminal
diff --git a/execdriver/lxc/driver.go b/execdriver/lxc/driver.go
index 765a52ee43..f24b3b51be 100644
--- a/execdriver/lxc/driver.go
+++ b/execdriver/lxc/driver.go
@@ -94,13 +94,15 @@ func (d *driver) Run(c *execdriver.Command, pipes *execdriver.Pipes, startCallba
DriverName,
}
- if c.Network != nil {
+ if c.Network.Interface != nil {
params = append(params,
- "-g", c.Network.Gateway,
- "-i", fmt.Sprintf("%s/%d", c.Network.IPAddress, c.Network.IPPrefixLen),
- "-mtu", strconv.Itoa(c.Network.Mtu),
+ "-g", c.Network.Interface.Gateway,
+ "-i", fmt.Sprintf("%s/%d", c.Network.Interface.IPAddress, c.Network.Interface.IPPrefixLen),
)
}
+ params = append(params,
+ "-mtu", strconv.Itoa(c.Network.Mtu),
+ )
if c.User != "" {
params = append(params, "-u", c.User)
@@ -168,6 +170,9 @@ func (d *driver) Run(c *execdriver.Command, pipes *execdriver.Pipes, startCallba
// Poll lxc for RUNNING status
pid, err := d.waitForStart(c, waitLock)
if err != nil {
+ if c.Process != nil {
+ c.Process.Kill()
+ }
return -1, err
}
c.ContainerPid = pid
diff --git a/execdriver/lxc/info.go b/execdriver/lxc/info.go
index 3b2ea0d07f..27b4c58604 100644
--- a/execdriver/lxc/info.go
+++ b/execdriver/lxc/info.go
@@ -36,7 +36,7 @@ func parseLxcInfo(raw string) (*lxcInfo, error) {
if len(parts) < 2 {
continue
}
- switch strings.TrimSpace(parts[0]) {
+ switch strings.ToLower(strings.TrimSpace(parts[0])) {
case "state":
info.Running = strings.TrimSpace(parts[1]) == "RUNNING"
case "pid":
diff --git a/execdriver/lxc/lxc_template.go b/execdriver/lxc/lxc_template.go
index 1181396a18..03d01e893c 100644
--- a/execdriver/lxc/lxc_template.go
+++ b/execdriver/lxc/lxc_template.go
@@ -7,17 +7,17 @@ import (
)
const LxcTemplate = `
-{{if .Network}}
+{{if .Network.Interface}}
# network configuration
lxc.network.type = veth
-lxc.network.link = {{.Network.Bridge}}
+lxc.network.link = {{.Network.Interface.Bridge}}
lxc.network.name = eth0
-lxc.network.mtu = {{.Network.Mtu}}
{{else}}
# network is disabled (-n=false)
lxc.network.type = empty
lxc.network.flags = up
{{end}}
+lxc.network.mtu = {{.Network.Mtu}}
# root filesystem
{{$ROOTFS := .Rootfs}}
diff --git a/execdriver/lxc/lxc_template_unit_test.go b/execdriver/lxc/lxc_template_unit_test.go
index 99d6e636f5..6fea86d295 100644
--- a/execdriver/lxc/lxc_template_unit_test.go
+++ b/execdriver/lxc/lxc_template_unit_test.go
@@ -43,6 +43,10 @@ func TestLXCConfig(t *testing.T) {
Memory: int64(mem),
CpuShares: int64(cpu),
},
+ Network: &execdriver.Network{
+ Mtu: 1500,
+ Interface: nil,
+ },
}
p, err := driver.generateLXCConfig(command)
if err != nil {
@@ -75,6 +79,10 @@ func TestCustomLxcConfig(t *testing.T) {
"lxc.utsname = docker",
"lxc.cgroup.cpuset.cpus = 0,1",
},
+ Network: &execdriver.Network{
+ Mtu: 1500,
+ Interface: nil,
+ },
}
p, err := driver.generateLXCConfig(command)
diff --git a/execdriver/native/default_template.go b/execdriver/native/default_template.go
index 6e7d597b7b..62d804016a 100644
--- a/execdriver/native/default_template.go
+++ b/execdriver/native/default_template.go
@@ -19,19 +19,30 @@ func createContainer(c *execdriver.Command) *libcontainer.Container {
container.WorkingDir = c.WorkingDir
container.Env = c.Env
- if c.Network != nil {
- container.Networks = []*libcontainer.Network{
- {
- Mtu: c.Network.Mtu,
- Address: fmt.Sprintf("%s/%d", c.Network.IPAddress, c.Network.IPPrefixLen),
- Gateway: c.Network.Gateway,
- Type: "veth",
- Context: libcontainer.Context{
- "prefix": "veth",
- "bridge": c.Network.Bridge,
- },
+ loopbackNetwork := libcontainer.Network{
+ Mtu: c.Network.Mtu,
+ Address: fmt.Sprintf("%s/%d", "127.0.0.1", 0),
+ Gateway: "localhost",
+ Type: "loopback",
+ Context: libcontainer.Context{},
+ }
+
+ container.Networks = []*libcontainer.Network{
+ &loopbackNetwork,
+ }
+
+ if c.Network.Interface != nil {
+ vethNetwork := libcontainer.Network{
+ Mtu: c.Network.Mtu,
+ Address: fmt.Sprintf("%s/%d", c.Network.Interface.IPAddress, c.Network.Interface.IPPrefixLen),
+ Gateway: c.Network.Interface.Gateway,
+ Type: "veth",
+ Context: libcontainer.Context{
+ "prefix": "veth",
+ "bridge": c.Network.Interface.Bridge,
},
}
+ container.Networks = append(container.Networks, &vethNetwork)
}
container.Cgroups.Name = c.ID
diff --git a/graphdriver/devmapper/deviceset.go b/graphdriver/devmapper/deviceset.go
index 303e363e92..4d33e243e0 100644
--- a/graphdriver/devmapper/deviceset.go
+++ b/graphdriver/devmapper/deviceset.go
@@ -39,6 +39,13 @@ type DevInfo struct {
// first get (since we need to mount to set up the device
// a bit first).
floating bool `json:"-"`
+
+ // The global DeviceSet lock guarantees that we serialize all
+ // the calls to libdevmapper (which is not threadsafe), but we
+ // sometimes release that lock while sleeping. In that case
+ // this per-device lock is still held, protecting against
+ // other accesses to the device that we're doing the wait on.
+ lock sync.Mutex `json:"-"`
}
type MetaData struct {
@@ -47,7 +54,7 @@ type MetaData struct {
type DeviceSet struct {
MetaData
- sync.Mutex
+ sync.Mutex // Protects Devices map and serializes calls into libdevmapper
root string
devicePrefix string
TransactionId uint64
@@ -569,6 +576,9 @@ func (devices *DeviceSet) AddDevice(hash, baseHash string) error {
return fmt.Errorf("Error adding device for '%s': can't find device for parent '%s'", hash, baseHash)
}
+ baseInfo.lock.Lock()
+ defer baseInfo.lock.Unlock()
+
deviceId := devices.allocateDeviceId()
if err := devices.createSnapDevice(devices.getPoolDevName(), deviceId, baseInfo.Name(), baseInfo.DeviceId); err != nil {
@@ -636,6 +646,14 @@ func (devices *DeviceSet) DeleteDevice(hash string) error {
devices.Lock()
defer devices.Unlock()
+ info := devices.Devices[hash]
+ if info == nil {
+ return fmt.Errorf("Unknown device %s", hash)
+ }
+
+ info.lock.Lock()
+ defer info.lock.Unlock()
+
return devices.deleteDevice(hash)
}
@@ -683,7 +701,7 @@ func (devices *DeviceSet) deactivateDevice(hash string) error {
func (devices *DeviceSet) removeDeviceAndWait(devname string) error {
var err error
- for i := 0; i < 10; i++ {
+ for i := 0; i < 1000; i++ {
devices.sawBusy = false
err = removeDevice(devname)
if err == nil {
@@ -695,7 +713,9 @@ func (devices *DeviceSet) removeDeviceAndWait(devname string) error {
// If we see EBUSY it may be a transient error,
// sleep a bit a retry a few times.
- time.Sleep(5 * time.Millisecond)
+ devices.Unlock()
+ time.Sleep(10 * time.Millisecond)
+ devices.Lock()
}
if err != nil {
return err
@@ -709,7 +729,7 @@ func (devices *DeviceSet) removeDeviceAndWait(devname string) error {
// waitRemove blocks until either:
// a) the device registered at <device_set_prefix>-<hash> is removed,
-// or b) the 1 second timeout expires.
+// or b) the 10 second timeout expires.
func (devices *DeviceSet) waitRemove(devname string) error {
utils.Debugf("[deviceset %s] waitRemove(%s)", devices.devicePrefix, devname)
defer utils.Debugf("[deviceset %s] waitRemove(%s) END", devices.devicePrefix, devname)
@@ -728,7 +748,9 @@ func (devices *DeviceSet) waitRemove(devname string) error {
break
}
- time.Sleep(1 * time.Millisecond)
+ devices.Unlock()
+ time.Sleep(10 * time.Millisecond)
+ devices.Lock()
}
if i == 1000 {
return fmt.Errorf("Timeout while waiting for device %s to be removed", devname)
@@ -738,7 +760,7 @@ func (devices *DeviceSet) waitRemove(devname string) error {
// waitClose blocks until either:
// a) the device registered at <device_set_prefix>-<hash> is closed,
-// or b) the 1 second timeout expires.
+// or b) the 10 second timeout expires.
func (devices *DeviceSet) waitClose(hash string) error {
info := devices.Devices[hash]
if info == nil {
@@ -756,7 +778,9 @@ func (devices *DeviceSet) waitClose(hash string) error {
if devinfo.OpenCount == 0 {
break
}
- time.Sleep(1 * time.Millisecond)
+ devices.Unlock()
+ time.Sleep(10 * time.Millisecond)
+ devices.Lock()
}
if i == 1000 {
return fmt.Errorf("Timeout while waiting for device %s to close", hash)
@@ -773,20 +797,26 @@ func (devices *DeviceSet) Shutdown() error {
defer utils.Debugf("[deviceset %s] shutdown END", devices.devicePrefix)
for _, info := range devices.Devices {
+ info.lock.Lock()
if info.mountCount > 0 {
if err := sysUnmount(info.mountPath, 0); err != nil {
utils.Debugf("Shutdown unmounting %s, error: %s\n", info.mountPath, err)
}
}
+ info.lock.Unlock()
}
for _, d := range devices.Devices {
+ d.lock.Lock()
+
if err := devices.waitClose(d.Hash); err != nil {
utils.Errorf("Warning: error waiting for device %s to unmount: %s\n", d.Hash, err)
}
if err := devices.deactivateDevice(d.Hash); err != nil {
utils.Debugf("Shutdown deactivate %s , error: %s\n", d.Hash, err)
}
+
+ d.lock.Unlock()
}
if err := devices.deactivatePool(); err != nil {
@@ -805,6 +835,9 @@ func (devices *DeviceSet) MountDevice(hash, path string) error {
return fmt.Errorf("Unknown device %s", hash)
}
+ info.lock.Lock()
+ defer info.lock.Unlock()
+
if info.mountCount > 0 {
if path != info.mountPath {
return fmt.Errorf("Trying to mount devmapper device in multple places (%s, %s)", info.mountPath, path)
@@ -851,6 +884,9 @@ func (devices *DeviceSet) UnmountDevice(hash string, mode UnmountMode) error {
return fmt.Errorf("UnmountDevice: no such device %s\n", hash)
}
+ info.lock.Lock()
+ defer info.lock.Unlock()
+
if mode == UnmountFloat {
if info.floating {
return fmt.Errorf("UnmountDevice: can't float floating reference %s\n", hash)
@@ -920,6 +956,10 @@ func (devices *DeviceSet) HasActivatedDevice(hash string) bool {
if info == nil {
return false
}
+
+ info.lock.Lock()
+ defer info.lock.Unlock()
+
devinfo, _ := getInfo(info.Name())
return devinfo != nil && devinfo.Exists != 0
}
@@ -974,6 +1014,9 @@ func (devices *DeviceSet) GetDeviceStatus(hash string) (*DevStatus, error) {
return nil, fmt.Errorf("No device %s", hash)
}
+ info.lock.Lock()
+ defer info.lock.Unlock()
+
status := &DevStatus{
DeviceId: info.DeviceId,
Size: info.Size,
diff --git a/graphdriver/devmapper/driver.go b/graphdriver/devmapper/driver.go
index 4d414f9a75..8c5a19eea0 100644
--- a/graphdriver/devmapper/driver.go
+++ b/graphdriver/devmapper/driver.go
@@ -90,6 +90,13 @@ func (d *Driver) Create(id, parent string) error {
}
func (d *Driver) Remove(id string) error {
+ if !d.DeviceSet.HasDevice(id) {
+ // Consider removing a non-existing device a no-op
+ // This is useful to be able to progress on container removal
+ // if the underlying device has gone away due to earlier errors
+ return nil
+ }
+
// Sink the float from create in case no Get() call was made
if err := d.DeviceSet.UnmountDevice(id, UnmountSink); err != nil {
return err
diff --git a/hack/make.sh b/hack/make.sh
index 63edca4d4c..50886eb9da 100755
--- a/hack/make.sh
+++ b/hack/make.sh
@@ -141,6 +141,27 @@ find_dirs() {
\) -name "$1" -print0 | xargs -0n1 dirname | sort -u
}
+hash_files() {
+ while [ $# -gt 0 ]; do
+ f="$1"
+ shift
+ dir="$(dirname "$f")"
+ base="$(basename "$f")"
+ for hashAlgo in md5 sha256; do
+ if command -v "${hashAlgo}sum" &> /dev/null; then
+ (
+ # subshell and cd so that we get output files like:
+ # $HASH docker-$VERSION
+ # instead of:
+ # $HASH /go/src/github.com/.../$VERSION/binary/docker-$VERSION
+ cd "$dir"
+ "${hashAlgo}sum" "$base" > "$base.$hashAlgo"
+ )
+ fi
+ done
+ done
+}
+
bundle() {
bundlescript=$1
bundle=$(basename $bundlescript)
diff --git a/hack/make/binary b/hack/make/binary
index 7272b1ede0..7b4d7b5b5b 100644..100755
--- a/hack/make/binary
+++ b/hack/make/binary
@@ -3,7 +3,7 @@
DEST=$1
go build \
- -o $DEST/docker-$VERSION \
+ -o "$DEST/docker-$VERSION" \
"${BUILDFLAGS[@]}" \
-ldflags "
$LDFLAGS
@@ -11,3 +11,5 @@ go build \
" \
./docker
echo "Created binary: $DEST/docker-$VERSION"
+
+hash_files "$DEST/docker-$VERSION"
diff --git a/hack/make/dynbinary b/hack/make/dynbinary
index d4f583fb62..75cffe3dcc 100644
--- a/hack/make/dynbinary
+++ b/hack/make/dynbinary
@@ -5,7 +5,7 @@ DEST=$1
if [ -z "$DOCKER_CLIENTONLY" ]; then
# dockerinit still needs to be a static binary, even if docker is dynamic
go build \
- -o $DEST/dockerinit-$VERSION \
+ -o "$DEST/dockerinit-$VERSION" \
"${BUILDFLAGS[@]}" \
-ldflags "
$LDFLAGS
@@ -14,7 +14,9 @@ if [ -z "$DOCKER_CLIENTONLY" ]; then
" \
./dockerinit
echo "Created binary: $DEST/dockerinit-$VERSION"
- ln -sf dockerinit-$VERSION $DEST/dockerinit
+ ln -sf "dockerinit-$VERSION" "$DEST/dockerinit"
+
+ hash_files "$DEST/dockerinit-$VERSION"
sha1sum=
if command -v sha1sum &> /dev/null; then
diff --git a/hack/make/tgz b/hack/make/tgz
index 5d03306322..120339976b 100644
--- a/hack/make/tgz
+++ b/hack/make/tgz
@@ -23,6 +23,8 @@ for d in "$CROSS/"*/*; do
tar --numeric-owner --owner 0 -C "$DEST/build" -czf "$TGZ" usr
+ hash_files "$TGZ"
+
rm -rf "$DEST/build"
echo "Created tgz: $TGZ"
diff --git a/hack/release.sh b/hack/release.sh
index 50913dd395..c771fb7a97 100755
--- a/hack/release.sh
+++ b/hack/release.sh
@@ -55,33 +55,16 @@ RELEASE_BUNDLES=(
if [ "$1" != '--release-regardless-of-test-failure' ]; then
RELEASE_BUNDLES=( test "${RELEASE_BUNDLES[@]}" )
fi
-
-if ! ./hack/make.sh "${RELEASE_BUNDLES[@]}"; then
- echo >&2
- echo >&2 'The build or tests appear to have failed.'
- echo >&2
- echo >&2 'You, as the release maintainer, now have a couple options:'
- echo >&2 '- delay release and fix issues'
- echo >&2 '- delay release and fix issues'
- echo >&2 '- did we mention how important this is? issues need fixing :)'
- echo >&2
- echo >&2 'As a final LAST RESORT, you (because only you, the release maintainer,'
- echo >&2 ' really knows all the hairy problems at hand with the current release'
- echo >&2 ' issues) may bypass this checking by running this script again with the'
- echo >&2 ' single argument of "--release-regardless-of-test-failure", which will skip'
- echo >&2 ' running the test suite, and will only build the binaries and packages. Please'
- echo >&2 ' avoid using this if at all possible.'
- echo >&2
- echo >&2 'Regardless, we cannot stress enough the scarcity with which this bypass'
- echo >&2 ' should be used. If there are release issues, we should always err on the'
- echo >&2 ' side of caution.'
- echo >&2
- exit 1
-fi
-
+
VERSION=$(cat VERSION)
BUCKET=$AWS_S3_BUCKET
+# These are the 2 keys we've used to sign the deb's
+# release (get.docker.io)
+# GPG_KEY="36A1D7869245C8950F966E92D8576A8BA88D21E9"
+# test (test.docker.io)
+# GPG_KEY="740B314AE3941731B942C66ADF4FD13717AAD7D6"
+
setup_s3() {
# Try creating the bucket. Ignore errors (it might already exist).
s3cmd mb s3://$BUCKET 2>/dev/null || true
@@ -114,76 +97,138 @@ s3_url() {
esac
}
+build_all() {
+ if ! ./hack/make.sh "${RELEASE_BUNDLES[@]}"; then
+ echo >&2
+ echo >&2 'The build or tests appear to have failed.'
+ echo >&2
+ echo >&2 'You, as the release maintainer, now have a couple options:'
+ echo >&2 '- delay release and fix issues'
+ echo >&2 '- delay release and fix issues'
+ echo >&2 '- did we mention how important this is? issues need fixing :)'
+ echo >&2
+ echo >&2 'As a final LAST RESORT, you (because only you, the release maintainer,'
+ echo >&2 ' really knows all the hairy problems at hand with the current release'
+ echo >&2 ' issues) may bypass this checking by running this script again with the'
+ echo >&2 ' single argument of "--release-regardless-of-test-failure", which will skip'
+ echo >&2 ' running the test suite, and will only build the binaries and packages. Please'
+ echo >&2 ' avoid using this if at all possible.'
+ echo >&2
+ echo >&2 'Regardless, we cannot stress enough the scarcity with which this bypass'
+ echo >&2 ' should be used. If there are release issues, we should always err on the'
+ echo >&2 ' side of caution.'
+ echo >&2
+ exit 1
+ fi
+}
+
+upload_release_build() {
+ src="$1"
+ dst="$2"
+ latest="$3"
+
+ echo
+ echo "Uploading $src"
+ echo " to $dst"
+ echo
+ s3cmd --follow-symlinks --preserve --acl-public put "$src" "$dst"
+ if [ "$latest" ]; then
+ echo
+ echo "Copying to $latest"
+ echo
+ s3cmd --acl-public cp "$dst" "$latest"
+ fi
+
+ # get hash files too (see hash_files() in hack/make.sh)
+ for hashAlgo in md5 sha256; do
+ if [ -e "$src.$hashAlgo" ]; then
+ echo
+ echo "Uploading $src.$hashAlgo"
+ echo " to $dst.$hashAlgo"
+ echo
+ s3cmd --follow-symlinks --preserve --acl-public --mime-type='text/plain' put "$src.$hashAlgo" "$dst.$hashAlgo"
+ if [ "$latest" ]; then
+ echo
+ echo "Copying to $latest.$hashAlgo"
+ echo
+ s3cmd --acl-public cp "$dst.$hashAlgo" "$latest.$hashAlgo"
+ fi
+ fi
+ done
+}
+
release_build() {
GOOS=$1
GOARCH=$2
- BINARY=bundles/$VERSION/cross/$GOOS/$GOARCH/docker-$VERSION
- TGZ=bundles/$VERSION/tgz/$GOOS/$GOARCH/docker-$VERSION.tgz
+ binDir=bundles/$VERSION/cross/$GOOS/$GOARCH
+ tgzDir=bundles/$VERSION/tgz/$GOOS/$GOARCH
+ binary=docker-$VERSION
+ tgz=docker-$VERSION.tgz
+
+ latestBase=
+ if [ -z "$NOLATEST" ]; then
+ latestBase=docker-latest
+ fi
# we need to map our GOOS and GOARCH to uname values
# see https://en.wikipedia.org/wiki/Uname
# ie, GOOS=linux -> "uname -s"=Linux
- S3OS=$GOOS
- case "$S3OS" in
+ s3Os=$GOOS
+ case "$s3Os" in
darwin)
- S3OS=Darwin
+ s3Os=Darwin
;;
freebsd)
- S3OS=FreeBSD
+ s3Os=FreeBSD
;;
linux)
- S3OS=Linux
+ s3Os=Linux
;;
*)
- echo >&2 "error: can't convert $S3OS to an appropriate value for 'uname -s'"
+ echo >&2 "error: can't convert $s3Os to an appropriate value for 'uname -s'"
exit 1
;;
esac
- S3ARCH=$GOARCH
- case "$S3ARCH" in
+ s3Arch=$GOARCH
+ case "$s3Arch" in
amd64)
- S3ARCH=x86_64
+ s3Arch=x86_64
;;
386)
- S3ARCH=i386
+ s3Arch=i386
;;
arm)
- S3ARCH=armel
+ s3Arch=armel
# someday, we might potentially support mutliple GOARM values, in which case we might get armhf here too
;;
*)
- echo >&2 "error: can't convert $S3ARCH to an appropriate value for 'uname -m'"
+ echo >&2 "error: can't convert $s3Arch to an appropriate value for 'uname -m'"
exit 1
;;
esac
- S3DIR=s3://$BUCKET/builds/$S3OS/$S3ARCH
+ s3Dir=s3://$BUCKET/builds/$s3Os/$s3Arch
+ latest=
+ latestTgz=
+ if [ "$latestBase" ]; then
+ latest="$s3Dir/$latestBase"
+ latestTgz="$s3Dir/$latestBase.tgz"
+ fi
- if [ ! -x "$BINARY" ]; then
- echo >&2 "error: can't find $BINARY - was it compiled properly?"
+ if [ ! -x "$binDir/$binary" ]; then
+ echo >&2 "error: can't find $binDir/$binary - was it compiled properly?"
exit 1
fi
- if [ ! -f "$TGZ" ]; then
- echo >&2 "error: can't find $TGZ - was it packaged properly?"
+ if [ ! -f "$tgzDir/$tgz" ]; then
+ echo >&2 "error: can't find $tgzDir/$tgz - was it packaged properly?"
exit 1
fi
- echo "Uploading $BINARY to $S3OS/$S3ARCH/docker-$VERSION"
- s3cmd --follow-symlinks --preserve --acl-public put $BINARY $S3DIR/docker-$VERSION
-
- echo "Uploading $TGZ to $S3OS/$S3ARCH/docker-$VERSION.tgz"
- s3cmd --follow-symlinks --preserve --acl-public put $TGZ $S3DIR/docker-$VERSION.tgz
-
- if [ -z "$NOLATEST" ]; then
- echo "Copying $S3OS/$S3ARCH/docker-$VERSION to $S3OS/$S3ARCH/docker-latest"
- s3cmd --acl-public cp $S3DIR/docker-$VERSION $S3DIR/docker-latest
-
- echo "Copying $S3OS/$S3ARCH/docker-$VERSION.tgz to $S3OS/$S3ARCH/docker-latest.tgz"
- s3cmd --acl-public cp $S3DIR/docker-$VERSION.tgz $S3DIR/docker-latest.tgz
- fi
+ upload_release_build "$binDir/$binary" "$s3Dir/$binary" "$latest"
+ upload_release_build "$tgzDir/$tgz" "$s3Dir/$tgz" "$latestTgz"
}
# Upload the 'ubuntu' bundle to S3:
@@ -194,21 +239,6 @@ release_ubuntu() {
echo >&2 './hack/make.sh must be run before release_ubuntu'
exit 1
}
- # Make sure that we have our keys
- mkdir -p /.gnupg/
- s3cmd sync s3://$BUCKET/ubuntu/.gnupg/ /.gnupg/ || true
- gpg --list-keys releasedocker >/dev/null || {
- gpg --gen-key --batch <<EOF
-Key-Type: RSA
-Key-Length: 2048
-Passphrase: $GPG_PASSPHRASE
-Name-Real: Docker Release Tool
-Name-Email: docker@dotcloud.com
-Name-Comment: releasedocker
-Expire-Date: 0
-%commit
-EOF
- }
# Sign our packages
dpkg-sig -g "--passphrase $GPG_PASSPHRASE" -k releasedocker \
@@ -305,8 +335,28 @@ release_test() {
fi
}
+setup_gpg() {
+ # Make sure that we have our keys
+ mkdir -p /.gnupg/
+ s3cmd sync s3://$BUCKET/ubuntu/.gnupg/ /.gnupg/ || true
+ gpg --list-keys releasedocker >/dev/null || {
+ gpg --gen-key --batch <<EOF
+Key-Type: RSA
+Key-Length: 4096
+Passphrase: $GPG_PASSPHRASE
+Name-Real: Docker Release Tool
+Name-Email: docker@dotcloud.com
+Name-Comment: releasedocker
+Expire-Date: 0
+%commit
+EOF
+ }
+}
+
main() {
+ build_all
setup_s3
+ setup_gpg
release_binaries
release_ubuntu
release_index
diff --git a/integration/commands_test.go b/integration/commands_test.go
index 9f7a41384c..f254973b43 100644
--- a/integration/commands_test.go
+++ b/integration/commands_test.go
@@ -930,7 +930,7 @@ run [ "$(ls -d /var/run/sshd)" = "/var/run/sshd" ]
// #2098 - Docker cidFiles only contain short version of the containerId
//sudo docker run --cidfile /tmp/docker_test.cid ubuntu echo "test"
// TestRunCidFile tests that run --cidfile returns the longid
-func TestRunCidFile(t *testing.T) {
+func TestRunCidFileCheckIDLength(t *testing.T) {
stdout, stdoutPipe := io.Pipe()
tmpDir, err := ioutil.TempDir("", "TestRunCidFile")
@@ -979,6 +979,35 @@ func TestRunCidFile(t *testing.T) {
}
+// Ensure that CIDFile gets deleted if it's empty
+// Perform this test by making `docker run` fail
+func TestRunCidFileCleanupIfEmpty(t *testing.T) {
+ tmpDir, err := ioutil.TempDir("", "TestRunCidFile")
+ if err != nil {
+ t.Fatal(err)
+ }
+ tmpCidFile := path.Join(tmpDir, "cid")
+
+ cli := api.NewDockerCli(nil, ioutil.Discard, ioutil.Discard, testDaemonProto, testDaemonAddr)
+ defer cleanup(globalEngine, t)
+
+ c := make(chan struct{})
+ go func() {
+ defer close(c)
+ if err := cli.CmdRun("--cidfile", tmpCidFile, unitTestImageID); err == nil {
+ t.Fatal("running without a command should haveve failed")
+ }
+ if _, err := os.Stat(tmpCidFile); err == nil {
+ t.Fatalf("empty CIDFile '%s' should've been deleted", tmpCidFile)
+ }
+ }()
+ defer os.RemoveAll(tmpDir)
+
+ setTimeout(t, "CmdRun timed out", 5*time.Second, func() {
+ <-c
+ })
+}
+
func TestContainerOrphaning(t *testing.T) {
// setup a temporary directory
diff --git a/integration/container_test.go b/integration/container_test.go
index 4efb95a2a1..980fb44199 100644
--- a/integration/container_test.go
+++ b/integration/container_test.go
@@ -434,28 +434,6 @@ func TestOutput(t *testing.T) {
}
}
-func TestContainerNetwork(t *testing.T) {
- runtime := mkRuntime(t)
- defer nuke(runtime)
- container, _, err := runtime.Create(
- &runconfig.Config{
- Image: GetTestImage(runtime).ID,
- Cmd: []string{"ping", "-c", "1", "127.0.0.1"},
- },
- "",
- )
- if err != nil {
- t.Fatal(err)
- }
- defer runtime.Destroy(container)
- if err := container.Run(); err != nil {
- t.Fatal(err)
- }
- if code := container.State.GetExitCode(); code != 0 {
- t.Fatalf("Unexpected ping 127.0.0.1 exit code %d (expected 0)", code)
- }
-}
-
func TestKillDifferentUser(t *testing.T) {
runtime := mkRuntime(t)
defer nuke(runtime)
@@ -1523,6 +1501,53 @@ func TestVolumesFromWithVolumes(t *testing.T) {
}
}
+func TestContainerNetwork(t *testing.T) {
+ runtime := mkRuntime(t)
+ defer nuke(runtime)
+ container, _, err := runtime.Create(
+ &runconfig.Config{
+ Image: GetTestImage(runtime).ID,
+ // If I change this to ping 8.8.8.8 it fails. Any idea why? - timthelion
+ Cmd: []string{"ping", "-c", "1", "127.0.0.1"},
+ },
+ "",
+ )
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer runtime.Destroy(container)
+ if err := container.Run(); err != nil {
+ t.Fatal(err)
+ }
+ if code := container.State.GetExitCode(); code != 0 {
+ t.Fatalf("Unexpected ping 127.0.0.1 exit code %d (expected 0)", code)
+ }
+}
+
+// Issue #4681
+func TestLoopbackFunctionsWhenNetworkingIsDissabled(t *testing.T) {
+ runtime := mkRuntime(t)
+ defer nuke(runtime)
+ container, _, err := runtime.Create(
+ &runconfig.Config{
+ Image: GetTestImage(runtime).ID,
+ Cmd: []string{"ping", "-c", "1", "127.0.0.1"},
+ NetworkDisabled: true,
+ },
+ "",
+ )
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer runtime.Destroy(container)
+ if err := container.Run(); err != nil {
+ t.Fatal(err)
+ }
+ if code := container.State.GetExitCode(); code != 0 {
+ t.Fatalf("Unexpected ping 127.0.0.1 exit code %d (expected 0)", code)
+ }
+}
+
func TestOnlyLoopbackExistsWhenUsingDisableNetworkOption(t *testing.T) {
eng := NewTestEngine(t)
runtime := mkRuntimeFromEngine(eng, t)
diff --git a/integration/server_test.go b/integration/server_test.go
index 69a90527bf..e9781777e1 100644
--- a/integration/server_test.go
+++ b/integration/server_test.go
@@ -281,6 +281,63 @@ func TestCommit(t *testing.T) {
}
}
+func TestMergeConfigOnCommit(t *testing.T) {
+ eng := NewTestEngine(t)
+ runtime := mkRuntimeFromEngine(eng, t)
+ defer runtime.Nuke()
+
+ container1, _, _ := mkContainer(runtime, []string{"-e", "FOO=bar", unitTestImageID, "echo test > /tmp/foo"}, t)
+ defer runtime.Destroy(container1)
+
+ config, _, _, err := runconfig.Parse([]string{container1.ID, "cat /tmp/foo"}, nil)
+ if err != nil {
+ t.Error(err)
+ }
+
+ job := eng.Job("commit", container1.ID)
+ job.Setenv("repo", "testrepo")
+ job.Setenv("tag", "testtag")
+ job.SetenvJson("config", config)
+ var newId string
+ job.Stdout.AddString(&newId)
+ if err := job.Run(); err != nil {
+ t.Error(err)
+ }
+
+ container2, _, _ := mkContainer(runtime, []string{newId}, t)
+ defer runtime.Destroy(container2)
+
+ job = eng.Job("inspect", container1.Name, "container")
+ baseContainer, _ := job.Stdout.AddEnv()
+ if err := job.Run(); err != nil {
+ t.Error(err)
+ }
+
+ job = eng.Job("inspect", container2.Name, "container")
+ commitContainer, _ := job.Stdout.AddEnv()
+ if err := job.Run(); err != nil {
+ t.Error(err)
+ }
+
+ baseConfig := baseContainer.GetSubEnv("Config")
+ commitConfig := commitContainer.GetSubEnv("Config")
+
+ if commitConfig.Get("Env") != baseConfig.Get("Env") {
+ t.Fatalf("Env config in committed container should be %v, was %v",
+ baseConfig.Get("Env"), commitConfig.Get("Env"))
+ }
+
+ if baseConfig.Get("Cmd") != "[\"echo test \\u003e /tmp/foo\"]" {
+ t.Fatalf("Cmd in base container should be [\"echo test \\u003e /tmp/foo\"], was %s",
+ baseConfig.Get("Cmd"))
+ }
+
+ if commitConfig.Get("Cmd") != "[\"cat /tmp/foo\"]" {
+ t.Fatalf("Cmd in committed container should be [\"cat /tmp/foo\"], was %s",
+ commitConfig.Get("Cmd"))
+ }
+}
+
func TestRestartKillWait(t *testing.T) {
eng := NewTestEngine(t)
srv := mkServerFromEngine(eng, t)
diff --git a/pkg/libcontainer/apparmor/setup.go b/pkg/libcontainer/apparmor/setup.go
index e07759cc64..4e1c95143a 100644
--- a/pkg/libcontainer/apparmor/setup.go
+++ b/pkg/libcontainer/apparmor/setup.go
@@ -5,6 +5,7 @@ import (
"io/ioutil"
"os"
"os/exec"
+ "path"
)
const DefaultProfilePath = "/etc/apparmor.d/docker"
@@ -85,6 +86,11 @@ func InstallDefaultProfile() error {
return nil
}
+ // Make sure /etc/apparmor.d exists
+ if err := os.MkdirAll(path.Dir(DefaultProfilePath), 0755); err != nil {
+ return err
+ }
+
if err := ioutil.WriteFile(DefaultProfilePath, []byte(DefaultProfile), 0644); err != nil {
return err
}
diff --git a/pkg/libcontainer/network/loopback.go b/pkg/libcontainer/network/loopback.go
new file mode 100644
index 0000000000..6215061dc2
--- /dev/null
+++ b/pkg/libcontainer/network/loopback.go
@@ -0,0 +1,24 @@
+package network
+
+import (
+ "fmt"
+ "github.com/dotcloud/docker/pkg/libcontainer"
+)
+
+// Loopback is a network strategy that provides a basic loopback device
+type Loopback struct {
+}
+
+func (l *Loopback) Create(n *libcontainer.Network, nspid int, context libcontainer.Context) error {
+ return nil
+}
+
+func (l *Loopback) Initialize(config *libcontainer.Network, context libcontainer.Context) error {
+ if err := SetMtu("lo", config.Mtu); err != nil {
+ return fmt.Errorf("set lo mtu to %d %s", config.Mtu, err)
+ }
+ if err := InterfaceUp("lo"); err != nil {
+ return fmt.Errorf("lo up %s", err)
+ }
+ return nil
+}
diff --git a/pkg/libcontainer/network/strategy.go b/pkg/libcontainer/network/strategy.go
index 234fcc0aa2..693790d280 100644
--- a/pkg/libcontainer/network/strategy.go
+++ b/pkg/libcontainer/network/strategy.go
@@ -10,7 +10,8 @@ var (
)
var strategies = map[string]NetworkStrategy{
- "veth": &Veth{},
+ "veth": &Veth{},
+ "loopback": &Loopback{},
}
// NetworkStrategy represents a specific network configuration for
diff --git a/pkg/libcontainer/network/veth.go b/pkg/libcontainer/network/veth.go
index 3ab1b2393b..3df0cd61ee 100644
--- a/pkg/libcontainer/network/veth.go
+++ b/pkg/libcontainer/network/veth.go
@@ -68,12 +68,6 @@ func (v *Veth) Initialize(config *libcontainer.Network, context libcontainer.Con
if err := InterfaceUp("eth0"); err != nil {
return fmt.Errorf("eth0 up %s", err)
}
- if err := SetMtu("lo", config.Mtu); err != nil {
- return fmt.Errorf("set lo mtu to %d %s", config.Mtu, err)
- }
- if err := InterfaceUp("lo"); err != nil {
- return fmt.Errorf("lo up %s", err)
- }
if config.Gateway != "" {
if err := SetDefaultGateway(config.Gateway); err != nil {
return fmt.Errorf("set gateway to %s %s", config.Gateway, err)
diff --git a/pkg/libcontainer/nsinit/execin.go b/pkg/libcontainer/nsinit/execin.go
index 488fe0e248..628854ff32 100644
--- a/pkg/libcontainer/nsinit/execin.go
+++ b/pkg/libcontainer/nsinit/execin.go
@@ -14,9 +14,12 @@ import (
// ExecIn uses an existing pid and joins the pid's namespaces with the new command.
func (ns *linuxNs) ExecIn(container *libcontainer.Container, nspid int, args []string) (int, error) {
- for _, ns := range container.Namespaces {
- if err := system.Unshare(ns.Value); err != nil {
- return -1, err
+ for _, nsv := range container.Namespaces {
+ // skip the PID namespace on unshare because it it not supported
+ if nsv.Key != "NEWPID" {
+ if err := system.Unshare(nsv.Value); err != nil {
+ return -1, err
+ }
}
}
fds, err := ns.getNsFds(nspid, container)
diff --git a/pkg/libcontainer/nsinit/init.go b/pkg/libcontainer/nsinit/init.go
index 336fc1eaaf..09f85e2141 100644
--- a/pkg/libcontainer/nsinit/init.go
+++ b/pkg/libcontainer/nsinit/init.go
@@ -48,7 +48,9 @@ func (ns *linuxNs) Init(container *libcontainer.Container, uncleanRootfs, consol
return fmt.Errorf("setctty %s", err)
}
}
- if err := system.ParentDeathSignal(); err != nil {
+ // this is our best effort to let the process know that the parent has died and that it
+ // should it should act on it how it sees fit
+ if err := system.ParentDeathSignal(uintptr(syscall.SIGTERM)); err != nil {
return fmt.Errorf("parent death signal %s", err)
}
if err := setupNewMountNamespace(rootfs, console, container.ReadonlyFs, container.NoPivotRoot); err != nil {
@@ -124,7 +126,11 @@ func setupNetwork(container *libcontainer.Container, context libcontainer.Contex
if err != nil {
return err
}
- return strategy.Initialize(config, context)
+
+ err1 := strategy.Initialize(config, context)
+ if err1 != nil {
+ return err1
+ }
}
return nil
}
diff --git a/pkg/libcontainer/nsinit/mount.go b/pkg/libcontainer/nsinit/mount.go
index 83577cfa8c..072188ecd8 100644
--- a/pkg/libcontainer/nsinit/mount.go
+++ b/pkg/libcontainer/nsinit/mount.go
@@ -46,10 +46,8 @@ func setupNewMountNamespace(rootfs, console string, readonly, noPivotRoot bool)
if err := setupDev(rootfs); err != nil {
return err
}
- if console != "" {
- if err := setupPtmx(rootfs, console); err != nil {
- return err
- }
+ if err := setupPtmx(rootfs, console); err != nil {
+ return err
}
if err := system.Chdir(rootfs); err != nil {
return fmt.Errorf("chdir into %s %s", rootfs, err)
@@ -245,8 +243,10 @@ func setupPtmx(rootfs, console string) error {
if err := os.Symlink("pts/ptmx", ptmx); err != nil {
return fmt.Errorf("symlink dev ptmx %s", err)
}
- if err := setupConsole(rootfs, console); err != nil {
- return err
+ if console != "" {
+ if err := setupConsole(rootfs, console); err != nil {
+ return err
+ }
}
return nil
}
diff --git a/pkg/listenbuffer/buffer.go b/pkg/listenbuffer/buffer.go
index c350805a7d..17572c8a0e 100644
--- a/pkg/listenbuffer/buffer.go
+++ b/pkg/listenbuffer/buffer.go
@@ -5,15 +5,10 @@
*/
package listenbuffer
-import (
- "fmt"
- "net"
- "time"
-)
+import "net"
-// NewListenBuffer returns a listener listening on addr with the protocol. It sets the
-// timeout to wait on first connection before an error is returned
-func NewListenBuffer(proto, addr string, activate chan struct{}, timeout time.Duration) (net.Listener, error) {
+// NewListenBuffer returns a listener listening on addr with the protocol.
+func NewListenBuffer(proto, addr string, activate chan struct{}) (net.Listener, error) {
wrapped, err := net.Listen(proto, addr)
if err != nil {
return nil, err
@@ -22,7 +17,6 @@ func NewListenBuffer(proto, addr string, activate chan struct{}, timeout time.Du
return &defaultListener{
wrapped: wrapped,
activate: activate,
- timeout: timeout,
}, nil
}
@@ -30,7 +24,6 @@ type defaultListener struct {
wrapped net.Listener // the real listener to wrap
ready bool // is the listner ready to start accpeting connections
activate chan struct{}
- timeout time.Duration // how long to wait before we consider this an error
}
func (l *defaultListener) Close() error {
@@ -47,15 +40,7 @@ func (l *defaultListener) Accept() (net.Conn, error) {
if l.ready {
return l.wrapped.Accept()
}
-
- select {
- case <-time.After(l.timeout):
- // close the connection so any clients are disconnected
- l.Close()
- return nil, fmt.Errorf("timeout (%s) reached waiting for listener to become ready", l.timeout.String())
- case <-l.activate:
- l.ready = true
- return l.Accept()
- }
- panic("unreachable")
+ <-l.activate
+ l.ready = true
+ return l.Accept()
}
diff --git a/pkg/mflag/example/example.go b/pkg/mflag/example/example.go
index ed940e8d70..ce9dd30e4c 100644
--- a/pkg/mflag/example/example.go
+++ b/pkg/mflag/example/example.go
@@ -13,7 +13,8 @@ var (
func init() {
flag.Bool([]string{"#hp", "#-halp"}, false, "display the halp")
- flag.BoolVar(&b, []string{"b"}, false, "a simple bool")
+ flag.BoolVar(&b, []string{"b", "#bal", "#bol", "-bal"}, false, "a simple bool")
+ flag.BoolVar(&b, []string{"g", "#gil"}, false, "a simple bool")
flag.BoolVar(&b2, []string{"#-bool"}, false, "a simple bool")
flag.IntVar(&i, []string{"-integer", "-number"}, -1, "a simple integer")
flag.StringVar(&str, []string{"s", "#hidden", "-string"}, "", "a simple string") //-s -hidden and --string will work, but -hidden won't be in the usage
diff --git a/pkg/mflag/flag.go b/pkg/mflag/flag.go
index 7125c030ed..581041065c 100644
--- a/pkg/mflag/flag.go
+++ b/pkg/mflag/flag.go
@@ -805,9 +805,20 @@ func (f *FlagSet) parseOne() (bool, string, error) {
f.actual = make(map[string]*Flag)
}
f.actual[name] = flag
- for _, n := range flag.Names {
+ for i, n := range flag.Names {
if n == fmt.Sprintf("#%s", name) {
- fmt.Fprintf(f.out(), "Warning: '-%s' is deprecated, it will be removed soon. See usage.\n", name)
+ replacement := ""
+ for j := i; j < len(flag.Names); j++ {
+ if flag.Names[j][0] != '#' {
+ replacement = flag.Names[j]
+ break
+ }
+ }
+ if replacement != "" {
+ fmt.Fprintf(f.out(), "Warning: '-%s' is deprecated, it will be replaced by '-%s' soon. See usage.\n", name, replacement)
+ } else {
+ fmt.Fprintf(f.out(), "Warning: '-%s' is deprecated, it will be removed soon. See usage.\n", name)
+ }
}
}
return true, "", nil
diff --git a/pkg/signal/signal.go b/pkg/signal/signal.go
new file mode 100644
index 0000000000..63337542d7
--- /dev/null
+++ b/pkg/signal/signal.go
@@ -0,0 +1,19 @@
+package signal
+
+import (
+ "os"
+ "os/signal"
+)
+
+func CatchAll(sigc chan os.Signal) {
+ handledSigs := []os.Signal{}
+ for _, s := range SignalMap {
+ handledSigs = append(handledSigs, s)
+ }
+ signal.Notify(sigc, handledSigs...)
+}
+
+func StopCatch(sigc chan os.Signal) {
+ signal.Stop(sigc)
+ close(sigc)
+}
diff --git a/pkg/signal/signal_darwin.go b/pkg/signal/signal_darwin.go
new file mode 100644
index 0000000000..fcd3a8f2c9
--- /dev/null
+++ b/pkg/signal/signal_darwin.go
@@ -0,0 +1,40 @@
+package signal
+
+import (
+ "syscall"
+)
+
+var SignalMap = map[string]syscall.Signal{
+ "ABRT": syscall.SIGABRT,
+ "ALRM": syscall.SIGALRM,
+ "BUG": syscall.SIGBUS,
+ "CHLD": syscall.SIGCHLD,
+ "CONT": syscall.SIGCONT,
+ "EMT": syscall.SIGEMT,
+ "FPE": syscall.SIGFPE,
+ "HUP": syscall.SIGHUP,
+ "ILL": syscall.SIGILL,
+ "INFO": syscall.SIGINFO,
+ "INT": syscall.SIGINT,
+ "IO": syscall.SIGIO,
+ "IOT": syscall.SIGIOT,
+ "KILL": syscall.SIGKILL,
+ "PIPE": syscall.SIGPIPE,
+ "PROF": syscall.SIGPROF,
+ "QUIT": syscall.SIGQUIT,
+ "SEGV": syscall.SIGSEGV,
+ "STOP": syscall.SIGSTOP,
+ "SYS": syscall.SIGSYS,
+ "TERM": syscall.SIGTERM,
+ "TRAP": syscall.SIGTRAP,
+ "TSTP": syscall.SIGTSTP,
+ "TTIN": syscall.SIGTTIN,
+ "TTOU": syscall.SIGTTOU,
+ "URG": syscall.SIGURG,
+ "USR1": syscall.SIGUSR1,
+ "USR2": syscall.SIGUSR2,
+ "VTALRM": syscall.SIGVTALRM,
+ "WINCH": syscall.SIGWINCH,
+ "XCPU": syscall.SIGXCPU,
+ "XFSZ": syscall.SIGXFSZ,
+}
diff --git a/pkg/signal/signal_freebsd.go b/pkg/signal/signal_freebsd.go
new file mode 100644
index 0000000000..da042d7e72
--- /dev/null
+++ b/pkg/signal/signal_freebsd.go
@@ -0,0 +1,44 @@
+package signal
+
+import (
+ "os"
+ "os/signal"
+ "syscall"
+)
+
+var SignalMap = map[string]syscall.Signal{
+ "ABRT": syscall.SIGABRT,
+ "ALRM": syscall.SIGALRM,
+ "BUF": syscall.SIGBUS,
+ "CHLD": syscall.SIGCHLD,
+ "CONT": syscall.SIGCONT,
+ "EMT": syscall.SIGEMT,
+ "FPE": syscall.SIGFPE,
+ "HUP": syscall.SIGHUP,
+ "ILL": syscall.SIGILL,
+ "INFO": syscall.SIGINFO,
+ "INT": syscall.SIGINT,
+ "IO": syscall.SIGIO,
+ "IOT": syscall.SIGIOT,
+ "KILL": syscall.SIGKILL,
+ "LWP": syscall.SIGLWP,
+ "PIPE": syscall.SIGPIPE,
+ "PROF": syscall.SIGPROF,
+ "QUIT": syscall.SIGQUIT,
+ "SEGV": syscall.SIGSEGV,
+ "STOP": syscall.SIGSTOP,
+ "SYS": syscall.SIGSYS,
+ "TERM": syscall.SIGTERM,
+ "THR": syscall.SIGTHR,
+ "TRAP": syscall.SIGTRAP,
+ "TSTP": syscall.SIGTSTP,
+ "TTIN": syscall.SIGTTIN,
+ "TTOU": syscall.SIGTTOU,
+ "URG": syscall.SIGURG,
+ "USR1": syscall.SIGUSR1,
+ "USR2": syscall.SIGUSR2,
+ "VTALRM": syscall.SIGVTALRM,
+ "WINCH": syscall.SIGWINCH,
+ "XCPU": syscall.SIGXCPU,
+ "XFSZ": syscall.SIGXFSZ,
+}
diff --git a/pkg/signal/signal_linux.go b/pkg/signal/signal_linux.go
new file mode 100644
index 0000000000..a62f79d4af
--- /dev/null
+++ b/pkg/signal/signal_linux.go
@@ -0,0 +1,43 @@
+package signal
+
+import (
+ "syscall"
+)
+
+var SignalMap = map[string]syscall.Signal{
+ "ABRT": syscall.SIGABRT,
+ "ALRM": syscall.SIGALRM,
+ "BUS": syscall.SIGBUS,
+ "CHLD": syscall.SIGCHLD,
+ "CLD": syscall.SIGCLD,
+ "CONT": syscall.SIGCONT,
+ "FPE": syscall.SIGFPE,
+ "HUP": syscall.SIGHUP,
+ "ILL": syscall.SIGILL,
+ "INT": syscall.SIGINT,
+ "IO": syscall.SIGIO,
+ "IOT": syscall.SIGIOT,
+ "KILL": syscall.SIGKILL,
+ "PIPE": syscall.SIGPIPE,
+ "POLL": syscall.SIGPOLL,
+ "PROF": syscall.SIGPROF,
+ "PWR": syscall.SIGPWR,
+ "QUIT": syscall.SIGQUIT,
+ "SEGV": syscall.SIGSEGV,
+ "STKFLT": syscall.SIGSTKFLT,
+ "STOP": syscall.SIGSTOP,
+ "SYS": syscall.SIGSYS,
+ "TERM": syscall.SIGTERM,
+ "TRAP": syscall.SIGTRAP,
+ "TSTP": syscall.SIGTSTP,
+ "TTIN": syscall.SIGTTIN,
+ "TTOU": syscall.SIGTTOU,
+ "UNUSED": syscall.SIGUNUSED,
+ "URG": syscall.SIGURG,
+ "USR1": syscall.SIGUSR1,
+ "USR2": syscall.SIGUSR2,
+ "VTALRM": syscall.SIGVTALRM,
+ "WINCH": syscall.SIGWINCH,
+ "XCPU": syscall.SIGXCPU,
+ "XFSZ": syscall.SIGXFSZ,
+}
diff --git a/pkg/signal/signal_unsupported.go b/pkg/signal/signal_unsupported.go
new file mode 100644
index 0000000000..99f9465970
--- /dev/null
+++ b/pkg/signal/signal_unsupported.go
@@ -0,0 +1,9 @@
+// +build !linux,!darwin,!freebsd
+
+package signal
+
+import (
+ "syscall"
+)
+
+var SignalMap = map[string]syscall.Signal{}
diff --git a/pkg/system/calls_linux.go b/pkg/system/calls_linux.go
index bf667c535b..43c00ed554 100644
--- a/pkg/system/calls_linux.go
+++ b/pkg/system/calls_linux.go
@@ -115,8 +115,8 @@ func Mknod(path string, mode uint32, dev int) error {
return syscall.Mknod(path, mode, dev)
}
-func ParentDeathSignal() error {
- if _, _, err := syscall.RawSyscall(syscall.SYS_PRCTL, syscall.PR_SET_PDEATHSIG, uintptr(syscall.SIGKILL), 0); err != 0 {
+func ParentDeathSignal(sig uintptr) error {
+ if _, _, err := syscall.RawSyscall(syscall.SYS_PRCTL, syscall.PR_SET_PDEATHSIG, sig, 0); err != 0 {
return err
}
return nil
diff --git a/pkg/term/termios_darwin.go b/pkg/term/termios_darwin.go
index 24e79de4b2..11cd70d10b 100644
--- a/pkg/term/termios_darwin.go
+++ b/pkg/term/termios_darwin.go
@@ -9,16 +9,24 @@ const (
getTermios = syscall.TIOCGETA
setTermios = syscall.TIOCSETA
- ECHO = 0x00000008
- ONLCR = 0x2
- ISTRIP = 0x20
- INLCR = 0x40
- ISIG = 0x80
- IGNCR = 0x80
- ICANON = 0x100
- ICRNL = 0x100
- IXOFF = 0x400
- IXON = 0x200
+ IGNBRK = syscall.IGNBRK
+ PARMRK = syscall.PARMRK
+ INLCR = syscall.INLCR
+ IGNCR = syscall.IGNCR
+ ECHONL = syscall.ECHONL
+ CSIZE = syscall.CSIZE
+ ICRNL = syscall.ICRNL
+ ISTRIP = syscall.ISTRIP
+ PARENB = syscall.PARENB
+ ECHO = syscall.ECHO
+ ICANON = syscall.ICANON
+ ISIG = syscall.ISIG
+ IXON = syscall.IXON
+ BRKINT = syscall.BRKINT
+ INPCK = syscall.INPCK
+ OPOST = syscall.OPOST
+ CS8 = syscall.CS8
+ IEXTEN = syscall.IEXTEN
)
type Termios struct {
@@ -41,10 +49,13 @@ func MakeRaw(fd uintptr) (*State, error) {
}
newState := oldState.termios
- newState.Iflag &^= (ISTRIP | INLCR | IGNCR | IXON | IXOFF)
- newState.Iflag |= ICRNL
- newState.Oflag |= ONLCR
- newState.Lflag &^= (ECHO | ICANON | ISIG)
+ newState.Iflag &^= (IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL | IXON)
+ newState.Oflag &^= OPOST
+ newState.Lflag &^= (ECHO | ECHONL | ICANON | ISIG | IEXTEN)
+ newState.Cflag &^= (CSIZE | PARENB)
+ newState.Cflag |= CS8
+ newState.Cc[syscall.VMIN] = 1
+ newState.Cc[syscall.VTIME] = 0
if _, _, err := syscall.Syscall(syscall.SYS_IOCTL, fd, uintptr(setTermios), uintptr(unsafe.Pointer(&newState))); err != 0 {
return nil, err
diff --git a/server.go b/server.go
index 2c8a7ee4f8..d6513d8743 100644
--- a/server.go
+++ b/server.go
@@ -8,6 +8,7 @@ import (
"github.com/dotcloud/docker/dockerversion"
"github.com/dotcloud/docker/engine"
"github.com/dotcloud/docker/pkg/graphdb"
+ "github.com/dotcloud/docker/pkg/signal"
"github.com/dotcloud/docker/registry"
"github.com/dotcloud/docker/runconfig"
"github.com/dotcloud/docker/utils"
@@ -18,7 +19,7 @@ import (
"net/url"
"os"
"os/exec"
- "os/signal"
+ gosignal "os/signal"
"path"
"path/filepath"
"runtime"
@@ -47,7 +48,7 @@ func InitServer(job *engine.Job) engine.Status {
}
job.Logf("Setting up signal traps")
c := make(chan os.Signal, 1)
- signal.Notify(c, os.Interrupt, syscall.SIGTERM, syscall.SIGQUIT)
+ gosignal.Notify(c, os.Interrupt, syscall.SIGTERM, syscall.SIGQUIT)
go func() {
sig := <-c
log.Printf("Received signal '%v', exiting\n", sig)
@@ -122,56 +123,30 @@ func (v *simpleVersionInfo) Version() string {
// for the container to exit.
// If a signal is given, then just send it to the container and return.
func (srv *Server) ContainerKill(job *engine.Job) engine.Status {
- signalMap := map[string]syscall.Signal{
- "HUP": syscall.SIGHUP,
- "INT": syscall.SIGINT,
- "QUIT": syscall.SIGQUIT,
- "ILL": syscall.SIGILL,
- "TRAP": syscall.SIGTRAP,
- "ABRT": syscall.SIGABRT,
- "BUS": syscall.SIGBUS,
- "FPE": syscall.SIGFPE,
- "KILL": syscall.SIGKILL,
- "USR1": syscall.SIGUSR1,
- "SEGV": syscall.SIGSEGV,
- "USR2": syscall.SIGUSR2,
- "PIPE": syscall.SIGPIPE,
- "ALRM": syscall.SIGALRM,
- "TERM": syscall.SIGTERM,
- //"STKFLT": syscall.SIGSTKFLT,
- "CHLD": syscall.SIGCHLD,
- "CONT": syscall.SIGCONT,
- "STOP": syscall.SIGSTOP,
- "TSTP": syscall.SIGTSTP,
- "TTIN": syscall.SIGTTIN,
- "TTOU": syscall.SIGTTOU,
- "URG": syscall.SIGURG,
- "XCPU": syscall.SIGXCPU,
- "XFSZ": syscall.SIGXFSZ,
- "VTALRM": syscall.SIGVTALRM,
- "PROF": syscall.SIGPROF,
- "WINCH": syscall.SIGWINCH,
- "IO": syscall.SIGIO,
- //"PWR": syscall.SIGPWR,
- "SYS": syscall.SIGSYS,
- }
-
if n := len(job.Args); n < 1 || n > 2 {
return job.Errorf("Usage: %s CONTAINER [SIGNAL]", job.Name)
}
- name := job.Args[0]
- var sig uint64
+ var (
+ name = job.Args[0]
+ sig uint64
+ err error
+ )
+
+ // If we have a signal, look at it. Otherwise, do nothing
if len(job.Args) == 2 && job.Args[1] != "" {
- sig = uint64(signalMap[job.Args[1]])
- if sig == 0 {
- var err error
- // The largest legal signal is 31, so let's parse on 5 bits
- sig, err = strconv.ParseUint(job.Args[1], 10, 5)
- if err != nil {
+ // Check if we passed the singal as a number:
+ // The largest legal signal is 31, so let's parse on 5 bits
+ sig, err = strconv.ParseUint(job.Args[1], 10, 5)
+ if err != nil {
+ // The signal is not a number, treat it as a string
+ sig = uint64(signal.SignalMap[job.Args[1]])
+ if sig == 0 {
return job.Errorf("Invalid signal: %s", job.Args[1])
}
+
}
}
+
if container := srv.runtime.Get(name); container != nil {
// If no signal is passed, or SIGKILL, perform regular Kill (SIGKILL + wait())
if sig == 0 || syscall.Signal(sig) == syscall.SIGKILL {
@@ -1039,12 +1014,17 @@ func (srv *Server) ContainerCommit(job *engine.Job) engine.Status {
if container == nil {
return job.Errorf("No such container: %s", name)
}
- var config runconfig.Config
- if err := job.GetenvJson("config", &config); err != nil {
+ var config = container.Config
+ var newConfig runconfig.Config
+ if err := job.GetenvJson("config", &newConfig); err != nil {
+ return job.Error(err)
+ }
+
+ if err := runconfig.Merge(&newConfig, config); err != nil {
return job.Error(err)
}
- img, err := srv.runtime.Commit(container, job.Getenv("repo"), job.Getenv("tag"), job.Getenv("comment"), job.Getenv("author"), &config)
+ img, err := srv.runtime.Commit(container, job.Getenv("repo"), job.Getenv("tag"), job.Getenv("comment"), job.Getenv("author"), &newConfig)
if err != nil {
return job.Error(err)
}
@@ -1087,16 +1067,32 @@ func (srv *Server) pullImage(r *registry.Registry, out io.Writer, imgID, endpoin
if !srv.runtime.graph.Exists(id) {
out.Write(sf.FormatProgress(utils.TruncateID(id), "Pulling metadata", nil))
- imgJSON, imgSize, err := r.GetRemoteImageJSON(id, endpoint, token)
- if err != nil {
- out.Write(sf.FormatProgress(utils.TruncateID(id), "Error pulling dependent layers", nil))
- // FIXME: Keep going in case of error?
- return err
- }
- img, err := NewImgJSON(imgJSON)
- if err != nil {
- out.Write(sf.FormatProgress(utils.TruncateID(id), "Error pulling dependent layers", nil))
- return fmt.Errorf("Failed to parse json: %s", err)
+ var (
+ imgJSON []byte
+ imgSize int
+ err error
+ img *Image
+ )
+ retries := 5
+ for j := 1; j <= retries; j++ {
+ imgJSON, imgSize, err = r.GetRemoteImageJSON(id, endpoint, token)
+ if err != nil && j == retries {
+ out.Write(sf.FormatProgress(utils.TruncateID(id), "Error pulling dependent layers", nil))
+ return err
+ } else if err != nil {
+ time.Sleep(time.Duration(j) * 500 * time.Millisecond)
+ continue
+ }
+ img, err = NewImgJSON(imgJSON)
+ if err != nil && j == retries {
+ out.Write(sf.FormatProgress(utils.TruncateID(id), "Error pulling dependent layers", nil))
+ return fmt.Errorf("Failed to parse json: %s", err)
+ } else if err != nil {
+ time.Sleep(time.Duration(j) * 500 * time.Millisecond)
+ continue
+ } else {
+ break
+ }
}
// Get the layer
@@ -2390,7 +2386,13 @@ func (srv *Server) IsRunning() bool {
}
func (srv *Server) Close() error {
+ if srv == nil {
+ return nil
+ }
srv.SetRunning(false)
+ if srv.runtime == nil {
+ return nil
+ }
return srv.runtime.Close()
}
diff --git a/utils/jsonmessage.go b/utils/jsonmessage.go
index 9050dda746..f84cc42c78 100644
--- a/utils/jsonmessage.go
+++ b/utils/jsonmessage.go
@@ -85,7 +85,7 @@ func (jm *JSONMessage) Display(out io.Writer, isTerminal bool) error {
return jm.Error
}
var endl string
- if isTerminal {
+ if isTerminal && jm.Stream == "" {
// <ESC>[2K = erase entire current line
fmt.Fprintf(out, "%c[2K\r", 27)
endl = "\r"
diff --git a/utils/signal.go b/utils/signal.go
deleted file mode 100644
index 0cac7d113f..0000000000
--- a/utils/signal.go
+++ /dev/null
@@ -1,11 +0,0 @@
-package utils
-
-import (
- "os"
- "os/signal"
-)
-
-func StopCatch(sigc chan os.Signal) {
- signal.Stop(sigc)
- close(sigc)
-}
diff --git a/utils/signal_darwin.go b/utils/signal_darwin.go
deleted file mode 100644
index 28730db8e5..0000000000
--- a/utils/signal_darwin.go
+++ /dev/null
@@ -1,44 +0,0 @@
-package utils
-
-import (
- "os"
- "os/signal"
- "syscall"
-)
-
-func CatchAll(sigc chan os.Signal) {
- signal.Notify(sigc,
- syscall.SIGABRT,
- syscall.SIGALRM,
- syscall.SIGBUS,
- syscall.SIGCHLD,
- syscall.SIGCONT,
- syscall.SIGEMT,
- syscall.SIGFPE,
- syscall.SIGHUP,
- syscall.SIGILL,
- syscall.SIGINFO,
- syscall.SIGINT,
- syscall.SIGIO,
- syscall.SIGIOT,
- syscall.SIGKILL,
- syscall.SIGPIPE,
- syscall.SIGPROF,
- syscall.SIGQUIT,
- syscall.SIGSEGV,
- syscall.SIGSTOP,
- syscall.SIGSYS,
- syscall.SIGTERM,
- syscall.SIGTRAP,
- syscall.SIGTSTP,
- syscall.SIGTTIN,
- syscall.SIGTTOU,
- syscall.SIGURG,
- syscall.SIGUSR1,
- syscall.SIGUSR2,
- syscall.SIGVTALRM,
- syscall.SIGWINCH,
- syscall.SIGXCPU,
- syscall.SIGXFSZ,
- )
-}
diff --git a/utils/signal_linux.go b/utils/signal_linux.go
deleted file mode 100644
index 26cfd56967..0000000000
--- a/utils/signal_linux.go
+++ /dev/null
@@ -1,47 +0,0 @@
-package utils
-
-import (
- "os"
- "os/signal"
- "syscall"
-)
-
-func CatchAll(sigc chan os.Signal) {
- signal.Notify(sigc,
- syscall.SIGABRT,
- syscall.SIGALRM,
- syscall.SIGBUS,
- syscall.SIGCHLD,
- syscall.SIGCLD,
- syscall.SIGCONT,
- syscall.SIGFPE,
- syscall.SIGHUP,
- syscall.SIGILL,
- syscall.SIGINT,
- syscall.SIGIO,
- syscall.SIGIOT,
- syscall.SIGKILL,
- syscall.SIGPIPE,
- syscall.SIGPOLL,
- syscall.SIGPROF,
- syscall.SIGPWR,
- syscall.SIGQUIT,
- syscall.SIGSEGV,
- syscall.SIGSTKFLT,
- syscall.SIGSTOP,
- syscall.SIGSYS,
- syscall.SIGTERM,
- syscall.SIGTRAP,
- syscall.SIGTSTP,
- syscall.SIGTTIN,
- syscall.SIGTTOU,
- syscall.SIGUNUSED,
- syscall.SIGURG,
- syscall.SIGUSR1,
- syscall.SIGUSR2,
- syscall.SIGVTALRM,
- syscall.SIGWINCH,
- syscall.SIGXCPU,
- syscall.SIGXFSZ,
- )
-}