Add unless-stopped restart policy

Fixes #11008

Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
This commit is contained in:
Tonis Tiigi 2015-08-05 14:09:08 -07:00
parent 6206cbe193
commit 10305dc5e8
12 changed files with 99 additions and 10 deletions

View File

@ -79,6 +79,7 @@ type CommonContainer struct {
MountLabel, ProcessLabel string
RestartCount int
HasBeenStartedBefore bool
HasBeenManuallyStopped bool // used for unless-stopped restart policy
hostConfig *runconfig.HostConfig
command *execdriver.Command
monitor *containerMonitor
@ -1063,6 +1064,7 @@ func copyEscapable(dst io.Writer, src io.ReadCloser) (written int64, err error)
func (container *Container) shouldRestart() bool {
return container.hostConfig.RestartPolicy.Name == "always" ||
(container.hostConfig.RestartPolicy.Name == "unless-stopped" && !container.HasBeenManuallyStopped) ||
(container.hostConfig.RestartPolicy.Name == "on-failure" && container.ExitCode != 0)
}

View File

@ -103,6 +103,7 @@ type Daemon struct {
EventsService *events.Events
netController libnetwork.NetworkController
root string
shutdown bool
}
// Get looks for a container using the provided information, which could be
@ -744,6 +745,7 @@ func NewDaemon(config *Config, registryService *registry.Service) (daemon *Daemo
}
func (daemon *Daemon) Shutdown() error {
daemon.shutdown = true
if daemon.containers != nil {
group := sync.WaitGroup{}
logrus.Debug("starting clean shutdown of all containers...")

View File

@ -119,6 +119,10 @@ func (m *containerMonitor) Start() error {
}
m.Close()
}()
// reset stopped flag
if m.container.HasBeenManuallyStopped {
m.container.HasBeenManuallyStopped = false
}
// reset the restart count
m.container.RestartCount = -1
@ -223,11 +227,12 @@ func (m *containerMonitor) shouldRestart(exitCode int) bool {
// do not restart if the user or docker has requested that this container be stopped
if m.shouldStop {
m.container.HasBeenManuallyStopped = !m.container.daemon.shutdown
return false
}
switch {
case m.restartPolicy.IsAlways():
case m.restartPolicy.IsAlways(), m.restartPolicy.IsUnlessStopped():
return true
case m.restartPolicy.IsOnFailure():
// the default value of 0 for MaximumRetryCount means that we will not enforce a maximum count

View File

@ -276,7 +276,8 @@ Json Parameters:
- **Capdrop** - A list of kernel capabilities to drop from the container.
- **RestartPolicy** The behavior to apply when the container exits. The
value is an object with a `Name` property of either `"always"` to
always restart or `"on-failure"` to restart only when the container
always restart, `"unless-stopped"` to restart always except when
user has manually stopped the container or `"on-failure"` to restart only when the container
exit code is non-zero. If `on-failure` is used, `MaximumRetryCount`
controls the number of times to retry before giving up.
The default is not to restart. (optional)

View File

@ -58,7 +58,7 @@ Creates a new container.
--pid="" PID namespace to use
--privileged=false Give extended privileges to this container
--read-only=false Mount the container's root filesystem as read only
--restart="no" Restart policy (no, on-failure[:max-retry], always)
--restart="no" Restart policy (no, on-failure[:max-retry], always, unless-stopped)
--security-opt=[] Security options
-t, --tty=false Allocate a pseudo-TTY
--disable-content-trust=true Skip image verification

View File

@ -58,7 +58,7 @@ weight=1
--pid="" PID namespace to use
--privileged=false Give extended privileges to this container
--read-only=false Mount the container's root filesystem as read only
--restart="no" Restart policy (no, on-failure[:max-retry], always)
--restart="no" Restart policy (no, on-failure[:max-retry], always, unless-stopped)
--rm=false Automatically remove the container when it exits
--security-opt=[] Security Options
--sig-proxy=true Proxy received signals to the process
@ -440,7 +440,16 @@ Docker supports the following restart policies:
<td>
Always restart the container regardless of the exit status.
When you specify always, the Docker daemon will try to restart
the container indefinitely.
the container indefinitely. The container will also always start
on daemon startup, regardless of the current state of the container.
</td>
</tr>
<tr>
<td><strong>unless-stopped</strong></td>
<td>
Always restart the container regardless of the exit status, but
do not start it on daemon startup if the container has been put
to a stopped state before.
</td>
</tr>
</tbody>

View File

@ -398,7 +398,16 @@ Docker supports the following restart policies:
<td>
Always restart the container regardless of the exit status.
When you specify always, the Docker daemon will try to restart
the container indefinitely.
the container indefinitely. The container will also always start
on daemon startup, regardless of the current state of the container.
</td>
</tr>
<tr>
<td><strong>unless-stopped</strong></td>
<td>
Always restart the container regardless of the exit status, but
do not start it on daemon startup if the container has been put
to a stopped state before.
</td>
</tr>
</tbody>

View File

@ -87,6 +87,60 @@ func (s *DockerDaemonSuite) TestDaemonRestartWithVolumesRefs(c *check.C) {
}
}
// #11008
func (s *DockerDaemonSuite) TestDaemonRestartUnlessStopped(c *check.C) {
err := s.d.StartWithBusybox()
c.Assert(err, check.IsNil)
out, err := s.d.Cmd("run", "-d", "--name", "top1", "--restart", "always", "busybox:latest", "top")
c.Assert(err, check.IsNil, check.Commentf("run top1: %v", out))
out, err = s.d.Cmd("run", "-d", "--name", "top2", "--restart", "unless-stopped", "busybox:latest", "top")
c.Assert(err, check.IsNil, check.Commentf("run top2: %v", out))
testRun := func(m map[string]bool, prefix string) {
var format string
for name, shouldRun := range m {
out, err := s.d.Cmd("ps")
c.Assert(err, check.IsNil, check.Commentf("run ps: %v", out))
if shouldRun {
format = "%scontainer %q is not running"
} else {
format = "%scontainer %q is running"
}
c.Assert(strings.Contains(out, name), check.Equals, shouldRun, check.Commentf(format, prefix, name))
}
}
// both running
testRun(map[string]bool{"top1": true, "top2": true}, "")
out, err = s.d.Cmd("stop", "top1")
c.Assert(err, check.IsNil, check.Commentf(out))
out, err = s.d.Cmd("stop", "top2")
c.Assert(err, check.IsNil, check.Commentf(out))
// both stopped
testRun(map[string]bool{"top1": false, "top2": false}, "")
err = s.d.Restart()
c.Assert(err, check.IsNil)
// restart=always running
testRun(map[string]bool{"top1": true, "top2": false}, "After daemon restart: ")
out, err = s.d.Cmd("start", "top2")
c.Assert(err, check.IsNil, check.Commentf("start top2: %v", out))
err = s.d.Restart()
c.Assert(err, check.IsNil)
// both running
testRun(map[string]bool{"top1": true, "top2": true}, "After second daemon restart: ")
}
func (s *DockerDaemonSuite) TestDaemonStartIptablesFalse(c *check.C) {
if err := s.d.Start("--iptables=false"); err != nil {
c.Fatalf("we should have been able to start the daemon with passing iptables=false: %v", err)

View File

@ -226,7 +226,7 @@ This value should always larger than **-m**, so you should always use this with
Mount the container's root filesystem as read only.
**--restart**="no"
Restart policy to apply when a container exits (no, on-failure[:max-retry], always)
Restart policy to apply when a container exits (no, on-failure[:max-retry], always, unless-stopped).
**--security-opt**=[]
Security Options

View File

@ -360,7 +360,7 @@ to write files anywhere. By specifying the `--read-only` flag the container wil
its root filesystem mounted as read only prohibiting any writes.
**--restart**="no"
Restart policy to apply when a container exits (no, on-failure[:max-retry], always)
Restart policy to apply when a container exits (no, on-failure[:max-retry], always, unless-stopped).
**--rm**=*true*|*false*
Automatically remove the container when it exits (incompatible with -d). The default is *false*.

View File

@ -140,6 +140,13 @@ func (rp *RestartPolicy) IsOnFailure() bool {
return rp.Name == "on-failure"
}
// IsUnlessStopped indicates whether the container has the
// "unless-stopped" restart policy. This means the container will
// automatically restart unless user has put it to stopped state.
func (rp *RestartPolicy) IsUnlessStopped() bool {
return rp.Name == "unless-stopped"
}
// LogConfig represents the logging configuration of the container.
type LogConfig struct {
Type string

View File

@ -450,9 +450,9 @@ func ParseRestartPolicy(policy string) (RestartPolicy, error) {
p.Name = name
switch name {
case "always":
case "always", "unless-stopped":
if len(parts) > 1 {
return p, fmt.Errorf("maximum restart count not valid with restart policy of \"always\"")
return p, fmt.Errorf("maximum restart count not valid with restart policy of \"%s\"", name)
}
case "no":
// do nothing