浏览代码

backend: add StopOptions to ContainerRestart and ContainerStop

While we're modifying the interface, also add a context to both.

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
Sebastiaan van Stijn 3 年之前
父节点
当前提交
90de570cfa

+ 2 - 2
api/server/router/container/backend.go

@@ -37,10 +37,10 @@ type stateBackend interface {
 	ContainerPause(name string) error
 	ContainerPause(name string) error
 	ContainerRename(oldName, newName string) error
 	ContainerRename(oldName, newName string) error
 	ContainerResize(name string, height, width int) error
 	ContainerResize(name string, height, width int) error
-	ContainerRestart(name string, seconds *int) error
+	ContainerRestart(ctx context.Context, name string, options container.StopOptions) error
 	ContainerRm(name string, config *types.ContainerRmConfig) error
 	ContainerRm(name string, config *types.ContainerRmConfig) error
 	ContainerStart(name string, hostConfig *container.HostConfig, checkpoint string, checkpointDir string) error
 	ContainerStart(name string, hostConfig *container.HostConfig, checkpoint string, checkpointDir string) error
-	ContainerStop(name string, seconds *int) error
+	ContainerStop(ctx context.Context, name string, options container.StopOptions) error
 	ContainerUnpause(name string) error
 	ContainerUnpause(name string) error
 	ContainerUpdate(name string, hostConfig *container.HostConfig) (container.ContainerUpdateOKBody, error)
 	ContainerUpdate(name string, hostConfig *container.HostConfig) (container.ContainerUpdateOKBody, error)
 	ContainerWait(ctx context.Context, name string, condition containerpkg.WaitCondition) (<-chan containerpkg.StateStatus, error)
 	ContainerWait(ctx context.Context, name string, condition containerpkg.WaitCondition) (<-chan containerpkg.StateStatus, error)

+ 6 - 6
api/server/router/container/container_routes.go

@@ -221,16 +221,16 @@ func (s *containerRouter) postContainersStop(ctx context.Context, w http.Respons
 		return err
 		return err
 	}
 	}
 
 
-	var seconds *int
+	var options container.StopOptions
 	if tmpSeconds := r.Form.Get("t"); tmpSeconds != "" {
 	if tmpSeconds := r.Form.Get("t"); tmpSeconds != "" {
 		valSeconds, err := strconv.Atoi(tmpSeconds)
 		valSeconds, err := strconv.Atoi(tmpSeconds)
 		if err != nil {
 		if err != nil {
 			return err
 			return err
 		}
 		}
-		seconds = &valSeconds
+		options.Timeout = &valSeconds
 	}
 	}
 
 
-	if err := s.backend.ContainerStop(vars["name"], seconds); err != nil {
+	if err := s.backend.ContainerStop(ctx, vars["name"], options); err != nil {
 		return err
 		return err
 	}
 	}
 	w.WriteHeader(http.StatusNoContent)
 	w.WriteHeader(http.StatusNoContent)
@@ -278,16 +278,16 @@ func (s *containerRouter) postContainersRestart(ctx context.Context, w http.Resp
 		return err
 		return err
 	}
 	}
 
 
-	var seconds *int
+	var options container.StopOptions
 	if tmpSeconds := r.Form.Get("t"); tmpSeconds != "" {
 	if tmpSeconds := r.Form.Get("t"); tmpSeconds != "" {
 		valSeconds, err := strconv.Atoi(tmpSeconds)
 		valSeconds, err := strconv.Atoi(tmpSeconds)
 		if err != nil {
 		if err != nil {
 			return err
 			return err
 		}
 		}
-		seconds = &valSeconds
+		options.Timeout = &valSeconds
 	}
 	}
 
 
-	if err := s.backend.ContainerRestart(vars["name"], seconds); err != nil {
+	if err := s.backend.ContainerRestart(ctx, vars["name"], options); err != nil {
 		return err
 		return err
 	}
 	}
 
 

+ 13 - 0
api/types/container/config.go

