mirror of
https://github.com/moby/moby.git
synced 2026-01-12 03:01:38 +00:00
Compare commits
37 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
867b2a90c2 | ||
|
|
3600720a36 | ||
|
|
8e681c34af | ||
|
|
b60e316b67 | ||
|
|
a2fc45e627 | ||
|
|
3f47f23fec | ||
|
|
e5974ae0ff | ||
|
|
ce002bf8da | ||
|
|
9cad4c2a1b | ||
|
|
6056cce265 | ||
|
|
cec512ccbb | ||
|
|
d1e027291b | ||
|
|
fda2934b6d | ||
|
|
6fe06c6cc8 | ||
|
|
63596f8c5d | ||
|
|
3c9e23c297 | ||
|
|
6908e0ba1e | ||
|
|
816d55342d | ||
|
|
2463be4dea | ||
|
|
9e98ba72e1 | ||
|
|
d1f7ebda16 | ||
|
|
899f06bf79 | ||
|
|
f020f1d31e | ||
|
|
b9a0e886fc | ||
|
|
bcab23bbdc | ||
|
|
7c35bfd78e | ||
|
|
78d66068a5 | ||
|
|
ab5b715964 | ||
|
|
d8448f27c6 | ||
|
|
463cf28788 | ||
|
|
6ce9daea0e | ||
|
|
39dbaa08dc | ||
|
|
d70e20cddd | ||
|
|
2741b82cf3 | ||
|
|
70ec7c6355 | ||
|
|
0225a37951 | ||
|
|
4bb1950089 |
40
CHANGELOG.md
40
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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
10
container.go
10
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.
|
||||
|
||||
@@ -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 $?
|
||||
;;
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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/
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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":
|
||||
|
||||
@@ -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}}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
21
hack/make.sh
21
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)
|
||||
|
||||
4
hack/make/binary
Normal file → Executable file
4
hack/make/binary
Normal file → Executable file
@@ -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"
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"
|
||||
|
||||
192
hack/release.sh
192
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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
24
pkg/libcontainer/network/loopback.go
Normal file
24
pkg/libcontainer/network/loopback.go
Normal file
@@ -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
|
||||
}
|
||||
@@ -10,7 +10,8 @@ var (
|
||||
)
|
||||
|
||||
var strategies = map[string]NetworkStrategy{
|
||||
"veth": &Veth{},
|
||||
"veth": &Veth{},
|
||||
"loopback": &Loopback{},
|
||||
}
|
||||
|
||||
// NetworkStrategy represents a specific network configuration for
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
19
pkg/signal/signal.go
Normal file
19
pkg/signal/signal.go
Normal file
@@ -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)
|
||||
}
|
||||
40
pkg/signal/signal_darwin.go
Normal file
40
pkg/signal/signal_darwin.go
Normal file
@@ -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,
|
||||
}
|
||||
44
pkg/signal/signal_freebsd.go
Normal file
44
pkg/signal/signal_freebsd.go
Normal file
@@ -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,
|
||||
}
|
||||
43
pkg/signal/signal_linux.go
Normal file
43
pkg/signal/signal_linux.go
Normal file
@@ -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,
|
||||
}
|
||||
9
pkg/signal/signal_unsupported.go
Normal file
9
pkg/signal/signal_unsupported.go
Normal file
@@ -0,0 +1,9 @@
|
||||
// +build !linux,!darwin,!freebsd
|
||||
|
||||
package signal
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
)
|
||||
|
||||
var SignalMap = map[string]syscall.Signal{}
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
116
server.go
116
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)
|
||||
}
|
||||
|
||||
img, err := srv.runtime.Commit(container, job.Getenv("repo"), job.Getenv("tag"), job.Getenv("comment"), job.Getenv("author"), &config)
|
||||
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"), &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()
|
||||
}
|
||||
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"os"
|
||||
"os/signal"
|
||||
)
|
||||
|
||||
func StopCatch(sigc chan os.Signal) {
|
||||
signal.Stop(sigc)
|
||||
close(sigc)
|
||||
}
|
||||
@@ -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,
|
||||
)
|
||||
}
|
||||
@@ -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,
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user