Compare commits

...

37 Commits

Author SHA1 Message Date
unclejack 867b2a90c2 Merge pull request #4831 from unclejack/final_bump_v0.9.1
Bump to version 0.9.1
2014-03-26 00:00:37 +02:00
unclejack 3600720a36 Bump to version v0.9.1
Docker-DCO-1.1-Signed-off-by: Cristian Staretu <cristian.staretu@gmail.com> (github: unclejack)
2014-03-25 00:02:35 +02:00
Guillaume J. Charmes 8e681c34af Use BSD raw mode on darwin. Fixes nano, tmux and others
Docker-DCO-1.1-Signed-off-by: Guillaume J. Charmes <guillaume@charmes.net> (github: creack)

Conflicts:
	pkg/term/termios_freebsd.go:
	  removed changes for this file because it's not present
2014-03-24 23:47:31 +02:00
Michael Crosby b60e316b67 Only unshare the mount namespace for execin
Fixes #4728
Docker-DCO-1.1-Signed-off-by: Michael Crosby <michael@crosbymichael.com> (github: crosbymichael)

Conflicts:
	pkg/libcontainer/nsinit/execin.go:
	  ns.logger.Println("unshare namespaces")' was removed already
2014-03-24 23:47:31 +02:00
unclejack a2fc45e627 retry to retrieve metadata on failure during pull
This makes Docker retry to retrieve the JSON metadata for the layers.
Docker will make 5 attempts to retrieve the metadata before failing and
it will increase the delay between attempts after each failed attempt.

This is a backport of PR #4387. The following changes had to be made:
-				img     *image.Image
+				img     *Image

-				img, err = image.NewImgJSON(imgJSON)
+				img, err = NewImgJSON(imgJSON)

Docker-DCO-1.1-Signed-off-by: Cristian Staretu <cristian.staretu@gmail.com> (github: unclejack)
2014-03-24 23:47:31 +02:00
Tianon Gravi 3f47f23fec Fix a lot of the sha256 and md5 stuff to be more DRY and extendible, and on more things (specifically, the tgz files too)
Docker-DCO-1.1-Signed-off-by: Andrew Page <admwiggin@gmail.com> (github: tianon)
2014-03-19 22:49:49 -06:00
Sven Dowideit e5974ae0ff missed a bug
Docker-DCO-1.1-Signed-off-by: Sven Dowideit <SvenDowideit@fosiki.com> (github: SvenDowideit)
2014-03-19 18:19:57 -06:00
Sven Dowideit ce002bf8da whitespace-blind
Docker-DCO-1.1-Signed-off-by: Sven Dowideit <SvenDowideit@fosiki.com> (github: SvenDowideit)

Docker-DCO-1.1-Signed-off-by: Andrew Page <admwiggin@gmail.com> (github: tianon)
2014-03-19 17:39:13 -06:00
Sven Dowideit 9cad4c2a1b Generate md5 and sha265 hashes when building, and upload them in hack/release.sh
Docker-DCO-1.1-Signed-off-by: Sven Dowideit <SvenDowideit@home.org.au> (github: SvenDowideit)

Docker-DCO-1.1-Signed-off-by: Andrew Page <admwiggin@gmail.com> (github: tianon)
2014-03-19 17:39:06 -06:00
unclejack 6056cce265 Merge pull request #4745 from unclejack/prepare_for_bump_v0.9.1
Prepare for bump v0.9.1
2014-03-19 20:00:55 +02:00
Guillaume J. Charmes cec512ccbb Make docker use the signal pkg with strings
Docker-DCO-1.1-Signed-off-by: Guillaume J. Charmes <guillaume@charmes.net> (github: creack)
2014-03-18 20:29:37 +02:00
Guillaume J. Charmes d1e027291b Create portable signalMap
Docker-DCO-1.1-Signed-off-by: Guillaume J. Charmes <guillaume@charmes.net> (github: creack)
2014-03-18 20:29:24 +02:00
Guillaume J. Charmes fda2934b6d Move signal to pkg
Docker-DCO-1.1-Signed-off-by: Guillaume J. Charmes <guillaume@charmes.net> (github: creack)
2014-03-18 20:29:10 +02:00
Alexander Larsson 6fe06c6cc8 devmapper: Increase timeout in waitClose to 10sec
As reported in https://github.com/dotcloud/docker/issues/4389 we're
currently seeing timeouts in waitClose on some systems. We already
bumped the timeout in waitRemove() in
https://github.com/dotcloud/docker/issues/4504.

Docker-DCO-1.1-Signed-off-by: Alexander Larsson <alexl@redhat.com> (github: alexlarsson)
2014-03-18 19:15:44 +02:00
Alexander Larsson 63596f8c5d DeviceMapper: Succeed immediately when removing non-existant devices
We've seen situations where removal of "ID-init" failed during
container deletion (EBUSY), after removal of "ID" has succeeded. This
caused the container delete operation to fail, and on the next delete
attempt the removal of "ID" failed immediately with "does not exist".

Ideally we should not fail the ID-init removal, but its also non-ideal
to allow a state where the container is half-removed and we cannot
make progress deleting the container. So, we silently ignore not-exist
errors on device removal.

Docker-DCO-1.1-Signed-off-by: Alexander Larsson <alexl@redhat.com> (github: alexlarsson)
2014-03-18 18:57:31 +02:00
Vladimir Rutsky 3c9e23c297 Fix external link on security of containers
Docker-DCO-1.1-Signed-off-by: Vladimir Rutsky <altsysrq@gmail.com> (github: rutsky)
2014-03-18 18:08:59 +02:00
Victor Vieux 6908e0ba1e fix content-type detection in docker cp
Docker-DCO-1.1-Signed-off-by: Victor Vieux <victor.vieux@docker.com> (github: vieux)
2014-03-18 18:08:03 +02:00
Wes Morgan 816d55342d merge existing config when committing
Fixes #1141