@@ -13,6 +13,19 @@ import (
 // Docker interprets it as 3 nanoseconds.
 // Docker interprets it as 3 nanoseconds.
 const MinimumDuration = 1 * time.Millisecond
 const MinimumDuration = 1 * time.Millisecond
 
 
+// StopOptions holds the options to stop or restart a container.
+type StopOptions struct {
+	// Timeout (optional) is the timeout (in seconds) to wait for the container
+	// to stop gracefully before forcibly terminating it with SIGKILL.
+	//
+	// - Use nil to use the default timeout (10 seconds).
+	// - Use '-1' to wait indefinitely.
+	// - Use '0' to not wait for the container to exit gracefully, and
+	//   immediately proceeds to forcibly terminating the container.
+	// - Other positive values are used as timeout (in seconds).
+	Timeout *int `json:",omitempty"`
+}
+
 // HealthConfig holds configuration settings for the HEALTHCHECK feature.
 // HealthConfig holds configuration settings for the HEALTHCHECK feature.
 type HealthConfig struct {
 type HealthConfig struct {
 	// Test is the test to perform to check that the container is healthy.
 	// Test is the test to perform to check that the container is healthy.

+ 2 - 2
daemon/cluster/executor/backend.go

@@ -35,8 +35,8 @@ type Backend interface {
 	ReleaseIngress() (<-chan struct{}, error)
 	ReleaseIngress() (<-chan struct{}, error)
 	CreateManagedContainer(config types.ContainerCreateConfig) (container.ContainerCreateCreatedBody, error)
 	CreateManagedContainer(config types.ContainerCreateConfig) (container.ContainerCreateCreatedBody, error)
 	ContainerStart(name string, hostConfig *container.HostConfig, checkpoint string, checkpointDir string) error
 	ContainerStart(name string, hostConfig *container.HostConfig, checkpoint string, checkpointDir string) error
-	ContainerStop(name string, seconds *int) error
-	ContainerLogs(context.Context, string, *types.ContainerLogsOptions) (msgs <-chan *backend.LogMessage, tty bool, err error)
+	ContainerStop(ctx context.Context, name string, config container.StopOptions) error
+	ContainerLogs(ctx context.Context, name string, config *types.ContainerLogsOptions) (msgs <-chan *backend.LogMessage, tty bool, err error)
 	ConnectContainerToNetwork(containerName, networkName string, endpointConfig *network.EndpointSettings) error
 	ConnectContainerToNetwork(containerName, networkName string, endpointConfig *network.EndpointSettings) error
 	ActivateContainerServiceBinding(containerName string) error
 	ActivateContainerServiceBinding(containerName string) error
 	DeactivateContainerServiceBinding(containerName string) error
 	DeactivateContainerServiceBinding(containerName string) error

+ 5 - 6
daemon/cluster/executor/container/adapter.go

@@ -407,14 +407,13 @@ func (c *containerAdapter) wait(ctx context.Context) (<-chan containerpkg.StateS
 }
 }
 
 
 func (c *containerAdapter) shutdown(ctx context.Context) error {
 func (c *containerAdapter) shutdown(ctx context.Context) error {
+	var options = containertypes.StopOptions{}
 	// Default stop grace period to nil (daemon will use the stopTimeout of the container)
 	// Default stop grace period to nil (daemon will use the stopTimeout of the container)
-	var stopgrace *int
-	spec := c.container.spec()
-	if spec.StopGracePeriod != nil {
-		stopgraceValue := int(spec.StopGracePeriod.Seconds)
-		stopgrace = &stopgraceValue
+	if spec := c.container.spec(); spec.StopGracePeriod != nil {
+		timeout := int(spec.StopGracePeriod.Seconds)
+		options.Timeout = &timeout
 	}
 	}
-	return c.backend.ContainerStop(c.container.name(), stopgrace)
+	return c.backend.ContainerStop(ctx, c.container.name(), options)
 }
 }
 
 
 func (c *containerAdapter) terminate(ctx context.Context) error {
 func (c *containerAdapter) terminate(ctx context.Context) error {

+ 1 - 1
daemon/daemon.go

@@ -1117,7 +1117,7 @@ func (daemon *Daemon) waitForStartupDone() {
 
 
 func (daemon *Daemon) shutdownContainer(c *container.Container) error {
 func (daemon *Daemon) shutdownContainer(c *container.Container) error {
 	// If container failed to exit in stopTimeout seconds of SIGTERM, then using the force
 	// If container failed to exit in stopTimeout seconds of SIGTERM, then using the force
-	if err := daemon.containerStop(c, nil); err != nil {
+	if err := daemon.containerStop(context.TODO(), c, containertypes.StopOptions{}); err != nil {
 		return fmt.Errorf("Failed to stop container %s with error: %v", c.ID, err)
 		return fmt.Errorf("Failed to stop container %s with error: %v", c.ID, err)
 	}
 	}
 
 

+ 3 - 1
daemon/delete.go

@@ -1,6 +1,7 @@
 package daemon // import "github.com/docker/docker/daemon"
 package daemon // import "github.com/docker/docker/daemon"
 
 
 import (
 import (
+	"context"
 	"fmt"
 	"fmt"
 	"os"
 	"os"
 	"path"
 	"path"
@@ -8,6 +9,7 @@ import (
 	"time"
 	"time"
 
 
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types"
+	containertypes "github.com/docker/docker/api/types/container"
 	"github.com/docker/docker/container"
 	"github.com/docker/docker/container"
 	"github.com/docker/docker/errdefs"
 	"github.com/docker/docker/errdefs"
 	"github.com/docker/docker/pkg/containerfs"
 	"github.com/docker/docker/pkg/containerfs"
@@ -109,7 +111,7 @@ func (daemon *Daemon) cleanupContainer(container *container.Container, config ty
 	// If you arrived here and know the answer, you earned yourself a picture
 	// If you arrived here and know the answer, you earned yourself a picture
 	// of a cute animal of your own choosing.
 	// of a cute animal of your own choosing.
 	var stopTimeout = 3
 	var stopTimeout = 3
-	if err := daemon.containerStop(container, &stopTimeout); err != nil {
+	if err := daemon.containerStop(context.TODO(), container, containertypes.StopOptions{Timeout: &stopTimeout}); err != nil {
 		return err
 		return err
 	}
 	}
 
 

+ 5 - 4
daemon/restart.go

@@ -1,6 +1,7 @@
 package daemon // import "github.com/docker/docker/daemon"
 package daemon // import "github.com/docker/docker/daemon"
 
 
 import (
 import (
+	"context"
 	"fmt"
 	"fmt"
 
 
 	containertypes "github.com/docker/docker/api/types/container"
 	containertypes "github.com/docker/docker/api/types/container"
@@ -14,12 +15,12 @@ import (
 // timeout, ContainerRestart will wait forever until a graceful
 // timeout, ContainerRestart will wait forever until a graceful
 // stop. Returns an error if the container cannot be found, or if
 // stop. Returns an error if the container cannot be found, or if
 // there is an underlying error at any stage of the restart.
 // there is an underlying error at any stage of the restart.
-func (daemon *Daemon) ContainerRestart(name string, seconds *int) error {
+func (daemon *Daemon) ContainerRestart(ctx context.Context, name string, options containertypes.StopOptions) error {
 	ctr, err := daemon.GetContainer(name)
 	ctr, err := daemon.GetContainer(name)
 	if err != nil {
 	if err != nil {
 		return err
 		return err
 	}
 	}
-	err = daemon.containerRestart(ctr, seconds)
+	err = daemon.containerRestart(ctx, ctr, options)
 	if err != nil {
 	if err != nil {
 		return fmt.Errorf("Cannot restart container %s: %v", name, err)
 		return fmt.Errorf("Cannot restart container %s: %v", name, err)
 	}
 	}
@@ -31,7 +32,7 @@ func (daemon *Daemon) ContainerRestart(name string, seconds *int) error {
 // container. When stopping, wait for the given duration in seconds to
 // container. When stopping, wait for the given duration in seconds to
 // gracefully stop, before forcefully terminating the container. If
 // gracefully stop, before forcefully terminating the container. If
 // given a negative duration, wait forever for a graceful stop.
 // given a negative duration, wait forever for a graceful stop.
-func (daemon *Daemon) containerRestart(container *container.Container, seconds *int) error {
+func (daemon *Daemon) containerRestart(ctx context.Context, container *container.Container, options containertypes.StopOptions) error {
 	// Determine isolation. If not specified in the hostconfig, use daemon default.
 	// Determine isolation. If not specified in the hostconfig, use daemon default.
 	actualIsolation := container.HostConfig.Isolation
 	actualIsolation := container.HostConfig.Isolation
 	if containertypes.Isolation.IsDefault(actualIsolation) {
 	if containertypes.Isolation.IsDefault(actualIsolation) {
@@ -56,7 +57,7 @@ func (daemon *Daemon) containerRestart(container *container.Container, seconds *
 		autoRemove := container.HostConfig.AutoRemove
 		autoRemove := container.HostConfig.AutoRemove
 
 
 		container.HostConfig.AutoRemove = false
 		container.HostConfig.AutoRemove = false
-		err := daemon.containerStop(container, seconds)
+		err := daemon.containerStop(ctx, container, options)
 		// restore AutoRemove irrespective of whether the stop worked or not
 		// restore AutoRemove irrespective of whether the stop worked or not
 		container.HostConfig.AutoRemove = autoRemove
 		container.HostConfig.AutoRemove = autoRemove
 		// containerStop will write HostConfig to disk, we shall restore AutoRemove
 		// containerStop will write HostConfig to disk, we shall restore AutoRemove

+ 6 - 7
daemon/stop.go

@@ -4,6 +4,7 @@ import (
 	"context"
 	"context"
 	"time"
 	"time"
 
 
+	containertypes "github.com/docker/docker/api/types/container"
 	"github.com/docker/docker/container"
 	"github.com/docker/docker/container"
 	"github.com/docker/docker/errdefs"
 	"github.com/docker/docker/errdefs"
 	"github.com/pkg/errors"
 	"github.com/pkg/errors"
@@ -18,7 +19,7 @@ import (
 // If the timeout is nil, the container's StopTimeout value is used, if set,
 // If the timeout is nil, the container's StopTimeout value is used, if set,
 // otherwise the engine default. A negative timeout value can be specified,
 // otherwise the engine default. A negative timeout value can be specified,
 // meaning no timeout, i.e. no forceful termination is performed.
 // meaning no timeout, i.e. no forceful termination is performed.
-func (daemon *Daemon) ContainerStop(name string, timeout *int) error {
+func (daemon *Daemon) ContainerStop(ctx context.Context, name string, options containertypes.StopOptions) error {
 	ctr, err := daemon.GetContainer(name)
 	ctr, err := daemon.GetContainer(name)
 	if err != nil {
 	if err != nil {
 		return err
 		return err
@@ -26,7 +27,7 @@ func (daemon *Daemon) ContainerStop(name string, timeout *int) error {
 	if !ctr.IsRunning() {
 	if !ctr.IsRunning() {
 		return containerNotModifiedError{}
 		return containerNotModifiedError{}
 	}
 	}
-	err = daemon.containerStop(ctr, timeout)
+	err = daemon.containerStop(ctx, ctr, options)
 	if err != nil {
 	if err != nil {
 		return errdefs.System(errors.Wrapf(err, "cannot stop container: %s", name))
 		return errdefs.System(errors.Wrapf(err, "cannot stop container: %s", name))
 	}
 	}
@@ -34,9 +35,7 @@ func (daemon *Daemon) ContainerStop(name string, timeout *int) error {
 }
 }
 
 
 // containerStop sends a stop signal, waits, sends a kill signal.
 // containerStop sends a stop signal, waits, sends a kill signal.
-func (daemon *Daemon) containerStop(ctr *container.Container, seconds *int) (retErr error) {
-	// TODO propagate a context down to this function
-	ctx := context.TODO()
+func (daemon *Daemon) containerStop(ctx context.Context, ctr *container.Container, options containertypes.StopOptions) (retErr error) {
 	if !ctr.IsRunning() {
 	if !ctr.IsRunning() {
 		return nil
 		return nil
 	}
 	}
@@ -45,8 +44,8 @@ func (daemon *Daemon) containerStop(ctr *container.Container, seconds *int) (ret
 		stopSignal  = ctr.StopSignal()
 		stopSignal  = ctr.StopSignal()
 		stopTimeout = ctr.StopTimeout()
 		stopTimeout = ctr.StopTimeout()
 	)
 	)
-	if seconds != nil {
-		stopTimeout = *seconds
+	if options.Timeout != nil {
+		stopTimeout = *options.Timeout
 	}
 	}
 
 
 	var wait time.Duration
 	var wait time.Duration