浏览代码

Add unless-stopped restart policy

Fixes #11008

Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
Tonis Tiigi 10 年之前
父节点
当前提交
10305dc5e8

+ 2 - 0
daemon/container.go

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

+ 2 - 0
daemon/daemon.go

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

+ 6 - 1
daemon/monitor.go

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

+ 2 - 1
docs/reference/api/docker_remote_api_v1.21.md

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

+ 1 - 1
docs/reference/commandline/create.md

@@ -58,7 +58,7 @@ Creates a new container.
       --pid=""                      PID namespace to use
       --pid=""                      PID namespace to use
       --privileged=false            Give extended privileges to this container
       --privileged=false            Give extended privileges to this container
       --read-only=false             Mount the container's root filesystem as read only
       --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
       --security-opt=[]             Security options
       -t, --tty=false               Allocate a pseudo-TTY
       -t, --tty=false               Allocate a pseudo-TTY
       --disable-content-trust=true  Skip image verification
       --disable-content-trust=true  Skip image verification

+ 11 - 2
docs/reference/commandline/run.md

@@ -58,7 +58,7 @@ weight=1
       --pid=""                      PID namespace to use
       --pid=""                      PID namespace to use
       --privileged=false            Give extended privileges to this container
       --privileged=false            Give extended privileges to this container
       --read-only=false             Mount the container's root filesystem as read only
       --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
       --rm=false                    Automatically remove the container when it exits
       --security-opt=[]             Security Options
       --security-opt=[]             Security Options
       --sig-proxy=true              Proxy received signals to the process
       --sig-proxy=true              Proxy received signals to the process
@@ -440,7 +440,16 @@ Docker supports the following restart policies:
       <td>
       <td>
         Always restart the container regardless of the exit status.
         Always restart the container regardless of the exit status.
         When you specify always, the Docker daemon will try to restart
         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>
       </td>
     </tr>
     </tr>
   </tbody>
   </tbody>

+ 10 - 1
docs/reference/run.md

@@ -398,7 +398,16 @@ Docker supports the following restart policies:
       <td>
       <td>
         Always restart the container regardless of the exit status.
         Always restart the container regardless of the exit status.
         When you specify always, the Docker daemon will try to restart
         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>
       </td>
     </tr>
     </tr>
   </tbody>
   </tbody>

+ 54 - 0
integration-cli/docker_cli_daemon_test.go

@@ -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) {
 func (s *DockerDaemonSuite) TestDaemonStartIptablesFalse(c *check.C) {
 	if err := s.d.Start("--iptables=false"); err != nil {
 	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)
 		c.Fatalf("we should have been able to start the daemon with passing iptables=false: %v", err)

+ 1 - 1
man/docker-create.1.md

@@ -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.
    Mount the container's root filesystem as read only.
 
 
 **--restart**="no"
 **--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-opt**=[]
    Security Options
    Security Options

+ 1 - 1
man/docker-run.1.md

@@ -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.
 its root filesystem mounted as read only prohibiting any writes.
 
 
 **--restart**="no"
 **--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*
 **--rm**=*true*|*false*
    Automatically remove the container when it exits (incompatible with -d). The default is *false*.
    Automatically remove the container when it exits (incompatible with -d). The default is *false*.

+ 7 - 0
runconfig/hostconfig.go

@@ -140,6 +140,13 @@ func (rp *RestartPolicy) IsOnFailure() bool {
 	return rp.Name == "on-failure"
 	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.
 // LogConfig represents the logging configuration of the container.
 type LogConfig struct {
 type LogConfig struct {
 	Type   string
 	Type   string

+ 2 - 2
runconfig/parse.go

@@ -450,9 +450,9 @@ func ParseRestartPolicy(policy string) (RestartPolicy, error) {
 
 
 	p.Name = name
 	p.Name = name
 	switch name {
 	switch name {
-	case "always":
+	case "always", "unless-stopped":
 		if len(parts) > 1 {
 		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":
 	case "no":
 		// do nothing
 		// do nothing