Docker-DCO-1.1-Signed-off-by: Wes Morgan <cap10morgan@gmail.com> (github: cap10morgan)
2014-03-18 18:04:58 +02:00
Victor Vieux 2463be4dea fix panic in monitor
Docker-DCO-1.1-Signed-off-by: Victor Vieux <victor.vieux@docker.com> (github: vieux)
2014-03-18 17:04:56 +02:00
Isabel Jimenez 9e98ba72e1 adding configuration for timeout and disable it by default
Docker-DCO-1.1-Signed-off-by: Isabel Jimenez <contact@isabeljimenez.com> (github: jimenez)
2014-03-18 17:04:21 +02:00
Timothy Hobbs d1f7ebda16 Refactor out interface specific information from execdriver.Network
Docker-DCO-1.1-Signed-off-by: Timothy Hobbs <timothyhobbs@seznam.cz> (github: https://github.com/timthelion)
2014-03-18 16:52:57 +02:00
Timothy Hobbs 899f06bf79 Fix issue #4681 - No loopback interface within container when networking is disabled.
Docker-DCO-1.1-Signed-off-by: Timothy Hobbs <timothyhobbs@seznam.cz> (github: https://github.com/timthelion)

Remove loopback code from veth strategy

Docker-DCO-1.1-Signed-off-by: Timothy Hobbs <timothyhobbs@seznam.cz> (github: https://github.com/timthelion)

Looback strategy: Get rid of uneeded code in Create
Docker-DCO-1.1-Signed-off-by: Timothy Hobbs <timothyhobbs@seznam.cz> (github: https://github.com/timthelion)

Use append when building network strategy list

Docker-DCO-1.1-Signed-off-by: Timothy Hobbs <timothyhobbs@seznam.cz> (github: https://github.com/timthelion)

Swap loopback and veth strategies in Networks list

Docker-DCO-1.1-Signed-off-by: Timothy Hobbs <timothyhobbs@seznam.cz> (github: https://github.com/timthelion)

Revert "Swap loopback and veth strategies in Networks list"

This reverts commit 3b8b2c8454171d79bed5e9a80165172617e92fc7.

Docker-DCO-1.1-Signed-off-by: Timothy Hobbs <timothyhobbs@seznam.cz> (github: https://github.com/timthelion)

When initializing networks, only return from the loop if there is an error

Docker-DCO-1.1-Signed-off-by: Timothy Hobbs <timothyhobbs@seznam.cz> (github: https://github.com/timthelion)
2014-03-18 16:52:46 +02:00
Timothy Hobbs f020f1d31e Add failing test case for issue #4681
Add a failing test case for an issue where docker is not creating a loopback device if networking is dissabled.

Docker-DCO-1.1-Signed-off-by: Timothy Hobbs <timothyhobbs@seznam.cz> (github: https://github.com/timthelion)
2014-03-18 16:52:28 +02:00
Michael Crosby b9a0e886fc Send sigterm to child instead of sigkill
Docker-DCO-1.1-Signed-off-by: Michael Crosby <michael@crosbymichael.com> (github: crosbymichael)
2014-03-18 16:51:34 +02:00
Guillaume J. Charmes bcab23bbdc Have the exec driver and kernel version in non-debug mode in docker info
Docker-DCO-1.1-Signed-off-by: Guillaume J. Charmes <guillaume@charmes.net> (github: creack)
2014-03-18 16:50:55 +02:00
Victor Vieux 7c35bfd78e as you could have multiple messages per line with streams, don't \r
Docker-DCO-1.1-Signed-off-by: Victor Vieux <victor.vieux@docker.com> (github: vieux)
2014-03-18 16:50:19 +02:00
Michael Crosby 78d66068a5 Always symlink /dev/ptmx for libcontainer
Docker-DCO-1.1-Signed-off-by: Michael Crosby <michael@crosbymichael.com> (github: crosbymichael)
2014-03-18 16:48:57 +02:00
Guillaume J. Charmes ab5b715964 Fix issue when /etc/apparmor.d does not exists
Docker-DCO-1.1-Signed-off-by: Guillaume J. Charmes <guillaume@charmes.net> (github: creack)
2014-03-18 16:45:25 +02:00
unclejack d8448f27c6 don't leave empty cidFile behind
This makes `--cidfile` clean up empty container ID files. These are
left behind when creating the container fails.

Docker-DCO-1.1-Signed-off-by: Cristian Staretu <cristian.staretu@gmail.com> (github: unclejack)
2014-03-18 16:42:25 +02:00
Victor Vieux 463cf28788 improve deprecation message
Docker-DCO-1.1-Signed-off-by: Victor Vieux <victor.vieux@docker.com> (github: vieux)
2014-03-18 16:37:26 +02:00
Guillaume J. Charmes 6ce9daea0e Fix attach exit on darwin
Docker-DCO-1.1-Signed-off-by: Guillaume J. Charmes <guillaume@charmes.net> (github: creack)
2014-03-18 16:35:48 +02:00
Alexander Larsson 39dbaa08dc devmapper: Increase sleep times and unlock while sleeping
We've seen some cases in the wild where waiting for unmount/deactivate
of devmapper devices taking a long time (several seconds). So, we increase
the sleeps to 10 seconds before we timeout. For instance:

https://github.com/dotcloud/docker/issues/4389

But, in order to not keep other processes blocked we unlock the global
dm lock while waiting to allow other devices to continue working.

Docker-DCO-1.1-Signed-off-by: Alexander Larsson <alexl@redhat.com> (github: alexlarsson)
2014-03-18 16:30:53 +02:00
Alexander Larsson d70e20cddd devmapper: Add per-device lock
We currently use a global lock to protect global data (like the
Devices map) as well as device data itself and access to
(non-threadsafe) libdevmapper.

This commit also adds a per-device lock, which will allow per-device
operations to temporarily release the global lock while e.g. waiting.
The per-device lock will make sure that nothing else accesses that
device while we're operating on it.

Docker-DCO-1.1-Signed-off-by: Alexander Larsson <alexl@redhat.com> (github: alexlarsson)
2014-03-18 16:30:28 +02:00
Tianon Gravi 2741b82cf3 Add variable for DOCKER_LOGFILE to sysvinit and use append instead of overwrite in opening the logfile
Docker-DCO-1.1-Signed-off-by: Andrew Page <admwiggin@gmail.com> (github: tianon)
2014-03-11 23:47:15 -06:00
Tianon Gravi 70ec7c6355 Fix init script cgroup mounting workarounds to be more similar to cgroupfs-mount and thus work properly
Docker-DCO-1.1-Signed-off-by: Andrew Page <admwiggin@gmail.com> (github: tianon)
2014-03-11 23:47:10 -06:00
Guillaume J. Charmes 0225a37951 Update parseLxcInfo to comply with new lxc1.0 format
Docker-DCO-1.1-Signed-off-by: Guillaume J. Charmes <guillaume@charmes.net> (github: creack)
2014-03-11 23:46:43 -06:00
Guillaume J. Charmes 4bb1950089 Remove goroutine leak upon error
Docker-DCO-1.1-Signed-off-by: Guillaume J. Charmes <guillaume@charmes.net> (github: creack)
2014-03-11 23:46:36 -06:00
47 changed files with 901 additions and 407 deletions
+40
View File
@@ -1,5 +1,45 @@
# Changelog # 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) ## 0.9.0 (2014-03-10)
#### Builder #### Builder
+1 -1
View File
@@ -1 +1 @@
0.9.0 0.9.1
+45 -14
View File
@@ -13,6 +13,7 @@ import (
"github.com/dotcloud/docker/engine" "github.com/dotcloud/docker/engine"
"github.com/dotcloud/docker/nat" "github.com/dotcloud/docker/nat"
flag "github.com/dotcloud/docker/pkg/mflag" flag "github.com/dotcloud/docker/pkg/mflag"
"github.com/dotcloud/docker/pkg/signal"
"github.com/dotcloud/docker/pkg/term" "github.com/dotcloud/docker/pkg/term"
"github.com/dotcloud/docker/registry" "github.com/dotcloud/docker/registry"
"github.com/dotcloud/docker/runconfig" "github.com/dotcloud/docker/runconfig"
@@ -24,11 +25,11 @@ import (
"net/http/httputil" "net/http/httputil"
"net/url" "net/url"
"os" "os"
"os/signal" gosignal "os/signal"
"path" "path"
"reflect" "reflect"
"regexp" "regexp"
"runtime" goruntime "runtime"
"strconv" "strconv"
"strings" "strings"
"syscall" "syscall"
@@ -367,7 +368,7 @@ func (cli *DockerCli) CmdVersion(args ...string) error {
if dockerversion.VERSION != "" { if dockerversion.VERSION != "" {
fmt.Fprintf(cli.out, "Client version: %s\n", 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 != "" { if dockerversion.GITCOMMIT != "" {
fmt.Fprintf(cli.out, "Git commit (client): %s\n", 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, "Containers: %d\n", remoteInfo.GetInt("Containers"))
fmt.Fprintf(cli.out, "Images: %d\n", remoteInfo.GetInt("Images")) 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 var driverStatus [][2]string
if err := remoteInfo.GetJson("DriverStatus", &driverStatus); err != nil { if err := remoteInfo.GetJson("DriverStatus", &driverStatus); err != nil {
return err return err
@@ -440,14 +441,15 @@ func (cli *DockerCli) CmdInfo(args ...string) error {
for _, pair := range driverStatus { for _, pair := range driverStatus {
fmt.Fprintf(cli.out, " %s: %s\n", pair[0], pair[1]) 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") != "" { 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 (server): %v\n", remoteInfo.GetBool("Debug"))
fmt.Fprintf(cli.out, "Debug mode (client): %v\n", os.Getenv("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, "Fds: %d\n", remoteInfo.GetInt("NFd"))
fmt.Fprintf(cli.out, "Goroutines: %d\n", remoteInfo.GetInt("NGoroutines")) 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, "EventsListeners: %d\n", remoteInfo.GetInt("NEventsListener"))
fmt.Fprintf(cli.out, "Kernel Version: %s\n", remoteInfo.Get("KernelVersion"))
if initSha1 := remoteInfo.Get("InitSha1"); initSha1 != "" { if initSha1 := remoteInfo.Get("InitSha1"); initSha1 != "" {
fmt.Fprintf(cli.out, "Init SHA1: %s\n", 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 { func (cli *DockerCli) forwardAllSignals(cid string) chan os.Signal {
sigc := make(chan os.Signal, 1) sigc := make(chan os.Signal, 1)
utils.CatchAll(sigc) signal.CatchAll(sigc)
go func() { go func() {
for s := range sigc { for s := range sigc {
if s == syscall.SIGCHLD { if s == syscall.SIGCHLD {
continue 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) utils.Debugf("Error sending signal: %s", err)
} }
} }
@@ -581,7 +593,7 @@ func (cli *DockerCli) CmdStart(args ...string) error {
if !container.Config.Tty { if !container.Config.Tty {
sigc := cli.forwardAllSignals(cmd.Arg(0)) sigc := cli.forwardAllSignals(cmd.Arg(0))
defer utils.StopCatch(sigc) defer signal.StopCatch(sigc)
} }
var in io.ReadCloser var in io.ReadCloser
@@ -1614,7 +1626,7 @@ func (cli *DockerCli) CmdAttach(args ...string) error {
if *proxy && !container.Config.Tty { if *proxy && !container.Config.Tty {
sigc := cli.forwardAllSignals(cmd.Arg(0)) 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 { 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 { if containerIDFile, err = os.Create(hostConfig.ContainerIDFile); err != nil {
return fmt.Errorf("Failed to create the container ID file: %s", err) 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{} containerValues := url.Values{}
@@ -1818,7 +1844,7 @@ func (cli *DockerCli) CmdRun(args ...string) error {
if sigProxy { if sigProxy {
sigc := cli.forwardAllSignals(runResult.Get("Id")) sigc := cli.forwardAllSignals(runResult.Get("Id"))
defer utils.StopCatch(sigc) defer signal.StopCatch(sigc)
} }
var ( var (
@@ -2239,7 +2265,12 @@ func (cli *DockerCli) hijack(method, path string, setRawTerminal bool, in io.Rea
if setRawTerminal && cli.isTerminal { if setRawTerminal && cli.isTerminal {
term.RestoreTerminal(cli.terminalFd, oldState) 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) cli.resizeTty(id)
sigchan := make(chan os.Signal, 1) sigchan := make(chan os.Signal, 1)
signal.Notify(sigchan, syscall.SIGWINCH) gosignal.Notify(sigchan, syscall.SIGWINCH)
go func() { go func() {
for _ = range sigchan { for _ = range sigchan {
cli.resizeTty(id) cli.resizeTty(id)
+3 -4
View File
@@ -26,7 +26,6 @@ import (
"strconv" "strconv"
"strings" "strings"
"syscall" "syscall"
"time"
) )
var ( var (
@@ -883,7 +882,7 @@ func postContainersCopy(eng *engine.Engine, version version.Version, w http.Resp
var copyData engine.Env 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 { if err := copyData.Decode(r.Body); err != nil {
return err 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 // ListenAndServe sets up the required http.Server and gets it listening for
// each addr passed in and does protocol specific checking. // 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) r, err := createRouter(eng, logging, enableCors, dockerVersion)
if err != nil { 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 { if err != nil {
return err return err
} }
+7 -3
View File
@@ -363,14 +363,18 @@ func populateCommand(c *Container) {
driverConfig []string driverConfig []string
) )
en = &execdriver.Network{
Mtu: c.runtime.config.Mtu,
Interface: nil,
}
if !c.Config.NetworkDisabled { if !c.Config.NetworkDisabled {
network := c.NetworkSettings network := c.NetworkSettings
en = &execdriver.Network{ en.Interface = &execdriver.NetworkInterface{
Gateway: network.Gateway, Gateway: network.Gateway,
Bridge: network.Bridge, Bridge: network.Bridge,
IPAddress: network.IPAddress, IPAddress: network.IPAddress,
IPPrefixLen: network.IPPrefixLen, 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) 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) container.State.SetStopped(exitCode)
// FIXME: there is a race condition here which causes this to fail during the unit tests. // FIXME: there is a race condition here which causes this to fail during the unit tests.
+28 -13
View File
@@ -21,6 +21,7 @@ BASE=$(basename $0)
# modify these in /etc/default/$BASE (/etc/default/docker) # modify these in /etc/default/$BASE (/etc/default/docker)
DOCKER=/usr/bin/$BASE DOCKER=/usr/bin/$BASE
DOCKER_PIDFILE=/var/run/$BASE.pid DOCKER_PIDFILE=/var/run/$BASE.pid
DOCKER_LOGFILE=/var/log/$BASE.log
DOCKER_OPTS= DOCKER_OPTS=
DOCKER_DESC="Docker" DOCKER_DESC="Docker"
@@ -50,23 +51,37 @@ fail_unless_root() {
fi 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 case "$1" in
start) start)
fail_unless_root fail_unless_root
if ! grep -q cgroup /proc/mounts; then cgroupfs_mount
# 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
touch /var/log/docker.log touch "$DOCKER_LOGFILE"
chgrp docker /var/log/docker.log chgrp docker "$DOCKER_LOGFILE"
log_begin_msg "Starting $DOCKER_DESC: $BASE" log_begin_msg "Starting $DOCKER_DESC: $BASE"
start-stop-daemon --start --background \ start-stop-daemon --start --background \
@@ -76,7 +91,7 @@ case "$1" in
-- \ -- \
-d -p "$DOCKER_PIDFILE" \ -d -p "$DOCKER_PIDFILE" \
$DOCKER_OPTS \ $DOCKER_OPTS \
> /var/log/docker.log 2>&1 >> "$DOCKER_LOGFILE" 2>&1
log_end_msg $? log_end_msg $?
;; ;;
+23 -10
View File
@@ -5,6 +5,29 @@ stop on runlevel [!2345]
respawn 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 script
# modify these in /etc/default/$UPSTART_JOB (/etc/default/docker) # modify these in /etc/default/$UPSTART_JOB (/etc/default/docker)
DOCKER=/usr/bin/$UPSTART_JOB DOCKER=/usr/bin/$UPSTART_JOB
@@ -12,15 +35,5 @@ script
if [ -f /etc/default/$UPSTART_JOB ]; then if [ -f /etc/default/$UPSTART_JOB ]; then
. /etc/default/$UPSTART_JOB . /etc/default/$UPSTART_JOB
fi 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 "$DOCKER" -d $DOCKER_OPTS
end script end script
+2 -2
View File
@@ -7,7 +7,7 @@
Docker Security 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: 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 For more context and especially for comparisons with VMs and other
container systems, please also see the `original blog post container systems, please also see the `original blog post
<blogsecurity>`_. <blogsecurity_>`_.
.. _blogsecurity: http://blog.docker.io/2013/08/containers-docker-how-secure-are-they/ .. _blogsecurity: http://blog.docker.io/2013/08/containers-docker-how-secure-are-they/
+31 -2
View File
@@ -303,7 +303,7 @@ by using the ``git://`` schema.
-m, --message="": Commit message -m, --message="": Commit message
-a, --author="": Author (eg. "John Hannibal Smith <hannibal@a-team.com>" -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"]}') (ex: -run='{"Cmd": ["cat", "/world"], "PortSpecs": ["22"]}')
.. _cli_commit_examples: .. _cli_commit_examples:
@@ -344,11 +344,40 @@ run ``ls /etc``.
apt host.conf lsb-base rc2.d 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 Full -run example
................. .................
The ``--run`` JSON hash changes the ``Config`` section when running ``docker inspect CONTAINERID`` 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 ``'``) (Multiline is okay within a single quote ``'``)
+7 -3
View File
@@ -84,11 +84,15 @@ type Driver interface {
// Network settings of the container // Network settings of the container
type Network struct { 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"` Gateway string `json:"gateway"`
IPAddress string `json:"ip"` IPAddress string `json:"ip"`
Bridge string `json:"bridge"` Bridge string `json:"bridge"`
IPPrefixLen int `json:"ip_prefix_len"` IPPrefixLen int `json:"ip_prefix_len"`
Mtu int `json:"mtu"`
} }
type Resources struct { type Resources struct {
@@ -111,8 +115,8 @@ type Command struct {
WorkingDir string `json:"working_dir"` 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 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"` Tty bool `json:"tty"`
Network *Network `json:"network"` // if network is nil then networking is disabled Network *Network `json:"network"`
Config []string `json:"config"` // generic values that specific drivers can consume Config []string `json:"config"` // generic values that specific drivers can consume
Resources *Resources `json:"resources"` Resources *Resources `json:"resources"`
Terminal Terminal `json:"-"` // standard or tty terminal Terminal Terminal `json:"-"` // standard or tty terminal
+9 -4
View File
@@ -94,13 +94,15 @@ func (d *driver) Run(c *execdriver.Command, pipes *execdriver.Pipes, startCallba
DriverName, DriverName,
} }
if c.Network != nil { if c.Network.Interface != nil {
params = append(params, params = append(params,
"-g", c.Network.Gateway, "-g", c.Network.Interface.Gateway,
"-i", fmt.Sprintf("%s/%d", c.Network.IPAddress, c.Network.IPPrefixLen), "-i", fmt.Sprintf("%s/%d", c.Network.Interface.IPAddress, c.Network.Interface.IPPrefixLen),
"-mtu", strconv.Itoa(c.Network.Mtu),
) )
} }
params = append(params,
"-mtu", strconv.Itoa(c.Network.Mtu),
)
if c.User != "" { if c.User != "" {
params = append(params, "-u", 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 // Poll lxc for RUNNING status
pid, err := d.waitForStart(c, waitLock) pid, err := d.waitForStart(c, waitLock)
if err != nil { if err != nil {
if c.Process != nil {
c.Process.Kill()
}
return -1, err return -1, err
} }
c.ContainerPid = pid c.ContainerPid = pid
+1 -1
View File
@@ -36,7 +36,7 @@ func parseLxcInfo(raw string) (*lxcInfo, error) {
if len(parts) < 2 { if len(parts) < 2 {
continue continue
} }
switch strings.TrimSpace(parts[0]) { switch strings.ToLower(strings.TrimSpace(parts[0])) {
case "state": case "state":
info.Running = strings.TrimSpace(parts[1]) == "RUNNING" info.Running = strings.TrimSpace(parts[1]) == "RUNNING"
case "pid": case "pid":
+3 -3
View File
@@ -7,17 +7,17 @@ import (
) )
const LxcTemplate = ` const LxcTemplate = `
{{if .Network}} {{if .Network.Interface}}
# network configuration # network configuration
lxc.network.type = veth lxc.network.type = veth
lxc.network.link = {{.Network.Bridge}} lxc.network.link = {{.Network.Interface.Bridge}}
lxc.network.name = eth0 lxc.network.name = eth0
lxc.network.mtu = {{.Network.Mtu}}
{{else}} {{else}}
# network is disabled (-n=false) # network is disabled (-n=false)
lxc.network.type = empty lxc.network.type = empty
lxc.network.flags = up lxc.network.flags = up
{{end}} {{end}}
lxc.network.mtu = {{.Network.Mtu}}
# root filesystem # root filesystem
{{$ROOTFS := .Rootfs}} {{$ROOTFS := .Rootfs}}
+8
View File
@@ -43,6 +43,10 @@ func TestLXCConfig(t *testing.T) {
Memory: int64(mem), Memory: int64(mem),
CpuShares: int64(cpu), CpuShares: int64(cpu),
}, },
Network: &execdriver.Network{
Mtu: 1500,
Interface: nil,
},
} }
p, err := driver.generateLXCConfig(command) p, err := driver.generateLXCConfig(command)
if err != nil { if err != nil {
@@ -75,6 +79,10 @@ func TestCustomLxcConfig(t *testing.T) {
"lxc.utsname = docker", "lxc.utsname = docker",
"lxc.cgroup.cpuset.cpus = 0,1", "lxc.cgroup.cpuset.cpus = 0,1",
}, },
Network: &execdriver.Network{
Mtu: 1500,
Interface: nil,
},
} }
p, err := driver.generateLXCConfig(command) p, err := driver.generateLXCConfig(command)
+22 -11
View File
@@ -19,19 +19,30 @@ func createContainer(c *execdriver.Command) *libcontainer.Container {
container.WorkingDir = c.WorkingDir container.WorkingDir = c.WorkingDir
container.Env = c.Env container.Env = c.Env
if c.Network != nil { loopbackNetwork := libcontainer.Network{
container.Networks = []*libcontainer.Network{ Mtu: c.Network.Mtu,
{ Address: fmt.Sprintf("%s/%d", "127.0.0.1", 0),
Mtu: c.Network.Mtu, Gateway: "localhost",
Address: fmt.Sprintf("%s/%d", c.Network.IPAddress, c.Network.IPPrefixLen), Type: "loopback",
Gateway: c.Network.Gateway, Context: libcontainer.Context{},
Type: "veth", }
Context: libcontainer.Context{
"prefix": "veth", container.Networks = []*libcontainer.Network{
"bridge": c.Network.Bridge, &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 container.Cgroups.Name = c.ID
+50 -7
View File
@@ -39,6 +39,13 @@ type DevInfo struct {
// first get (since we need to mount to set up the device // first get (since we need to mount to set up the device
// a bit first). // a bit first).
floating bool `json:"-"` 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 { type MetaData struct {
@@ -47,7 +54,7 @@ type MetaData struct {
type DeviceSet struct { type DeviceSet struct {
MetaData MetaData
sync.Mutex sync.Mutex // Protects Devices map and serializes calls into libdevmapper
root string root string
devicePrefix string devicePrefix string
TransactionId uint64 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) 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() deviceId := devices.allocateDeviceId()
if err := devices.createSnapDevice(devices.getPoolDevName(), deviceId, baseInfo.Name(), baseInfo.DeviceId); err != nil { 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() devices.Lock()
defer devices.Unlock() 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) return devices.deleteDevice(hash)
} }
@@ -683,7 +701,7 @@ func (devices *DeviceSet) deactivateDevice(hash string) error {
func (devices *DeviceSet) removeDeviceAndWait(devname string) error { func (devices *DeviceSet) removeDeviceAndWait(devname string) error {
var err error var err error
for i := 0; i < 10; i++ { for i := 0; i < 1000; i++ {
devices.sawBusy = false devices.sawBusy = false
err = removeDevice(devname) err = removeDevice(devname)
if err == nil { if err == nil {
@@ -695,7 +713,9 @@ func (devices *DeviceSet) removeDeviceAndWait(devname string) error {
// If we see EBUSY it may be a transient error, // If we see EBUSY it may be a transient error,
// sleep a bit a retry a few times. // 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 { if err != nil {
return err return err
@@ -709,7 +729,7 @@ func (devices *DeviceSet) removeDeviceAndWait(devname string) error {
// waitRemove blocks until either: // waitRemove blocks until either:
// a) the device registered at <device_set_prefix>-<hash> is removed, // 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 { func (devices *DeviceSet) waitRemove(devname string) error {
utils.Debugf("[deviceset %s] waitRemove(%s)", devices.devicePrefix, devname) utils.Debugf("[deviceset %s] waitRemove(%s)", devices.devicePrefix, devname)
defer utils.Debugf("[deviceset %s] waitRemove(%s) END", 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 break
} }
time.Sleep(1 * time.Millisecond) devices.Unlock()
time.Sleep(10 * time.Millisecond)
devices.Lock()
} }
if i == 1000 { if i == 1000 {
return fmt.Errorf("Timeout while waiting for device %s to be removed", devname) 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: // waitClose blocks until either:
// a) the device registered at <device_set_prefix>-<hash> is closed, // 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 { func (devices *DeviceSet) waitClose(hash string) error {
info := devices.Devices[hash] info := devices.Devices[hash]
if info == nil { if info == nil {
@@ -756,7 +778,9 @@ func (devices *DeviceSet) waitClose(hash string) error {
if devinfo.OpenCount == 0 { if devinfo.OpenCount == 0 {
break break
} }
time.Sleep(1 * time.Millisecond) devices.Unlock()
time.Sleep(10 * time.Millisecond)
devices.Lock()
} }
if i == 1000 { if i == 1000 {
return fmt.Errorf("Timeout while waiting for device %s to close", hash) 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) defer utils.Debugf("[deviceset %s] shutdown END", devices.devicePrefix)
for _, info := range devices.Devices { for _, info := range devices.Devices {
info.lock.Lock()
if info.mountCount > 0 { if info.mountCount > 0 {
if err := sysUnmount(info.mountPath, 0); err != nil { if err := sysUnmount(info.mountPath, 0); err != nil {
utils.Debugf("Shutdown unmounting %s, error: %s\n", info.mountPath, err) utils.Debugf("Shutdown unmounting %s, error: %s\n", info.mountPath, err)
} }
} }
info.lock.Unlock()
} }
for _, d := range devices.Devices { for _, d := range devices.Devices {
d.lock.Lock()
if err := devices.waitClose(d.Hash); err != nil { if err := devices.waitClose(d.Hash); err != nil {
utils.Errorf("Warning: error waiting for device %s to unmount: %s\n", d.Hash, err) utils.Errorf("Warning: error waiting for device %s to unmount: %s\n", d.Hash, err)
} }
if err := devices.deactivateDevice(d.Hash); err != nil { if err := devices.deactivateDevice(d.Hash); err != nil {
utils.Debugf("Shutdown deactivate %s , error: %s\n", d.Hash, err) utils.Debugf("Shutdown deactivate %s , error: %s\n", d.Hash, err)
} }
d.lock.Unlock()
} }
if err := devices.deactivatePool(); err != nil { 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) return fmt.Errorf("Unknown device %s", hash)
} }
info.lock.Lock()
defer info.lock.Unlock()
if info.mountCount > 0 { if info.mountCount > 0 {
if path != info.mountPath { if path != info.mountPath {
return fmt.Errorf("Trying to mount devmapper device in multple places (%s, %s)", info.mountPath, path) 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) return fmt.Errorf("UnmountDevice: no such device %s\n", hash)
} }
info.lock.Lock()
defer info.lock.Unlock()
if mode == UnmountFloat { if mode == UnmountFloat {
if info.floating { if info.floating {
return fmt.Errorf("UnmountDevice: can't float floating reference %s\n", hash) 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 { if info == nil {
return false return false
} }
info.lock.Lock()
defer info.lock.Unlock()
devinfo, _ := getInfo(info.Name()) devinfo, _ := getInfo(info.Name())
return devinfo != nil && devinfo.Exists != 0 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) return nil, fmt.Errorf("No device %s", hash)
} }
info.lock.Lock()
defer info.lock.Unlock()
status := &DevStatus{ status := &DevStatus{
DeviceId: info.DeviceId, DeviceId: info.DeviceId,
Size: info.Size, Size: info.Size,
+7
View File
@@ -90,6 +90,13 @@ func (d *Driver) Create(id, parent string) error {
} }
func (d *Driver) Remove(id 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 // Sink the float from create in case no Get() call was made
if err := d.DeviceSet.UnmountDevice(id, UnmountSink); err != nil { if err := d.DeviceSet.UnmountDevice(id, UnmountSink); err != nil {
return err return err
+21
View File
@@ -141,6 +141,27 @@ find_dirs() {
\) -name "$1" -print0 | xargs -0n1 dirname | sort -u \) -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() { bundle() {
bundlescript=$1 bundlescript=$1
bundle=$(basename $bundlescript) bundle=$(basename $bundlescript)
Regular → Executable
+3 -1
View File
@@ -3,7 +3,7 @@
DEST=$1 DEST=$1
go build \ go build \
-o $DEST/docker-$VERSION \ -o "$DEST/docker-$VERSION" \
"${BUILDFLAGS[@]}" \ "${BUILDFLAGS[@]}" \
-ldflags " -ldflags "
$LDFLAGS $LDFLAGS
@@ -11,3 +11,5 @@ go build \
" \ " \
./docker ./docker
echo "Created binary: $DEST/docker-$VERSION" echo "Created binary: $DEST/docker-$VERSION"
hash_files "$DEST/docker-$VERSION"
+4 -2
View File
@@ -5,7 +5,7 @@ DEST=$1
if [ -z "$DOCKER_CLIENTONLY" ]; then if [ -z "$DOCKER_CLIENTONLY" ]; then
# dockerinit still needs to be a static binary, even if docker is dynamic # dockerinit still needs to be a static binary, even if docker is dynamic
go build \ go build \
-o $DEST/dockerinit-$VERSION \ -o "$DEST/dockerinit-$VERSION" \
"${BUILDFLAGS[@]}" \ "${BUILDFLAGS[@]}" \
-ldflags " -ldflags "
$LDFLAGS $LDFLAGS
@@ -14,7 +14,9 @@ if [ -z "$DOCKER_CLIENTONLY" ]; then
" \ " \
./dockerinit ./dockerinit
echo "Created binary: $DEST/dockerinit-$VERSION" echo "Created binary: $DEST/dockerinit-$VERSION"
ln -sf dockerinit-$VERSION $DEST/dockerinit ln -sf "dockerinit-$VERSION" "$DEST/dockerinit"
hash_files "$DEST/dockerinit-$VERSION"
sha1sum= sha1sum=
if command -v sha1sum &> /dev/null; then if command -v sha1sum &> /dev/null; then
+2
View File
@@ -23,6 +23,8 @@ for d in "$CROSS/"*/*; do
tar --numeric-owner --owner 0 -C "$DEST/build" -czf "$TGZ" usr tar --numeric-owner --owner 0 -C "$DEST/build" -czf "$TGZ" usr
hash_files "$TGZ"
rm -rf "$DEST/build" rm -rf "$DEST/build"
echo "Created tgz: $TGZ" echo "Created tgz: $TGZ"
+120 -70
View File
@@ -56,32 +56,15 @@ if [ "$1" != '--release-regardless-of-test-failure' ]; then
RELEASE_BUNDLES=( test "${RELEASE_BUNDLES[@]}" ) RELEASE_BUNDLES=( test "${RELEASE_BUNDLES[@]}" )
fi 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) VERSION=$(cat VERSION)
BUCKET=$AWS_S3_BUCKET 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() { setup_s3() {
# Try creating the bucket. Ignore errors (it might already exist). # Try creating the bucket. Ignore errors (it might already exist).
s3cmd mb s3://$BUCKET 2>/dev/null || true s3cmd mb s3://$BUCKET 2>/dev/null || true
@@ -114,76 +97,138 @@ s3_url() {
esac 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() { release_build() {
GOOS=$1 GOOS=$1
GOARCH=$2 GOARCH=$2
BINARY=bundles/$VERSION/cross/$GOOS/$GOARCH/docker-$VERSION binDir=bundles/$VERSION/cross/$GOOS/$GOARCH
TGZ=bundles/$VERSION/tgz/$GOOS/$GOARCH/docker-$VERSION.tgz 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 # we need to map our GOOS and GOARCH to uname values
# see https://en.wikipedia.org/wiki/Uname # see https://en.wikipedia.org/wiki/Uname
# ie, GOOS=linux -> "uname -s"=Linux # ie, GOOS=linux -> "uname -s"=Linux
S3OS=$GOOS s3Os=$GOOS
case "$S3OS" in case "$s3Os" in
darwin) darwin)
S3OS=Darwin s3Os=Darwin
;; ;;
freebsd) freebsd)
S3OS=FreeBSD s3Os=FreeBSD
;; ;;
linux) 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 exit 1
;; ;;
esac esac
S3ARCH=$GOARCH s3Arch=$GOARCH
case "$S3ARCH" in case "$s3Arch" in
amd64) amd64)
S3ARCH=x86_64 s3Arch=x86_64
;; ;;
386) 386)
S3ARCH=i386 s3Arch=i386
;; ;;
arm) arm)
S3ARCH=armel s3Arch=armel
# someday, we might potentially support mutliple GOARM values, in which case we might get armhf here too # 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 exit 1
;; ;;
esac 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 if [ ! -x "$binDir/$binary" ]; then
echo >&2 "error: can't find $BINARY - was it compiled properly?" echo >&2 "error: can't find $binDir/$binary - was it compiled properly?"
exit 1 exit 1
fi fi
if [ ! -f "$TGZ" ]; then if [ ! -f "$tgzDir/$tgz" ]; then
echo >&2 "error: can't find $TGZ - was it packaged properly?" echo >&2 "error: can't find $tgzDir/$tgz - was it packaged properly?"
exit 1 exit 1
fi fi
echo "Uploading $BINARY to $S3OS/$S3ARCH/docker-$VERSION" upload_release_build "$binDir/$binary" "$s3Dir/$binary" "$latest"
s3cmd --follow-symlinks --preserve --acl-public put $BINARY $S3DIR/docker-$VERSION upload_release_build "$tgzDir/$tgz" "$s3Dir/$tgz" "$latestTgz"
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 the 'ubuntu' bundle to S3: # Upload the 'ubuntu' bundle to S3:
@@ -194,21 +239,6 @@ release_ubuntu() {
echo >&2 './hack/make.sh must be run before release_ubuntu' echo >&2 './hack/make.sh must be run before release_ubuntu'
exit 1 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 # Sign our packages
dpkg-sig -g "--passphrase $GPG_PASSPHRASE" -k releasedocker \ dpkg-sig -g "--passphrase $GPG_PASSPHRASE" -k releasedocker \
@@ -305,8 +335,28 @@ release_test() {
fi 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() { main() {
build_all
setup_s3 setup_s3
setup_gpg
release_binaries release_binaries
release_ubuntu release_ubuntu
release_index release_index
+30 -1
View File
@@ -930,7 +930,7 @@ run [ "$(ls -d /var/run/sshd)" = "/var/run/sshd" ]
// #2098 - Docker cidFiles only contain short version of the containerId // #2098 - Docker cidFiles only contain short version of the containerId
//sudo docker run --cidfile /tmp/docker_test.cid ubuntu echo "test" //sudo docker run --cidfile /tmp/docker_test.cid ubuntu echo "test"
// TestRunCidFile tests that run --cidfile returns the longid // TestRunCidFile tests that run --cidfile returns the longid
func TestRunCidFile(t *testing.T) { func TestRunCidFileCheckIDLength(t *testing.T) {
stdout, stdoutPipe := io.Pipe() stdout, stdoutPipe := io.Pipe()
tmpDir, err := ioutil.TempDir("", "TestRunCidFile") 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) { func TestContainerOrphaning(t *testing.T) {
// setup a temporary directory // setup a temporary directory
+47 -22
View File
@@ -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) { func TestKillDifferentUser(t *testing.T) {
runtime := mkRuntime(t) runtime := mkRuntime(t)
defer nuke(runtime) 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) { func TestOnlyLoopbackExistsWhenUsingDisableNetworkOption(t *testing.T) {
eng := NewTestEngine(t) eng := NewTestEngine(t)
runtime := mkRuntimeFromEngine(eng, t) runtime := mkRuntimeFromEngine(eng, t)
+57
View File
@@ -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) { func TestRestartKillWait(t *testing.T) {
eng := NewTestEngine(t) eng := NewTestEngine(t)
srv := mkServerFromEngine(eng, t) srv := mkServerFromEngine(eng, t)
+6
View File
@@ -5,6 +5,7 @@ import (
"io/ioutil" "io/ioutil"
"os" "os"
"os/exec" "os/exec"
"path"
) )
const DefaultProfilePath = "/etc/apparmor.d/docker" const DefaultProfilePath = "/etc/apparmor.d/docker"
@@ -85,6 +86,11 @@ func InstallDefaultProfile() error {
return nil 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 { if err := ioutil.WriteFile(DefaultProfilePath, []byte(DefaultProfile), 0644); err != nil {
return err return err
} }
+24
View 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
}
+2 -1
View File
@@ -10,7 +10,8 @@ var (
) )
var strategies = map[string]NetworkStrategy{ var strategies = map[string]NetworkStrategy{
"veth": &Veth{}, "veth": &Veth{},
"loopback": &Loopback{},
} }
// NetworkStrategy represents a specific network configuration for // NetworkStrategy represents a specific network configuration for
-6
View File
@@ -68,12 +68,6 @@ func (v *Veth) Initialize(config *libcontainer.Network, context libcontainer.Con
if err := InterfaceUp("eth0"); err != nil { if err := InterfaceUp("eth0"); err != nil {
return fmt.Errorf("eth0 up %s", err) 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 config.Gateway != "" {
if err := SetDefaultGateway(config.Gateway); err != nil { if err := SetDefaultGateway(config.Gateway); err != nil {
return fmt.Errorf("set gateway to %s %s", config.Gateway, err) return fmt.Errorf("set gateway to %s %s", config.Gateway, err)
+6 -3
View File
@@ -14,9 +14,12 @@ import (
// ExecIn uses an existing pid and joins the pid's namespaces with the new command. // 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) { func (ns *linuxNs) ExecIn(container *libcontainer.Container, nspid int, args []string) (int, error) {
for _, ns := range container.Namespaces { for _, nsv := range container.Namespaces {
if err := system.Unshare(ns.Value); err != nil { // skip the PID namespace on unshare because it it not supported
return -1, err if nsv.Key != "NEWPID" {
if err := system.Unshare(nsv.Value); err != nil {
return -1, err
}
} }
} }
fds, err := ns.getNsFds(nspid, container) fds, err := ns.getNsFds(nspid, container)
+8 -2
View File
@@ -48,7 +48,9 @@ func (ns *linuxNs) Init(container *libcontainer.Container, uncleanRootfs, consol
return fmt.Errorf("setctty %s", err) 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) return fmt.Errorf("parent death signal %s", err)
} }
if err := setupNewMountNamespace(rootfs, console, container.ReadonlyFs, container.NoPivotRoot); err != nil { 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 { if err != nil {
return err return err
} }
return strategy.Initialize(config, context)
err1 := strategy.Initialize(config, context)
if err1 != nil {
return err1
}
} }
return nil return nil
} }
+6 -6
View File
@@ -46,10 +46,8 @@ func setupNewMountNamespace(rootfs, console string, readonly, noPivotRoot bool)
if err := setupDev(rootfs); err != nil { if err := setupDev(rootfs); err != nil {
return err return err
} }
if console != "" { if err := setupPtmx(rootfs, console); err != nil {
if err := setupPtmx(rootfs, console); err != nil { return err
return err
}
} }
if err := system.Chdir(rootfs); err != nil { if err := system.Chdir(rootfs); err != nil {
return fmt.Errorf("chdir into %s %s", rootfs, err) 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 { if err := os.Symlink("pts/ptmx", ptmx); err != nil {
return fmt.Errorf("symlink dev ptmx %s", err) return fmt.Errorf("symlink dev ptmx %s", err)
} }
if err := setupConsole(rootfs, console); err != nil { if console != "" {
return err if err := setupConsole(rootfs, console); err != nil {
return err
}
} }
return nil return nil
} }
+6 -21
View File
@@ -5,15 +5,10 @@
*/ */
package listenbuffer package listenbuffer
import ( import "net"
"fmt"
"net"
"time"
)
// NewListenBuffer returns a listener listening on addr with the protocol. It sets the // NewListenBuffer returns a listener listening on addr with the protocol.
// timeout to wait on first connection before an error is returned func NewListenBuffer(proto, addr string, activate chan struct{}) (net.Listener, error) {
func NewListenBuffer(proto, addr string, activate chan struct{}, timeout time.Duration) (net.Listener, error) {
wrapped, err := net.Listen(proto, addr) wrapped, err := net.Listen(proto, addr)
if err != nil { if err != nil {
return nil, err return nil, err
@@ -22,7 +17,6 @@ func NewListenBuffer(proto, addr string, activate chan struct{}, timeout time.Du
return &defaultListener{ return &defaultListener{
wrapped: wrapped, wrapped: wrapped,
activate: activate, activate: activate,
timeout: timeout,
}, nil }, nil
} }
@@ -30,7 +24,6 @@ type defaultListener struct {
wrapped net.Listener // the real listener to wrap wrapped net.Listener // the real listener to wrap
ready bool // is the listner ready to start accpeting connections ready bool // is the listner ready to start accpeting connections
activate chan struct{} activate chan struct{}
timeout time.Duration // how long to wait before we consider this an error
} }
func (l *defaultListener) Close() error { func (l *defaultListener) Close() error {
@@ -47,15 +40,7 @@ func (l *defaultListener) Accept() (net.Conn, error) {
if l.ready { if l.ready {
return l.wrapped.Accept() return l.wrapped.Accept()
} }
<-l.activate
select { l.ready = true
case <-time.After(l.timeout): return l.Accept()
// 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")
} }
+2 -1
View File
@@ -13,7 +13,8 @@ var (
func init() { func init() {
flag.Bool([]string{"#hp", "#-halp"}, false, "display the halp") 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.BoolVar(&b2, []string{"#-bool"}, false, "a simple bool")
flag.IntVar(&i, []string{"-integer", "-number"}, -1, "a simple integer") 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 flag.StringVar(&str, []string{"s", "#hidden", "-string"}, "", "a simple string") //-s -hidden and --string will work, but -hidden won't be in the usage
+13 -2
View File
@@ -805,9 +805,20 @@ func (f *FlagSet) parseOne() (bool, string, error) {
f.actual = make(map[string]*Flag) f.actual = make(map[string]*Flag)
} }
f.actual[name] = flag f.actual[name] = flag
for _, n := range flag.Names { for i, n := range flag.Names {
if n == fmt.Sprintf("#%s", name) { 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 return true, "", nil
+19
View 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
View 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
View 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
View 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
View File
@@ -0,0 +1,9 @@
// +build !linux,!darwin,!freebsd
package signal
import (
"syscall"
)
var SignalMap = map[string]syscall.Signal{}
+2 -2
View File
@@ -115,8 +115,8 @@ func Mknod(path string, mode uint32, dev int) error {
return syscall.Mknod(path, mode, dev) return syscall.Mknod(path, mode, dev)
} }
func ParentDeathSignal() error { func ParentDeathSignal(sig uintptr) error {
if _, _, err := syscall.RawSyscall(syscall.SYS_PRCTL, syscall.PR_SET_PDEATHSIG, uintptr(syscall.SIGKILL), 0); err != 0 { if _, _, err := syscall.RawSyscall(syscall.SYS_PRCTL, syscall.PR_SET_PDEATHSIG, sig, 0); err != 0 {
return err return err
} }
return nil return nil
+25 -14
View File
@@ -9,16 +9,24 @@ const (
getTermios = syscall.TIOCGETA getTermios = syscall.TIOCGETA
setTermios = syscall.TIOCSETA setTermios = syscall.TIOCSETA
ECHO = 0x00000008 IGNBRK = syscall.IGNBRK
ONLCR = 0x2 PARMRK = syscall.PARMRK
ISTRIP = 0x20 INLCR = syscall.INLCR
INLCR = 0x40 IGNCR = syscall.IGNCR
ISIG = 0x80 ECHONL = syscall.ECHONL
IGNCR = 0x80 CSIZE = syscall.CSIZE
ICANON = 0x100 ICRNL = syscall.ICRNL
ICRNL = 0x100 ISTRIP = syscall.ISTRIP
IXOFF = 0x400 PARENB = syscall.PARENB
IXON = 0x200 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 { type Termios struct {
@@ -41,10 +49,13 @@ func MakeRaw(fd uintptr) (*State, error) {
} }
newState := oldState.termios newState := oldState.termios
newState.Iflag &^= (ISTRIP | INLCR | IGNCR | IXON | IXOFF) newState.Iflag &^= (IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL | IXON)
newState.Iflag |= ICRNL newState.Oflag &^= OPOST
newState.Oflag |= ONLCR newState.Lflag &^= (ECHO | ECHONL | ICANON | ISIG | IEXTEN)
newState.Lflag &^= (ECHO | ICANON | ISIG) 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 { if _, _, err := syscall.Syscall(syscall.SYS_IOCTL, fd, uintptr(setTermios), uintptr(unsafe.Pointer(&newState))); err != 0 {
return nil, err return nil, err
+59 -57
View File
@@ -8,6 +8,7 @@ import (
"github.com/dotcloud/docker/dockerversion" "github.com/dotcloud/docker/dockerversion"
"github.com/dotcloud/docker/engine" "github.com/dotcloud/docker/engine"
"github.com/dotcloud/docker/pkg/graphdb" "github.com/dotcloud/docker/pkg/graphdb"
"github.com/dotcloud/docker/pkg/signal"
"github.com/dotcloud/docker/registry" "github.com/dotcloud/docker/registry"
"github.com/dotcloud/docker/runconfig" "github.com/dotcloud/docker/runconfig"
"github.com/dotcloud/docker/utils" "github.com/dotcloud/docker/utils"
@@ -18,7 +19,7 @@ import (
"net/url" "net/url"
"os" "os"
"os/exec" "os/exec"
"os/signal" gosignal "os/signal"
"path" "path"
"path/filepath" "path/filepath"
"runtime" "runtime"
@@ -47,7 +48,7 @@ func InitServer(job *engine.Job) engine.Status {
} }
job.Logf("Setting up signal traps") job.Logf("Setting up signal traps")
c := make(chan os.Signal, 1) 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() { go func() {
sig := <-c sig := <-c
log.Printf("Received signal '%v', exiting\n", sig) log.Printf("Received signal '%v', exiting\n", sig)
@@ -122,56 +123,30 @@ func (v *simpleVersionInfo) Version() string {
// for the container to exit. // for the container to exit.
// If a signal is given, then just send it to the container and return. // If a signal is given, then just send it to the container and return.
func (srv *Server) ContainerKill(job *engine.Job) engine.Status { 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 { if n := len(job.Args); n < 1 || n > 2 {
return job.Errorf("Usage: %s CONTAINER [SIGNAL]", job.Name) return job.Errorf("Usage: %s CONTAINER [SIGNAL]", job.Name)
} }
name := job.Args[0] var (
var sig uint64 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] != "" { if len(job.Args) == 2 && job.Args[1] != "" {
sig = uint64(signalMap[job.Args[1]]) // Check if we passed the singal as a number:
if sig == 0 { // The largest legal signal is 31, so let's parse on 5 bits
var err error sig, err = strconv.ParseUint(job.Args[1], 10, 5)
// The largest legal signal is 31, so let's parse on 5 bits if err != nil {
sig, err = strconv.ParseUint(job.Args[1], 10, 5) // The signal is not a number, treat it as a string
if err != nil { sig = uint64(signal.SignalMap[job.Args[1]])
if sig == 0 {
return job.Errorf("Invalid signal: %s", job.Args[1]) return job.Errorf("Invalid signal: %s", job.Args[1])
} }
} }
} }
if container := srv.runtime.Get(name); container != nil { if container := srv.runtime.Get(name); container != nil {
// If no signal is passed, or SIGKILL, perform regular Kill (SIGKILL + wait()) // If no signal is passed, or SIGKILL, perform regular Kill (SIGKILL + wait())
if sig == 0 || syscall.Signal(sig) == syscall.SIGKILL { if sig == 0 || syscall.Signal(sig) == syscall.SIGKILL {
@@ -1039,12 +1014,17 @@ func (srv *Server) ContainerCommit(job *engine.Job) engine.Status {
if container == nil { if container == nil {
return job.Errorf("No such container: %s", name) return job.Errorf("No such container: %s", name)
} }
var config runconfig.Config var config = container.Config
if err := job.GetenvJson("config", &config); err != nil { var newConfig runconfig.Config
if err := job.GetenvJson("config", &newConfig); err != nil {
return job.Error(err) 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 { if err != nil {
return job.Error(err) 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) { if !srv.runtime.graph.Exists(id) {
out.Write(sf.FormatProgress(utils.TruncateID(id), "Pulling metadata", nil)) out.Write(sf.FormatProgress(utils.TruncateID(id), "Pulling metadata", nil))
imgJSON, imgSize, err := r.GetRemoteImageJSON(id, endpoint, token) var (
if err != nil { imgJSON []byte
out.Write(sf.FormatProgress(utils.TruncateID(id), "Error pulling dependent layers", nil)) imgSize int
// FIXME: Keep going in case of error? err error
return err img *Image
} )
img, err := NewImgJSON(imgJSON) retries := 5
if err != nil { for j := 1; j <= retries; j++ {
out.Write(sf.FormatProgress(utils.TruncateID(id), "Error pulling dependent layers", nil)) imgJSON, imgSize, err = r.GetRemoteImageJSON(id, endpoint, token)
return fmt.Errorf("Failed to parse json: %s", err) 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 // Get the layer
@@ -2390,7 +2386,13 @@ func (srv *Server) IsRunning() bool {
} }
func (srv *Server) Close() error { func (srv *Server) Close() error {
if srv == nil {
return nil
}
srv.SetRunning(false) srv.SetRunning(false)
if srv.runtime == nil {
return nil
}
return srv.runtime.Close() return srv.runtime.Close()
} }
+1 -1
View File
@@ -85,7 +85,7 @@ func (jm *JSONMessage) Display(out io.Writer, isTerminal bool) error {
return jm.Error return jm.Error
} }
var endl string var endl string
if isTerminal { if isTerminal && jm.Stream == "" {
// <ESC>[2K = erase entire current line // <ESC>[2K = erase entire current line
fmt.Fprintf(out, "%c[2K\r", 27) fmt.Fprintf(out, "%c[2K\r", 27)
endl = "\r" endl = "\r"
-11
View File
@@ -1,11 +0,0 @@
package utils
import (
"os"
"os/signal"
)
func StopCatch(sigc chan os.Signal) {
signal.Stop(sigc)
close(sigc)
}
-44
View File
@@ -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,
)
}
-47
View File
@@ -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,
)
}