mirror of
https://github.com/clearlinux/docker.git
synced 2026-08-22 05:56:17 +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 |
@@ -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
|
||||||
|
|||||||
+45
-14
@@ -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
@@ -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
@@ -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.
|
||||||
|
|||||||
@@ -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 $?
|
||||||
;;
|
;;
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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/
|
||||||
|
|
||||||
|
|||||||
@@ -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 ``'``)
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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":
|
||||||
|
|||||||
@@ -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}}
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
@@ -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
@@ -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
|
||||||
|
|||||||
@@ -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
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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{
|
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
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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")
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
@@ -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
|
||||||
|
|||||||
@@ -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)
|
||||||
|
}
|
||||||
@@ -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,
|
||||||
|
}
|
||||||
@@ -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,
|
||||||
|
}
|
||||||
@@ -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,
|
||||||
|
}
|
||||||
@@ -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)
|
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
@@ -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
|
||||||
|
|||||||
@@ -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()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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"
|
||||||
|
|||||||
@@ -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