diff --git a/container/container.go b/container/container.go index c9991629be..58892e7788 100644 --- a/container/container.go +++ b/container/container.go @@ -909,6 +909,7 @@ func (container *Container) FullHostname() string { func (container *Container) RestartManager(reset bool) restartmanager.RestartManager { if reset { container.RestartCount = 0 + container.restartManager = nil } if container.restartManager == nil { container.restartManager = restartmanager.New(container.HostConfig.RestartPolicy) diff --git a/contrib/completion/zsh/_docker b/contrib/completion/zsh/_docker index 1351ae9579..cb007b90d4 100644 --- a/contrib/completion/zsh/_docker +++ b/contrib/completion/zsh/_docker @@ -216,7 +216,7 @@ __docker_get_log_options() { gelf_options=("env" "gelf-address" "gelf-compression-level" "gelf-compression-type" "labels" "tag") journald_options=("env" "labels" "tag") json_file_options=("env" "labels" "max-file" "max-size") - syslog_options=("syslog-address" "syslog-tls-ca-cert" "syslog-tls-cert" "syslog-tls-key" "syslog-tls-skip-verify" "syslog-facility" "tag") + syslog_options=("syslog-address" "syslog-format" "syslog-tls-ca-cert" "syslog-tls-cert" "syslog-tls-key" "syslog-tls-skip-verify" "syslog-facility" "tag") splunk_options=("env" "labels" "splunk-caname" "splunk-capath" "splunk-index" "splunk-insecureskipverify" "splunk-source" "splunk-sourcetype" "splunk-token" "splunk-url" "tag") [[ $log_driver = (awslogs|all) ]] && _describe -t awslogs-options "awslogs options" awslogs_options "$@" && ret=0 @@ -236,7 +236,15 @@ __docker_log_options() { integer ret=1 if compset -P '*='; then - _message 'value' && ret=0 + case "${${words[-1]%=*}#*=}" in + (syslog-format) + syslog_format_opts=('rfc3164' 'rfc5424') + _describe -t syslog-format-opts "Syslog format Options" syslog_format_opts && ret=0 + ;; + *) + _message 'value' && ret=0 + ;; + esac else __docker_get_log_options -qS "=" && ret=0 fi diff --git a/daemon/daemon_unix.go b/daemon/daemon_unix.go index a152fd0e95..9a4ab32296 100644 --- a/daemon/daemon_unix.go +++ b/daemon/daemon_unix.go @@ -1092,6 +1092,11 @@ func (daemon *Daemon) stats(c *container.Container) (*types.StatsJSON, error) { MaxUsage: mem.MaxUsage, Stats: cgs.MemoryStats.Stats, Failcnt: mem.Failcnt, + Limit: mem.Limit, + } + // if the container does not set memory limit, use the machineMemory + if mem.Limit > daemon.statsCollector.machineMemory && daemon.statsCollector.machineMemory > 0 { + s.MemoryStats.Limit = daemon.statsCollector.machineMemory } if cgs.PidsStats != nil { s.PidsStats = types.PidsStats{ diff --git a/daemon/kill.go b/daemon/kill.go index e6ccfc7955..3967f0f299 100644 --- a/daemon/kill.go +++ b/daemon/kill.go @@ -3,6 +3,7 @@ package daemon import ( "fmt" "runtime" + "strings" "syscall" "time" @@ -81,7 +82,14 @@ func (daemon *Daemon) killWithSignal(container *container.Container, sig int) er } if err := daemon.kill(container, sig); err != nil { - return fmt.Errorf("Cannot kill container %s: %s", container.ID, err) + err = fmt.Errorf("Cannot kill container %s: %s", container.ID, err) + // if container or process not exists, ignore the error + if strings.Contains(err.Error(), "container not found") || + strings.Contains(err.Error(), "no such process") { + logrus.Warnf("%s", err.Error()) + } else { + return err + } } attributes := map[string]string{ diff --git a/daemon/monitor.go b/daemon/monitor.go index 0a82c5f8fd..f9f7def98d 100644 --- a/daemon/monitor.go +++ b/daemon/monitor.go @@ -77,6 +77,7 @@ func (daemon *Daemon) StateChanged(id string, e libcontainerd.StateInfo) error { c.Reset(false) return err } + daemon.LogContainerEvent(c, "start") case libcontainerd.StatePause: c.Paused = true daemon.LogContainerEvent(c, "pause") diff --git a/daemon/start.go b/daemon/start.go index 52531f511e..1b34f42692 100644 --- a/daemon/start.go +++ b/daemon/start.go @@ -131,7 +131,6 @@ func (daemon *Daemon) containerStart(container *container.Container) (err error) return err } - defer daemon.LogContainerEvent(container, "start") // this is logged even on error if err := daemon.containerd.Create(container.ID, *spec, libcontainerd.WithRestartManager(container.RestartManager(true))); err != nil { // if we receive an internal error from the initial start of a container then lets // return it instead of entering the restart loop @@ -149,6 +148,9 @@ func (daemon *Daemon) containerStart(container *container.Container) (err error) } container.Reset(false) + + // start event is logged even on error + daemon.LogContainerEvent(container, "start") return err } diff --git a/daemon/stats_collector_unix.go b/daemon/stats_collector_unix.go index 5010281498..fb1931dfb8 100644 --- a/daemon/stats_collector_unix.go +++ b/daemon/stats_collector_unix.go @@ -14,6 +14,7 @@ import ( "github.com/Sirupsen/logrus" "github.com/docker/docker/container" "github.com/docker/docker/pkg/pubsub" + sysinfo "github.com/docker/docker/pkg/system" "github.com/docker/engine-api/types" "github.com/opencontainers/runc/libcontainer/system" ) @@ -35,6 +36,11 @@ func (daemon *Daemon) newStatsCollector(interval time.Duration) *statsCollector clockTicksPerSecond: uint64(system.GetClockTicks()), bufReader: bufio.NewReaderSize(nil, 128), } + meminfo, err := sysinfo.ReadMemInfo() + if err == nil && meminfo.MemTotal > 0 { + s.machineMemory = uint64(meminfo.MemTotal) + } + go s.run() return s } @@ -47,6 +53,7 @@ type statsCollector struct { clockTicksPerSecond uint64 publishers map[*container.Container]*pubsub.Publisher bufReader *bufio.Reader + machineMemory uint64 } // collect registers the container with the collector and adds it to diff --git a/docs/extend/plugins_authorization.md b/docs/extend/plugins_authorization.md index 7db31b85c4..31a6a072ab 100644 --- a/docs/extend/plugins_authorization.md +++ b/docs/extend/plugins_authorization.md @@ -149,7 +149,7 @@ should implement the following two methods: "User": "The user identification", "UserAuthNMethod": "The authentication method used", "RequestMethod": "The HTTP method", - "RequestUri": "The HTTP request URI", + "RequestURI": "The HTTP request URI", "RequestBody": "Byte array containing the raw HTTP request body", "RequestHeader": "Byte array containing the raw HTTP request header as a map[string][]string ", "RequestStatusCode": "Request status code" @@ -174,7 +174,7 @@ should implement the following two methods: "User": "The user identification", "UserAuthNMethod": "The authentication method used", "RequestMethod": "The HTTP method", - "RequestUri": "The HTTP request URI", + "RequestURI": "The HTTP request URI", "RequestBody": "Byte array containing the raw HTTP request body", "RequestHeader": "Byte array containing the raw HTTP request header as a map[string][]string", "RequestStatusCode": "Request status code", diff --git a/docs/reference/api/docker_remote_api_v1.15.md b/docs/reference/api/docker_remote_api_v1.15.md index c95a66b0a4..f6a860a4f9 100644 --- a/docs/reference/api/docker_remote_api_v1.15.md +++ b/docs/reference/api/docker_remote_api_v1.15.md @@ -151,7 +151,7 @@ Create a container "ExposedPorts": { "22/tcp": {} }, - "SecurityOpts": [""], + "SecurityOpts": [], "HostConfig": { "Binds": ["/tmp:/tmp"], "Links": ["redis3:redis"], diff --git a/docs/reference/api/docker_remote_api_v1.16.md b/docs/reference/api/docker_remote_api_v1.16.md index f91f408495..630da70144 100644 --- a/docs/reference/api/docker_remote_api_v1.16.md +++ b/docs/reference/api/docker_remote_api_v1.16.md @@ -151,7 +151,7 @@ Create a container "ExposedPorts": { "22/tcp": {} }, - "SecurityOpts": [""], + "SecurityOpts": [], "HostConfig": { "Binds": ["/tmp:/tmp"], "Links": ["redis3:redis"], diff --git a/docs/reference/api/docker_remote_api_v1.17.md b/docs/reference/api/docker_remote_api_v1.17.md index 3da962b074..9baa758cc5 100644 --- a/docs/reference/api/docker_remote_api_v1.17.md +++ b/docs/reference/api/docker_remote_api_v1.17.md @@ -168,7 +168,7 @@ Create a container "RestartPolicy": { "Name": "", "MaximumRetryCount": 0 }, "NetworkMode": "bridge", "Devices": [], - "SecurityOpt": [""] + "SecurityOpt": [] } } diff --git a/docs/reference/api/docker_remote_api_v1.18.md b/docs/reference/api/docker_remote_api_v1.18.md index 421b169623..9de2cc6cf1 100644 --- a/docs/reference/api/docker_remote_api_v1.18.md +++ b/docs/reference/api/docker_remote_api_v1.18.md @@ -184,7 +184,7 @@ Create a container "Devices": [], "Ulimits": [{}], "LogConfig": { "Type": "json-file", Config: {} }, - "SecurityOpt": [""], + "SecurityOpt": [], "CgroupParent": "" } } diff --git a/docs/reference/api/docker_remote_api_v1.19.md b/docs/reference/api/docker_remote_api_v1.19.md index 9eeb353efe..e049dbe7c4 100644 --- a/docs/reference/api/docker_remote_api_v1.19.md +++ b/docs/reference/api/docker_remote_api_v1.19.md @@ -191,7 +191,7 @@ Create a container "Devices": [], "Ulimits": [{}], "LogConfig": { "Type": "json-file", "Config": {} }, - "SecurityOpt": [""], + "SecurityOpt": [], "CgroupParent": "" } } diff --git a/docs/reference/api/docker_remote_api_v1.20.md b/docs/reference/api/docker_remote_api_v1.20.md index ac0fc80813..dd47ff7f1f 100644 --- a/docs/reference/api/docker_remote_api_v1.20.md +++ b/docs/reference/api/docker_remote_api_v1.20.md @@ -156,7 +156,7 @@ Create a container }, "Volumes": { "/volumes/data": {} - } + }, "WorkingDir": "", "NetworkDisabled": false, "MacAddress": "12:34:56:78:9a:bc", @@ -193,7 +193,7 @@ Create a container "Devices": [], "Ulimits": [{}], "LogConfig": { "Type": "json-file", "Config": {} }, - "SecurityOpt": [""], + "SecurityOpt": [], "CgroupParent": "" } } diff --git a/docs/reference/api/docker_remote_api_v1.21.md b/docs/reference/api/docker_remote_api_v1.21.md index 6b3ac2e825..7cdfd0f3f3 100644 --- a/docs/reference/api/docker_remote_api_v1.21.md +++ b/docs/reference/api/docker_remote_api_v1.21.md @@ -160,7 +160,7 @@ Create a container }, "Volumes": { "/volumes/data": {} - } + }, "WorkingDir": "", "NetworkDisabled": false, "MacAddress": "12:34:56:78:9a:bc", @@ -201,7 +201,7 @@ Create a container "Devices": [], "Ulimits": [{}], "LogConfig": { "Type": "json-file", "Config": {} }, - "SecurityOpt": [""], + "SecurityOpt": [], "CgroupParent": "", "VolumeDriver": "" } diff --git a/docs/reference/api/docker_remote_api_v1.22.md b/docs/reference/api/docker_remote_api_v1.22.md index badd170566..d9a860a774 100644 --- a/docs/reference/api/docker_remote_api_v1.22.md +++ b/docs/reference/api/docker_remote_api_v1.22.md @@ -248,7 +248,7 @@ Create a container }, "Volumes": { "/volumes/data": {} - } + }, "WorkingDir": "", "NetworkDisabled": false, "MacAddress": "12:34:56:78:9a:bc", @@ -294,7 +294,7 @@ Create a container "Devices": [], "Ulimits": [{}], "LogConfig": { "Type": "json-file", "Config": {} }, - "SecurityOpt": [""], + "SecurityOpt": [], "CgroupParent": "", "VolumeDriver": "", "ShmSize": 67108864 diff --git a/docs/reference/api/docker_remote_api_v1.23.md b/docs/reference/api/docker_remote_api_v1.23.md index d235e4219c..eac495f09f 100644 --- a/docs/reference/api/docker_remote_api_v1.23.md +++ b/docs/reference/api/docker_remote_api_v1.23.md @@ -267,7 +267,7 @@ Create a container }, "Volumes": { "/volumes/data": {} - } + }, "WorkingDir": "", "NetworkDisabled": false, "MacAddress": "12:34:56:78:9a:bc", @@ -313,7 +313,7 @@ Create a container "Devices": [], "Ulimits": [{}], "LogConfig": { "Type": "json-file", "Config": {} }, - "SecurityOpt": [""], + "SecurityOpt": [], "CgroupParent": "", "VolumeDriver": "", "ShmSize": 67108864 diff --git a/docs/reference/commandline/images.md b/docs/reference/commandline/images.md index 8419fd0c52..9ab97684c4 100644 --- a/docs/reference/commandline/images.md +++ b/docs/reference/commandline/images.md @@ -85,16 +85,16 @@ If nothing matches `REPOSITORY[:TAG]`, the list is empty. ## Listing the full length image IDs $ docker images --no-trunc - REPOSITORY TAG IMAGE ID CREATED SIZE - <none> <none> 77af4d6b9913e693e8d0b4b294fa62ade6054e6b2f1ffb617ac955dd63fb0182 19 hours ago 1.089 GB - committest latest b6fa739cedf5ea12a620a439402b6004d057da800f91c7524b5086a5e4749c9f 19 hours ago 1.089 GB - <none> <none> 78a85c484f71509adeaace20e72e941f6bdd2b25b4c75da8693efd9f61a37921 19 hours ago 1.089 GB - docker latest 30557a29d5abc51e5f1d5b472e79b7e296f595abcf19fe6b9199dbbc809c6ff4 20 hours ago 1.089 GB - <none> <none> 0124422dd9f9cf7ef15c0617cda3931ee68346455441d66ab8bdc5b05e9fdce5 20 hours ago 1.089 GB - <none> <none> 18ad6fad340262ac2a636efd98a6d1f0ea775ae3d45240d3418466495a19a81b 22 hours ago 1.082 GB - <none> <none> f9f1e26352f0a3ba6a0ff68167559f64f3e21ff7ada60366e2d44a04befd1d3a 23 hours ago 1.089 GB - tryout latest 2629d1fa0b81b222fca63371ca16cbf6a0772d07759ff80e8d1369b926940074 23 hours ago 131.5 MB - <none> <none> 5ed6274db6ceb2397844896966ea239290555e74ef307030ebb01ff91b1914df 24 hours ago 1.089 GB + REPOSITORY TAG IMAGE ID CREATED SIZE + <none> <none> sha256:77af4d6b9913e693e8d0b4b294fa62ade6054e6b2f1ffb617ac955dd63fb0182 19 hours ago 1.089 GB + committest latest sha256:b6fa739cedf5ea12a620a439402b6004d057da800f91c7524b5086a5e4749c9f 19 hours ago 1.089 GB + <none> <none> sha256:78a85c484f71509adeaace20e72e941f6bdd2b25b4c75da8693efd9f61a37921 19 hours ago 1.089 GB + docker latest sha256:30557a29d5abc51e5f1d5b472e79b7e296f595abcf19fe6b9199dbbc809c6ff4 20 hours ago 1.089 GB + <none> <none> sha256:0124422dd9f9cf7ef15c0617cda3931ee68346455441d66ab8bdc5b05e9fdce5 20 hours ago 1.089 GB + <none> <none> sha256:18ad6fad340262ac2a636efd98a6d1f0ea775ae3d45240d3418466495a19a81b 22 hours ago 1.082 GB + <none> <none> sha256:f9f1e26352f0a3ba6a0ff68167559f64f3e21ff7ada60366e2d44a04befd1d3a 23 hours ago 1.089 GB + tryout latest sha256:2629d1fa0b81b222fca63371ca16cbf6a0772d07759ff80e8d1369b926940074 23 hours ago 131.5 MB + <none> <none> sha256:5ed6274db6ceb2397844896966ea239290555e74ef307030ebb01ff91b1914df 24 hours ago 1.089 GB ## Listing image digests diff --git a/docs/reference/run.md b/docs/reference/run.md index 170dd35079..b37dd5ae80 100644 --- a/docs/reference/run.md +++ b/docs/reference/run.md @@ -608,8 +608,8 @@ with the same logic -- if the original volume was specified with a name it will to the container --security-opt="no-new-privileges" : Disable container processes from gaining new privileges - --security-opt="seccomp:unconfined": Turn off seccomp confinement for the container - --security-opt="seccomp:profile.json: White listed syscalls seccomp Json file to be used as a seccomp filter + --security-opt="seccomp=unconfined": Turn off seccomp confinement for the container + --security-opt="seccomp=profile.json: White listed syscalls seccomp Json file to be used as a seccomp filter You can override the default labeling scheme for each container by specifying diff --git a/docs/userguide/containers/networkingcontainers.md b/docs/userguide/containers/networkingcontainers.md index 1ac1364d3a..9355e756f6 100644 --- a/docs/userguide/containers/networkingcontainers.md +++ b/docs/userguide/containers/networkingcontainers.md @@ -35,7 +35,7 @@ You name your container by using the `--name` flag, for example launch a new con $ docker run -d -P --name web training/webapp python app.py -Use the `docker ps` command to see check the name: +Use the `docker ps` command to check the name: $ docker ps -l CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES diff --git a/hack/vendor.sh b/hack/vendor.sh index 6fe46e5dcc..033e88dc65 100755 --- a/hack/vendor.sh +++ b/hack/vendor.sh @@ -20,7 +20,8 @@ clone git github.com/mattn/go-sqlite3 v1.1.0 clone git github.com/mistifyio/go-zfs v2.1.1 clone git github.com/tchap/go-patricia v2.1.0 clone git github.com/vdemeester/shakers 24d7f1d6a71aa5d9cbe7390e4afb66b7eef9e1b3 -clone git golang.org/x/net 47990a1ba55743e6ef1affd3a14e5bac8553615d https://github.com/golang/net.git +# forked golang.org/x/net package includes a patch for lazy loading trace templates +clone git golang.org/x/net 78cb2c067747f08b343f20614155233ab4ea2ad3 https://github.com/tonistiigi/net.git clone git golang.org/x/sys eb2c74142fd19a79b3f237334c7384d5167b1b46 https://github.com/golang/sys.git clone git github.com/docker/go-units 651fc226e7441360384da338d0fd37f2440ffbe3 clone git github.com/docker/go-connections v0.2.0 @@ -29,7 +30,7 @@ clone git github.com/RackSec/srslog 259aed10dfa74ea2961eddd1d9847619f6e98837 clone git github.com/imdario/mergo 0.2.1 #get libnetwork packages -clone git github.com/docker/libnetwork v0.7.0-rc.4 +clone git github.com/docker/libnetwork v0.7.0-rc.6 clone git github.com/armon/go-metrics eb0af217e5e9747e41dd5303755356b62d28e3ec clone git github.com/hashicorp/go-msgpack 71c2886f5a673a35f909803f38ece5810165097b clone git github.com/hashicorp/memberlist 9a1e242e454d2443df330bdd51a436d5a9058fc4 diff --git a/integration-cli/docker_api_stats_test.go b/integration-cli/docker_api_stats_test.go index 10c9fd5b20..7c3f8d3916 100644 --- a/integration-cli/docker_api_stats_test.go +++ b/integration-cli/docker_api_stats_test.go @@ -227,3 +227,31 @@ func (s *DockerSuite) TestApiStatsContainerNotFound(c *check.C) { c.Assert(err, checker.IsNil) c.Assert(status, checker.Equals, http.StatusNotFound) } + +func (s *DockerSuite) TestApiStatsContainerGetMemoryLimit(c *check.C) { + testRequires(c, DaemonIsLinux) + + resp, body, err := sockRequestRaw("GET", "/info", nil, "application/json") + c.Assert(err, checker.IsNil) + c.Assert(resp.StatusCode, checker.Equals, http.StatusOK) + var info types.Info + err = json.NewDecoder(body).Decode(&info) + c.Assert(err, checker.IsNil) + body.Close() + + // don't set a memory limit, the memory limit should be system memory + conName := "foo" + dockerCmd(c, "run", "-d", "--name", conName, "busybox", "top") + c.Assert(waitRun(conName), checker.IsNil) + + resp, body, err = sockRequestRaw("GET", fmt.Sprintf("/containers/%s/stats?stream=false", conName), nil, "") + c.Assert(err, checker.IsNil) + c.Assert(resp.StatusCode, checker.Equals, http.StatusOK) + c.Assert(resp.Header.Get("Content-Type"), checker.Equals, "application/json") + + var v *types.Stats + err = json.NewDecoder(body).Decode(&v) + c.Assert(err, checker.IsNil) + body.Close() + c.Assert(fmt.Sprintf("%d", v.MemoryStats.Limit), checker.Equals, fmt.Sprintf("%d", info.MemTotal)) +} diff --git a/integration-cli/docker_cli_events_test.go b/integration-cli/docker_cli_events_test.go index f426650c2b..f17b711cc2 100644 --- a/integration-cli/docker_cli_events_test.go +++ b/integration-cli/docker_cli_events_test.go @@ -604,3 +604,44 @@ func (s *DockerSuite) TestEventsFilterImageInContainerAction(c *check.C) { events := strings.Split(strings.TrimSpace(out), "\n") c.Assert(len(events), checker.GreaterThan, 1, check.Commentf(out)) } + +func (s *DockerSuite) TestEventsContainerRestart(c *check.C) { + dockerCmd(c, "run", "-d", "--name=testEvent", "--restart=on-failure:3", "busybox", "false") + + // wait until test2 is auto removed. + waitTime := 10 * time.Second + if daemonPlatform == "windows" { + // nslookup isn't present in Windows busybox. Is built-in. + waitTime = 90 * time.Second + } + + err := waitInspect("testEvent", "{{ .State.Restarting }} {{ .State.Running }}", "false false", waitTime) + c.Assert(err, checker.IsNil) + + var ( + createCount int + startCount int + dieCount int + ) + out, _ := dockerCmd(c, "events", "--since=0", fmt.Sprintf("--until=%d", daemonTime(c).Unix()), "-f", "container=testEvent") + events := strings.Split(strings.TrimSpace(out), "\n") + + nEvents := len(events) + c.Assert(nEvents, checker.GreaterOrEqualThan, 1) //Missing expected event + actions := eventActionsByIDAndType(c, events, "testEvent", "container") + + for _, a := range actions { + switch a { + case "create": + createCount++ + case "start": + startCount++ + case "die": + dieCount++ + } + } + c.Assert(createCount, checker.Equals, 1, check.Commentf("testEvent should be created 1 times: %v", actions)) + c.Assert(startCount, checker.Equals, 4, check.Commentf("testEvent should start 4 times: %v", actions)) + c.Assert(dieCount, checker.Equals, 4, check.Commentf("testEvent should die 4 times: %v", actions)) + +} diff --git a/integration-cli/docker_cli_restart_test.go b/integration-cli/docker_cli_restart_test.go index 297e16a169..31c8920385 100644 --- a/integration-cli/docker_cli_restart_test.go +++ b/integration-cli/docker_cli_restart_test.go @@ -188,3 +188,62 @@ func (s *DockerSuite) TestRestartWithPolicyUserDefinedNetwork(c *check.C) { _, _, err = dockerCmdWithError("exec", "second", "ping", "-c", "1", "foo") c.Assert(err, check.IsNil) } + +func (s *DockerSuite) TestRestartPolicyAfterRestart(c *check.C) { + testRequires(c, SameHostDaemon) + + out, _ := runSleepingContainer(c, "-d", "--restart=always") + id := strings.TrimSpace(out) + c.Assert(waitRun(id), check.IsNil) + + dockerCmd(c, "restart", id) + + c.Assert(waitRun(id), check.IsNil) + + pidStr := inspectField(c, id, "State.Pid") + + pid, err := strconv.Atoi(pidStr) + c.Assert(err, check.IsNil) + + p, err := os.FindProcess(pid) + c.Assert(err, check.IsNil) + c.Assert(p, check.NotNil) + + err = p.Kill() + c.Assert(err, check.IsNil) + + err = waitInspect(id, "{{.RestartCount}}", "1", 30*time.Second) + c.Assert(err, check.IsNil) + + err = waitInspect(id, "{{.State.Status}}", "running", 30*time.Second) + c.Assert(err, check.IsNil) +} + +func (s *DockerSuite) TestRestartContainerwithRestartPolicy(c *check.C) { + out1, _ := dockerCmd(c, "run", "-d", "--restart=on-failure:3", "busybox", "false") + out2, _ := dockerCmd(c, "run", "-d", "--restart=always", "busybox", "false") + + id1 := strings.TrimSpace(string(out1)) + id2 := strings.TrimSpace(string(out2)) + err := waitInspect(id1, "{{ .State.Restarting }} {{ .State.Running }}", "false false", 30*time.Second) + c.Assert(err, checker.IsNil) + + // TODO: fix racey problem during restart: + // https://jenkins.dockerproject.org/job/Docker-PRs-Win2Lin/24665/console + // Error response from daemon: Cannot restart container 6655f620d90b390527db23c0a15b3e46d86a58ecec20a5697ab228d860174251: remove /var/run/docker/libcontainerd/6655f620d90b390527db23c0a15b3e46d86a58ecec20a5697ab228d860174251/rootfs: device or resource busy + if _, _, err := dockerCmdWithError("restart", id1); err != nil { + // if restart met racey problem, try again + time.Sleep(500 * time.Millisecond) + dockerCmd(c, "restart", id1) + } + if _, _, err := dockerCmdWithError("restart", id2); err != nil { + // if restart met racey problem, try again + time.Sleep(500 * time.Millisecond) + dockerCmd(c, "restart", id2) + } + + dockerCmd(c, "stop", id1) + dockerCmd(c, "stop", id2) + dockerCmd(c, "start", id1) + dockerCmd(c, "start", id2) +} diff --git a/libcontainerd/client_linux.go b/libcontainerd/client_linux.go index f747b2fe2e..01806da889 100644 --- a/libcontainerd/client_linux.go +++ b/libcontainerd/client_linux.go @@ -134,7 +134,7 @@ func (clnt *client) Create(containerID string, spec Spec, options ...CreateOptio defer clnt.unlock(containerID) if ctr, err := clnt.getContainer(containerID); err == nil { - if ctr.restarting { // docker doesn't actually call start if restart is on atm, but probably should in the future + if ctr.restarting { ctr.restartManager.Cancel() ctr.clean() } else { diff --git a/libcontainerd/container_linux.go b/libcontainerd/container_linux.go index 506a1770dd..6f168ca1da 100644 --- a/libcontainerd/container_linux.go +++ b/libcontainerd/container_linux.go @@ -121,6 +121,7 @@ func (ctr *container) handleEvent(e *containerd.Event) error { } else if restart { st.State = StateRestart ctr.restarting = true + ctr.client.deleteContainer(e.Id) go func() { err := <-wait ctr.restarting = false diff --git a/pkg/ioutils/bytespipe.go b/pkg/ioutils/bytespipe.go index e263c284f0..fcaecc37b5 100644 --- a/pkg/ioutils/bytespipe.go +++ b/pkg/ioutils/bytespipe.go @@ -49,6 +49,7 @@ func (bp *BytesPipe) Write(p []byte) (int, error) { bp.mu.Lock() defer bp.mu.Unlock() written := 0 +loop0: for { if bp.closeErr != nil { return written, ErrClosed @@ -75,6 +76,9 @@ func (bp *BytesPipe) Write(p []byte) (int, error) { // block if too much data is still in the buffer for bp.bufLen >= blockThreshold { bp.wait.Wait() + if bp.closeErr != nil { + continue loop0 + } } // allocate slice that has twice the size of the last unless maximum reached diff --git a/restartmanager/restartmanager.go b/restartmanager/restartmanager.go index e534b2cf73..39b8b1502c 100644 --- a/restartmanager/restartmanager.go +++ b/restartmanager/restartmanager.go @@ -51,7 +51,7 @@ func (rm *restartManager) ShouldRestart(exitCode uint32) (bool, chan error, erro }() if rm.canceled { - return false, nil, nil + return false, nil, fmt.Errorf("restartmanager canceled") } if rm.active { diff --git a/vendor/src/github.com/docker/libnetwork/CHANGELOG.md b/vendor/src/github.com/docker/libnetwork/CHANGELOG.md index 0eb85cde52..883830f5cd 100644 --- a/vendor/src/github.com/docker/libnetwork/CHANGELOG.md +++ b/vendor/src/github.com/docker/libnetwork/CHANGELOG.md @@ -1,5 +1,15 @@ # Changelog +## 0.7.0-rc.6 (2016-04-10) +- Flush cached resolver socket on default gateway change + +## 0.7.0-rc.5 (2016-04-08) +- Persist ipam driver options +- Fixes https://github.com/docker/libnetwork/issues/1087 +- Use go vet from go tool +- Godep update to pick up latest docker/docker packages +- Validate remote driver response using docker plugins package method. + ## 0.7.0-rc.4 (2016-04-06) - Fix the handling for default gateway Endpoint join/leave. diff --git a/vendor/src/github.com/docker/libnetwork/Dockerfile.build b/vendor/src/github.com/docker/libnetwork/Dockerfile.build index 01b690e955..035f852c03 100644 --- a/vendor/src/github.com/docker/libnetwork/Dockerfile.build +++ b/vendor/src/github.com/docker/libnetwork/Dockerfile.build @@ -3,6 +3,5 @@ RUN apt-get update && apt-get -y install iptables RUN go get github.com/tools/godep \ github.com/golang/lint/golint \ - golang.org/x/tools/cmd/vet \ golang.org/x/tools/cmd/cover\ github.com/mattn/goveralls diff --git a/vendor/src/github.com/docker/libnetwork/drivers/remote/driver.go b/vendor/src/github.com/docker/libnetwork/drivers/remote/driver.go index 32533533dd..e3f2cd58e2 100644 --- a/vendor/src/github.com/docker/libnetwork/drivers/remote/driver.go +++ b/vendor/src/github.com/docker/libnetwork/drivers/remote/driver.go @@ -3,7 +3,6 @@ package remote import ( "fmt" "net" - "strings" log "github.com/Sirupsen/logrus" "github.com/docker/docker/pkg/plugins" @@ -14,10 +13,6 @@ import ( "github.com/docker/libnetwork/types" ) -const ( - missingMethod = "404 page not found" -) - type driver struct { endpoint *plugins.Client networkType string @@ -260,7 +255,7 @@ func (d *driver) ProgramExternalConnectivity(nid, eid string, options map[string Options: options, } err := d.call("ProgramExternalConnectivity", data, &api.ProgramExternalConnectivityResponse{}) - if err != nil && strings.Contains(err.Error(), missingMethod) { + if err != nil && plugins.IsNotFound(err) { // It is not mandatory yet to support this method return nil } @@ -274,7 +269,7 @@ func (d *driver) RevokeExternalConnectivity(nid, eid string) error { EndpointID: eid, } err := d.call("RevokeExternalConnectivity", data, &api.RevokeExternalConnectivityResponse{}) - if err != nil && strings.Contains(err.Error(), missingMethod) { + if err != nil && plugins.IsNotFound(err) { // It is not mandatory yet to support this method return nil } diff --git a/vendor/src/github.com/docker/libnetwork/endpoint.go b/vendor/src/github.com/docker/libnetwork/endpoint.go index 55b3a8e1ab..7608dd7457 100644 --- a/vendor/src/github.com/docker/libnetwork/endpoint.go +++ b/vendor/src/github.com/docker/libnetwork/endpoint.go @@ -477,6 +477,10 @@ func (ep *endpoint) sbJoin(sb *sandbox, options ...EndpointOption) error { ep.Name(), ep.ID(), err) } } + + if sb.resolver != nil { + sb.resolver.FlushExtServers() + } } if !sb.needDefaultGW() { diff --git a/vendor/src/github.com/docker/libnetwork/network.go b/vendor/src/github.com/docker/libnetwork/network.go index ed2b52c790..a14550cd7e 100644 --- a/vendor/src/github.com/docker/libnetwork/network.go +++ b/vendor/src/github.com/docker/libnetwork/network.go @@ -320,6 +320,13 @@ func (n *network) CopyTo(o datastore.KVObject) error { dstN.labels[k] = v } + if n.ipamOptions != nil { + dstN.ipamOptions = make(map[string]string, len(n.ipamOptions)) + for k, v := range n.ipamOptions { + dstN.ipamOptions[k] = v + } + } + for _, v4conf := range n.ipamV4Config { dstV4Conf := &IpamConf{} v4conf.CopyTo(dstV4Conf) @@ -372,6 +379,7 @@ func (n *network) MarshalJSON() ([]byte, error) { netMap["scope"] = n.scope netMap["labels"] = n.labels netMap["ipamType"] = n.ipamType + netMap["ipamOptions"] = n.ipamOptions netMap["addrSpace"] = n.addrSpace netMap["enableIPv6"] = n.enableIPv6 if n.generic != nil { @@ -432,6 +440,15 @@ func (n *network) UnmarshalJSON(b []byte) (err error) { } } + if v, ok := netMap["ipamOptions"]; ok { + if iOpts, ok := v.(map[string]interface{}); ok { + n.ipamOptions = make(map[string]string, len(iOpts)) + for k, v := range iOpts { + n.ipamOptions[k] = v.(string) + } + } + } + if v, ok := netMap["generic"]; ok { n.generic = v.(map[string]interface{}) // Restore opts in their map[string]string form diff --git a/vendor/src/github.com/docker/libnetwork/resolver.go b/vendor/src/github.com/docker/libnetwork/resolver.go index 7af1850cf6..cff692fd1f 100644 --- a/vendor/src/github.com/docker/libnetwork/resolver.go +++ b/vendor/src/github.com/docker/libnetwork/resolver.go @@ -45,7 +45,7 @@ const ( ptrIPv6domain = ".ip6.arpa." respTTL = 600 maxExtDNS = 3 //max number of external servers to try - extIOTimeout = 3 * time.Second + extIOTimeout = 4 * time.Second defaultRespSize = 512 maxConcurrent = 50 logInterval = 2 * time.Second @@ -158,6 +158,10 @@ func (r *resolver) Start() error { func (r *resolver) FlushExtServers() { for i := 0; i < maxExtDNS; i++ { + if r.extDNSList[i].extConn != nil { + r.extDNSList[i].extConn.Close() + } + r.extDNSList[i].extConn = nil r.extDNSList[i].extOnce = sync.Once{} } @@ -344,9 +348,6 @@ func (r *resolver) ServeDNS(w dns.ResponseWriter, query *dns.Msg) { if extDNS.ipStr == "" { break } - log.Debugf("Query %s[%d] from %s, forwarding to %s:%s", name, query.Question[0].Qtype, - w.LocalAddr().String(), proto, extDNS.ipStr) - extConnect := func() { addr := fmt.Sprintf("%s:%d", extDNS.ipStr, 53) extConn, err = net.DialTimeout(proto, addr, extIOTimeout) @@ -378,6 +379,8 @@ func (r *resolver) ServeDNS(w dns.ResponseWriter, query *dns.Msg) { if extConn == nil { continue } + log.Debugf("Query %s[%d] from %s, forwarding to %s:%s", name, query.Question[0].Qtype, + extConn.LocalAddr().String(), proto, extDNS.ipStr) // Timeout has to be set for every IO operation. extConn.SetDeadline(time.Now().Add(extIOTimeout)) @@ -424,7 +427,7 @@ func (r *resolver) ServeDNS(w dns.ResponseWriter, query *dns.Msg) { break } - if resp == nil { + if resp == nil || w == nil { return } } diff --git a/vendor/src/golang.org/x/net/trace/events.go b/vendor/src/golang.org/x/net/trace/events.go index e66c7e3282..d8daec1a79 100644 --- a/vendor/src/golang.org/x/net/trace/events.go +++ b/vendor/src/golang.org/x/net/trace/events.go @@ -21,11 +21,6 @@ import ( "time" ) -var eventsTmpl = template.Must(template.New("events").Funcs(template.FuncMap{ - "elapsed": elapsed, - "trimSpace": strings.TrimSpace, -}).Parse(eventsHTML)) - const maxEventsPerLog = 100 type bucket struct { @@ -101,7 +96,7 @@ func RenderEvents(w http.ResponseWriter, req *http.Request, sensitive bool) { famMu.RLock() defer famMu.RUnlock() - if err := eventsTmpl.Execute(w, data); err != nil { + if err := eventsTmpl().Execute(w, data); err != nil { log.Printf("net/trace: Failed executing template: %v", err) } } @@ -421,6 +416,19 @@ func freeEventLog(el *eventLog) { } } +var eventsTmplCache *template.Template +var eventsTmplOnce sync.Once + +func eventsTmpl() *template.Template { + eventsTmplOnce.Do(func() { + eventsTmplCache = template.Must(template.New("events").Funcs(template.FuncMap{ + "elapsed": elapsed, + "trimSpace": strings.TrimSpace, + }).Parse(eventsHTML)) + }) + return eventsTmplCache +} + const eventsHTML = ` <html> <head> diff --git a/vendor/src/golang.org/x/net/trace/histogram.go b/vendor/src/golang.org/x/net/trace/histogram.go index bb42aa5320..9bf4286c79 100644 --- a/vendor/src/golang.org/x/net/trace/histogram.go +++ b/vendor/src/golang.org/x/net/trace/histogram.go @@ -12,6 +12,7 @@ import ( "html/template" "log" "math" + "sync" "golang.org/x/net/internal/timeseries" ) @@ -320,15 +321,20 @@ func (h *histogram) newData() *data { func (h *histogram) html() template.HTML { buf := new(bytes.Buffer) - if err := distTmpl.Execute(buf, h.newData()); err != nil { + if err := distTmpl().Execute(buf, h.newData()); err != nil { buf.Reset() log.Printf("net/trace: couldn't execute template: %v", err) } return template.HTML(buf.String()) } -// Input: data -var distTmpl = template.Must(template.New("distTmpl").Parse(` +var distTmplCache *template.Template +var distTmplOnce sync.Once + +func distTmpl() *template.Template { + distTmplOnce.Do(func() { + // Input: data + distTmplCache = template.Must(template.New("distTmpl").Parse(` <table> <tr> <td style="padding:0.25em">Count: {{.Count}}</td> @@ -354,3 +360,6 @@ var distTmpl = template.Must(template.New("distTmpl").Parse(` {{end}} </table> `)) + }) + return distTmplCache +} diff --git a/vendor/src/golang.org/x/net/trace/trace.go b/vendor/src/golang.org/x/net/trace/trace.go index c44cb7ec9e..0a232a1016 100644 --- a/vendor/src/golang.org/x/net/trace/trace.go +++ b/vendor/src/golang.org/x/net/trace/trace.go @@ -232,7 +232,7 @@ func Render(w io.Writer, req *http.Request, sensitive bool) { completedMu.RLock() defer completedMu.RUnlock() - if err := pageTmpl.ExecuteTemplate(w, "Page", data); err != nil { + if err := pageTmpl().ExecuteTemplate(w, "Page", data); err != nil { log.Printf("net/trace: Failed executing template: %v", err) } } @@ -888,10 +888,18 @@ func elapsed(d time.Duration) string { return string(b) } -var pageTmpl = template.Must(template.New("Page").Funcs(template.FuncMap{ - "elapsed": elapsed, - "add": func(a, b int) int { return a + b }, -}).Parse(pageHTML)) +var pageTmplCache *template.Template +var pageTmplOnce sync.Once + +func pageTmpl() *template.Template { + pageTmplOnce.Do(func() { + pageTmplCache = template.Must(template.New("Page").Funcs(template.FuncMap{ + "elapsed": elapsed, + "add": func(a, b int) int { return a + b }, + }).Parse(pageHTML)) + }) + return pageTmplCache +} const pageHTML = ` {{template "Prolog" .}}