瀏覽代碼

Merge pull request #43206 from thaJeztah/having_such_a_good_time_im_having_a_ball

API: add "signal" parameter to container stop and restart endpoints
Sebastiaan van Stijn 3 年之前
父節點
當前提交
1a0c15abbb

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

@@ -37,10 +37,10 @@ type stateBackend interface {
 	ContainerPause(name string) error
 	ContainerRename(oldName, newName string) 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
 	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
 	ContainerUpdate(name string, hostConfig *container.HostConfig) (container.ContainerUpdateOKBody, error)
 	ContainerWait(ctx context.Context, name string, condition containerpkg.WaitCondition) (<-chan containerpkg.StateStatus, error)

+ 29 - 8
api/server/router/container/container_routes.go

@@ -221,20 +221,31 @@ func (s *containerRouter) postContainersStop(ctx context.Context, w http.Respons
 		return err
 	}
 
-	var seconds *int
+	var (
+		options container.StopOptions
+		version = httputils.VersionFromContext(ctx)
+	)
+	if versions.GreaterThanOrEqualTo(version, "1.42") {
+		if sig := r.Form.Get("signal"); sig != "" {
+			if _, err := signal.ParseSignal(sig); err != nil {
+				return errdefs.InvalidParameter(err)
+			}
+			options.Signal = sig
+		}
+	}
 	if tmpSeconds := r.Form.Get("t"); tmpSeconds != "" {
 		valSeconds, err := strconv.Atoi(tmpSeconds)
 		if err != nil {
 			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
 	}
-	w.WriteHeader(http.StatusNoContent)
 
+	w.WriteHeader(http.StatusNoContent)
 	return nil
 }
 
@@ -278,21 +289,31 @@ func (s *containerRouter) postContainersRestart(ctx context.Context, w http.Resp
 		return err
 	}
 
-	var seconds *int
+	var (
+		options container.StopOptions
+		version = httputils.VersionFromContext(ctx)
+	)
+	if versions.GreaterThanOrEqualTo(version, "1.42") {
+		if sig := r.Form.Get("signal"); sig != "" {
+			if _, err := signal.ParseSignal(sig); err != nil {
+				return errdefs.InvalidParameter(err)
+			}
+			options.Signal = sig
+		}
+	}
 	if tmpSeconds := r.Form.Get("t"); tmpSeconds != "" {
 		valSeconds, err := strconv.Atoi(tmpSeconds)
 		if err != nil {
 			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
 	}
 
 	w.WriteHeader(http.StatusNoContent)
-
 	return nil
 }
 

+ 12 - 1
api/swagger.yaml

@@ -6869,6 +6869,11 @@ paths:
           required: true
           description: "ID or name of the container"
           type: "string"
+        - name: "signal"
+          in: "query"
+          description: |
+            Signal to send to the container as an integer or string (e.g. `SIGINT`).
+          type: "string"
         - name: "t"
           in: "query"
           description: "Number of seconds to wait before killing the container"
@@ -6898,6 +6903,11 @@ paths:
           required: true
           description: "ID or name of the container"
           type: "string"
+        - name: "signal"
+          in: "query"
+          description: |
+            Signal to send to the container as an integer or string (e.g. `SIGINT`).
+          type: "string"
         - name: "t"
           in: "query"
           description: "Number of seconds to wait before killing the container"
@@ -6939,7 +6949,8 @@ paths:
           type: "string"
         - name: "signal"
           in: "query"
-          description: "Signal to send to the container as an integer or string (e.g. `SIGINT`)"
+          description: |
+            Signal to send to the container as an integer or string (e.g. `SIGINT`).
           type: "string"
           default: "SIGKILL"
       tags: ["Container"]

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

@@ -13,6 +13,24 @@ import (
 // Docker interprets it as 3 nanoseconds.
 const MinimumDuration = 1 * time.Millisecond
 
+// StopOptions holds the options to stop or restart a container.
+type StopOptions struct {
+	// Signal (optional) is the signal to send to the container to (gracefully)
+	// stop it before forcibly terminating the container with SIGKILL after the
+	// timeout expires. If not value is set, the default (SIGTERM) is used.
+	Signal string `json:",omitempty"`
+
+	// 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.
 type HealthConfig struct {
 	// Test is the test to perform to check that the container is healthy.

+ 0 - 12
api/types/time/duration_convert.go

@@ -1,12 +0,0 @@
-package time // import "github.com/docker/docker/api/types/time"
-
-import (
-	"strconv"
-	"time"
-)
-
-// DurationToSecondsString converts the specified duration to the number
-// seconds it represents, formatted as a string.
-func DurationToSecondsString(duration time.Duration) string {
-	return strconv.FormatFloat(duration.Seconds(), 'f', 0, 64)
-}

+ 0 - 26
api/types/time/duration_convert_test.go

@@ -1,26 +0,0 @@
-package time // import "github.com/docker/docker/api/types/time"
-
-import (
-	"testing"
-	"time"
-)
-
-func TestDurationToSecondsString(t *testing.T) {
-	cases := []struct {
-		in       time.Duration
-		expected string
-	}{
-		{0 * time.Second, "0"},
-		{1 * time.Second, "1"},
-		{1 * time.Minute, "60"},
-		{24 * time.Hour, "86400"},
-	}
-
-	for _, c := range cases {
-		s := DurationToSecondsString(c.in)
-		if s != c.expected {
-			t.Errorf("wrong value for input `%v`: expected `%s`, got `%s`", c.in, c.expected, s)
-			t.Fail()
-		}
-	}
-}

+ 9 - 5
client/container_restart.go

@@ -3,18 +3,22 @@ package client // import "github.com/docker/docker/client"
 import (
 	"context"
 	"net/url"
-	"time"
+	"strconv"
 
-	timetypes "github.com/docker/docker/api/types/time"
+	"github.com/docker/docker/api/types/container"
+	"github.com/docker/docker/api/types/versions"
 )
 
 // ContainerRestart stops and starts a container again.
 // It makes the daemon wait for the container to be up again for
 // a specific amount of time, given the timeout.
-func (cli *Client) ContainerRestart(ctx context.Context, containerID string, timeout *time.Duration) error {
+func (cli *Client) ContainerRestart(ctx context.Context, containerID string, options container.StopOptions) error {
 	query := url.Values{}
-	if timeout != nil {
-		query.Set("t", timetypes.DurationToSecondsString(*timeout))
+	if options.Timeout != nil {
+		query.Set("t", strconv.Itoa(*options.Timeout))
+	}
+	if options.Signal != "" && versions.GreaterThanOrEqualTo(cli.version, "1.42") {
+		query.Set("signal", options.Signal)
 	}
 	resp, err := cli.post(ctx, "/containers/"+containerID+"/restart", query, nil, nil)
 	ensureReaderClosed(resp)

+ 13 - 6
client/container_restart_test.go

@@ -8,8 +8,8 @@ import (
 	"net/http"
 	"strings"
 	"testing"
-	"time"
 
+	"github.com/docker/docker/api/types/container"
 	"github.com/docker/docker/errdefs"
 )
 
@@ -17,20 +17,23 @@ func TestContainerRestartError(t *testing.T) {
 	client := &Client{
 		client: newMockClient(errorMock(http.StatusInternalServerError, "Server error")),
 	}
-	timeout := 0 * time.Second
-	err := client.ContainerRestart(context.Background(), "nothing", &timeout)
+	err := client.ContainerRestart(context.Background(), "nothing", container.StopOptions{})
 	if !errdefs.IsSystem(err) {
 		t.Fatalf("expected a Server Error, got %[1]T: %[1]v", err)
 	}
 }
 
 func TestContainerRestart(t *testing.T) {
-	expectedURL := "/containers/container_id/restart"
+	const expectedURL = "/v1.42/containers/container_id/restart"
 	client := &Client{
 		client: newMockClient(func(req *http.Request) (*http.Response, error) {
 			if !strings.HasPrefix(req.URL.Path, expectedURL) {
 				return nil, fmt.Errorf("Expected URL '%s', got '%s'", expectedURL, req.URL)
 			}
+			s := req.URL.Query().Get("signal")
+			if s != "SIGKILL" {
+				return nil, fmt.Errorf("signal not set in URL query. Expected 'SIGKILL', got '%s'", s)
+			}
 			t := req.URL.Query().Get("t")
 			if t != "100" {
 				return nil, fmt.Errorf("t (timeout) not set in URL query properly. Expected '100', got %s", t)
@@ -40,9 +43,13 @@ func TestContainerRestart(t *testing.T) {
 				Body:       io.NopCloser(bytes.NewReader([]byte(""))),
 			}, nil
 		}),
+		version: "1.42",
 	}
-	timeout := 100 * time.Second
-	err := client.ContainerRestart(context.Background(), "container_id", &timeout)
+	timeout := 100
+	err := client.ContainerRestart(context.Background(), "container_id", container.StopOptions{
+		Signal:  "SIGKILL",
+		Timeout: &timeout,
+	})
 	if err != nil {
 		t.Fatal(err)
 	}

+ 9 - 5
client/container_stop.go

@@ -3,9 +3,10 @@ package client // import "github.com/docker/docker/client"
 import (
 	"context"
 	"net/url"
-	"time"
+	"strconv"
 
-	timetypes "github.com/docker/docker/api/types/time"
+	"github.com/docker/docker/api/types/container"
+	"github.com/docker/docker/api/types/versions"
 )
 
 // ContainerStop stops a container. In case the container fails to stop
@@ -15,10 +16,13 @@ import (
 // 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,
 // meaning no timeout, i.e. no forceful termination is performed.
-func (cli *Client) ContainerStop(ctx context.Context, containerID string, timeout *time.Duration) error {
+func (cli *Client) ContainerStop(ctx context.Context, containerID string, options container.StopOptions) error {
 	query := url.Values{}
-	if timeout != nil {
-		query.Set("t", timetypes.DurationToSecondsString(*timeout))
+	if options.Timeout != nil {
+		query.Set("t", strconv.Itoa(*options.Timeout))
+	}
+	if options.Signal != "" && versions.GreaterThanOrEqualTo(cli.version, "1.42") {
+		query.Set("signal", options.Signal)
 	}
 	resp, err := cli.post(ctx, "/containers/"+containerID+"/stop", query, nil, nil)
 	ensureReaderClosed(resp)

+ 13 - 6
client/container_stop_test.go

@@ -8,8 +8,8 @@ import (
 	"net/http"
 	"strings"
 	"testing"
-	"time"
 
+	"github.com/docker/docker/api/types/container"
 	"github.com/docker/docker/errdefs"
 )
 
@@ -17,20 +17,23 @@ func TestContainerStopError(t *testing.T) {
 	client := &Client{
 		client: newMockClient(errorMock(http.StatusInternalServerError, "Server error")),
 	}
-	timeout := 0 * time.Second
-	err := client.ContainerStop(context.Background(), "nothing", &timeout)
+	err := client.ContainerStop(context.Background(), "nothing", container.StopOptions{})
 	if !errdefs.IsSystem(err) {
 		t.Fatalf("expected a Server Error, got %[1]T: %[1]v", err)
 	}
 }
 
 func TestContainerStop(t *testing.T) {
-	expectedURL := "/containers/container_id/stop"
+	const expectedURL = "/v1.42/containers/container_id/stop"
 	client := &Client{
 		client: newMockClient(func(req *http.Request) (*http.Response, error) {
 			if !strings.HasPrefix(req.URL.Path, expectedURL) {
 				return nil, fmt.Errorf("Expected URL '%s', got '%s'", expectedURL, req.URL)
 			}
+			s := req.URL.Query().Get("signal")
+			if s != "SIGKILL" {
+				return nil, fmt.Errorf("signal not set in URL query. Expected 'SIGKILL', got '%s'", s)
+			}
 			t := req.URL.Query().Get("t")
 			if t != "100" {
 				return nil, fmt.Errorf("t (timeout) not set in URL query properly. Expected '100', got %s", t)
@@ -40,9 +43,13 @@ func TestContainerStop(t *testing.T) {
 				Body:       io.NopCloser(bytes.NewReader([]byte(""))),
 			}, nil
 		}),
+		version: "1.42",
 	}
-	timeout := 100 * time.Second
-	err := client.ContainerStop(context.Background(), "container_id", &timeout)
+	timeout := 100
+	err := client.ContainerStop(context.Background(), "container_id", container.StopOptions{
+		Signal:  "SIGKILL",
+		Timeout: &timeout,
+	})
 	if err != nil {
 		t.Fatal(err)
 	}

+ 13 - 14
client/interface.go

@@ -5,17 +5,16 @@ import (
 	"io"
 	"net"
 	"net/http"
-	"time"
 
 	"github.com/docker/docker/api/types"
-	containertypes "github.com/docker/docker/api/types/container"
+	"github.com/docker/docker/api/types/container"
 	"github.com/docker/docker/api/types/events"
 	"github.com/docker/docker/api/types/filters"
 	"github.com/docker/docker/api/types/image"
-	networktypes "github.com/docker/docker/api/types/network"
+	"github.com/docker/docker/api/types/network"
 	"github.com/docker/docker/api/types/registry"
 	"github.com/docker/docker/api/types/swarm"
-	volumetypes "github.com/docker/docker/api/types/volume"
+	"github.com/docker/docker/api/types/volume"
 	specs "github.com/opencontainers/image-spec/specs-go/v1"
 )
 
@@ -48,8 +47,8 @@ type CommonAPIClient interface {
 type ContainerAPIClient interface {
 	ContainerAttach(ctx context.Context, container string, options types.ContainerAttachOptions) (types.HijackedResponse, error)
 	ContainerCommit(ctx context.Context, container string, options types.ContainerCommitOptions) (types.IDResponse, error)
-	ContainerCreate(ctx context.Context, config *containertypes.Config, hostConfig *containertypes.HostConfig, networkingConfig *networktypes.NetworkingConfig, platform *specs.Platform, containerName string) (containertypes.ContainerCreateCreatedBody, error)
-	ContainerDiff(ctx context.Context, container string) ([]containertypes.ContainerChangeResponseItem, error)
+	ContainerCreate(ctx context.Context, config *container.Config, hostConfig *container.HostConfig, networkingConfig *network.NetworkingConfig, platform *specs.Platform, containerName string) (container.ContainerCreateCreatedBody, error)
+	ContainerDiff(ctx context.Context, container string) ([]container.ContainerChangeResponseItem, error)
 	ContainerExecAttach(ctx context.Context, execID string, config types.ExecStartCheck) (types.HijackedResponse, error)
 	ContainerExecCreate(ctx context.Context, container string, config types.ExecConfig) (types.IDResponse, error)
 	ContainerExecInspect(ctx context.Context, execID string) (types.ContainerExecInspect, error)
@@ -65,16 +64,16 @@ type ContainerAPIClient interface {
 	ContainerRemove(ctx context.Context, container string, options types.ContainerRemoveOptions) error
 	ContainerRename(ctx context.Context, container, newContainerName string) error
 	ContainerResize(ctx context.Context, container string, options types.ResizeOptions) error
-	ContainerRestart(ctx context.Context, container string, timeout *time.Duration) error
+	ContainerRestart(ctx context.Context, container string, options container.StopOptions) error
 	ContainerStatPath(ctx context.Context, container, path string) (types.ContainerPathStat, error)
 	ContainerStats(ctx context.Context, container string, stream bool) (types.ContainerStats, error)
 	ContainerStatsOneShot(ctx context.Context, container string) (types.ContainerStats, error)
 	ContainerStart(ctx context.Context, container string, options types.ContainerStartOptions) error
-	ContainerStop(ctx context.Context, container string, timeout *time.Duration) error
-	ContainerTop(ctx context.Context, container string, arguments []string) (containertypes.ContainerTopOKBody, error)
+	ContainerStop(ctx context.Context, container string, options container.StopOptions) error
+	ContainerTop(ctx context.Context, container string, arguments []string) (container.ContainerTopOKBody, error)
 	ContainerUnpause(ctx context.Context, container string) error
-	ContainerUpdate(ctx context.Context, container string, updateConfig containertypes.UpdateConfig) (containertypes.ContainerUpdateOKBody, error)
-	ContainerWait(ctx context.Context, container string, condition containertypes.WaitCondition) (<-chan containertypes.ContainerWaitOKBody, <-chan error)
+	ContainerUpdate(ctx context.Context, container string, updateConfig container.UpdateConfig) (container.ContainerUpdateOKBody, error)
+	ContainerWait(ctx context.Context, container string, condition container.WaitCondition) (<-chan container.ContainerWaitOKBody, <-chan error)
 	CopyFromContainer(ctx context.Context, container, srcPath string) (io.ReadCloser, types.ContainerPathStat, error)
 	CopyToContainer(ctx context.Context, container, path string, content io.Reader, options types.CopyToContainerOptions) error
 	ContainersPrune(ctx context.Context, pruneFilters filters.Args) (types.ContainersPruneReport, error)
@@ -107,7 +106,7 @@ type ImageAPIClient interface {
 
 // NetworkAPIClient defines API client methods for the networks
 type NetworkAPIClient interface {
-	NetworkConnect(ctx context.Context, network, container string, config *networktypes.EndpointSettings) error
+	NetworkConnect(ctx context.Context, network, container string, config *network.EndpointSettings) error
 	NetworkCreate(ctx context.Context, name string, options types.NetworkCreate) (types.NetworkCreateResponse, error)
 	NetworkDisconnect(ctx context.Context, network, container string, force bool) error
 	NetworkInspect(ctx context.Context, network string, options types.NetworkInspectOptions) (types.NetworkResource, error)
@@ -174,10 +173,10 @@ type SystemAPIClient interface {
 
 // VolumeAPIClient defines API client methods for the volumes
 type VolumeAPIClient interface {
-	VolumeCreate(ctx context.Context, options volumetypes.VolumeCreateBody) (types.Volume, error)
+	VolumeCreate(ctx context.Context, options volume.VolumeCreateBody) (types.Volume, error)
 	VolumeInspect(ctx context.Context, volumeID string) (types.Volume, error)
 	VolumeInspectWithRaw(ctx context.Context, volumeID string) (types.Volume, []byte, error)
-	VolumeList(ctx context.Context, filter filters.Args) (volumetypes.VolumeListOKBody, error)
+	VolumeList(ctx context.Context, filter filters.Args) (volume.VolumeListOKBody, error)
 	VolumeRemove(ctx context.Context, volumeID string, force bool) error
 	VolumesPrune(ctx context.Context, pruneFilter filters.Args) (types.VolumesPruneReport, error)
 }

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

@@ -35,8 +35,8 @@ type Backend interface {
 	ReleaseIngress() (<-chan struct{}, error)
 	CreateManagedContainer(config types.ContainerCreateConfig) (container.ContainerCreateCreatedBody, 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
 	ActivateContainerServiceBinding(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 {
+	var options = containertypes.StopOptions{}
 	// 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 {

+ 5 - 5
daemon/content.go

@@ -12,19 +12,19 @@ import (
 	"go.etcd.io/bbolt"
 )
 
-func (d *Daemon) configureLocalContentStore() (content.Store, leases.Manager, error) {
-	if err := os.MkdirAll(filepath.Join(d.root, "content"), 0700); err != nil {
+func (daemon *Daemon) configureLocalContentStore() (content.Store, leases.Manager, error) {
+	if err := os.MkdirAll(filepath.Join(daemon.root, "content"), 0700); err != nil {
 		return nil, nil, errors.Wrap(err, "error creating dir for content store")
 	}
-	db, err := bbolt.Open(filepath.Join(d.root, "content", "metadata.db"), 0600, nil)
+	db, err := bbolt.Open(filepath.Join(daemon.root, "content", "metadata.db"), 0600, nil)
 	if err != nil {
 		return nil, nil, errors.Wrap(err, "error opening bolt db for content metadata store")
 	}
-	cs, err := local.NewStore(filepath.Join(d.root, "content", "data"))
+	cs, err := local.NewStore(filepath.Join(daemon.root, "content", "data"))
 	if err != nil {
 		return nil, nil, errors.Wrap(err, "error setting up content store")
 	}
 	md := metadata.NewDB(db, cs, nil)
-	d.mdDB = db
+	daemon.mdDB = db
 	return md.ContentStore(), metadata.NewLeaseManager(md), nil
 }

+ 6 - 2
daemon/create.go

@@ -153,8 +153,12 @@ func (daemon *Daemon) create(opts createOpts) (retC *container.Container, retErr
 	}
 	defer func() {
 		if retErr != nil {
-			if err := daemon.cleanupContainer(ctr, true, true); err != nil {
-				logrus.Errorf("failed to cleanup container on create error: %v", err)
+			err = daemon.cleanupContainer(ctr, types.ContainerRmConfig{
+				ForceRemove:  true,
+				RemoveVolume: true,
+			})
+			if err != nil {
+				logrus.WithError(err).Error("failed to cleanup container on create error")
 			}
 		}
 	}()

+ 1 - 3
daemon/daemon.go

@@ -1116,10 +1116,8 @@ func (daemon *Daemon) waitForStartupDone() {
 }
 
 func (daemon *Daemon) shutdownContainer(c *container.Container) error {
-	stopTimeout := c.StopTimeout()
-
 	// If container failed to exit in stopTimeout seconds of SIGTERM, then using the force
-	if err := daemon.containerStop(c, stopTimeout); 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)
 	}
 

+ 24 - 10
daemon/delete.go

@@ -1,6 +1,7 @@
 package daemon // import "github.com/docker/docker/daemon"
 
 import (
+	"context"
 	"fmt"
 	"os"
 	"path"
@@ -8,6 +9,7 @@ import (
 	"time"
 
 	"github.com/docker/docker/api/types"
+	containertypes "github.com/docker/docker/api/types/container"
 	"github.com/docker/docker/container"
 	"github.com/docker/docker/errdefs"
 	"github.com/docker/docker/pkg/containerfs"
@@ -22,28 +24,28 @@ import (
 // network links are removed.
 func (daemon *Daemon) ContainerRm(name string, config *types.ContainerRmConfig) error {
 	start := time.Now()
-	container, err := daemon.GetContainer(name)
+	ctr, err := daemon.GetContainer(name)
 	if err != nil {
 		return err
 	}
 
 	// Container state RemovalInProgress should be used to avoid races.
-	if inProgress := container.SetRemovalInProgress(); inProgress {
+	if inProgress := ctr.SetRemovalInProgress(); inProgress {
 		err := fmt.Errorf("removal of container %s is already in progress", name)
 		return errdefs.Conflict(err)
 	}
-	defer container.ResetRemovalInProgress()
+	defer ctr.ResetRemovalInProgress()
 
 	// check if container wasn't deregistered by previous rm since Get
-	if c := daemon.containers.Get(container.ID); c == nil {
+	if c := daemon.containers.Get(ctr.ID); c == nil {
 		return nil
 	}
 
 	if config.RemoveLink {
-		return daemon.rmLink(container, name)
+		return daemon.rmLink(ctr, name)
 	}
 
-	err = daemon.cleanupContainer(container, config.ForceRemove, config.RemoveVolume)
+	err = daemon.cleanupContainer(ctr, *config)
 	containerActions.WithValues("delete").UpdateSince(start)
 
 	return err
@@ -77,9 +79,9 @@ func (daemon *Daemon) rmLink(container *container.Container, name string) error
 
 // cleanupContainer unregisters a container from the daemon, stops stats
 // collection and cleanly removes contents and metadata from the filesystem.
-func (daemon *Daemon) cleanupContainer(container *container.Container, forceRemove, removeVolume bool) error {
+func (daemon *Daemon) cleanupContainer(container *container.Container, config types.ContainerRmConfig) error {
 	if container.IsRunning() {
-		if !forceRemove {
+		if !config.ForceRemove {
 			state := container.StateString()
 			procedure := "Stop the container before attempting removal or force remove"
 			if state == "paused" {
@@ -97,7 +99,19 @@ func (daemon *Daemon) cleanupContainer(container *container.Container, forceRemo
 	// if stats are currently getting collected.
 	daemon.statsCollector.StopCollection(container)
 
-	if err := daemon.containerStop(container, 3); err != nil {
+	// stopTimeout is the number of seconds to wait for the container to stop
+	// gracefully before forcibly killing it.
+	//
+	// Why 3 seconds? The timeout specified here was originally added in commit
+	// 1615bb08c7c3fc6c4b22db0a633edda516f97cf0, which added a custom timeout to
+	// some commands, but lacking an option for a timeout on "docker rm", was
+	// hardcoded to 10 seconds. Commit 28fd289b448164b77affd8103c0d96fd8110daf9
+	// later on updated this to 3 seconds (but no background on that change).
+	//
+	// If you arrived here and know the answer, you earned yourself a picture
+	// of a cute animal of your own choosing.
+	var stopTimeout = 3
+	if err := daemon.containerStop(context.TODO(), container, containertypes.StopOptions{Timeout: &stopTimeout}); err != nil {
 		return err
 	}
 
@@ -135,7 +149,7 @@ func (daemon *Daemon) cleanupContainer(container *container.Container, forceRemo
 	daemon.idIndex.Delete(container.ID)
 	daemon.containers.Delete(container.ID)
 	daemon.containersReplica.Delete(container)
-	if err := daemon.removeMountPoints(container, removeVolume); err != nil {
+	if err := daemon.removeMountPoints(container, config.RemoveVolume); err != nil {
 		logrus.Error(err)
 	}
 	for _, name := range linkNames {

+ 6 - 9
daemon/restart.go

@@ -1,6 +1,7 @@
 package daemon // import "github.com/docker/docker/daemon"
 
 import (
+	"context"
 	"fmt"
 
 	containertypes "github.com/docker/docker/api/types/container"
@@ -14,16 +15,13 @@ import (
 // timeout, ContainerRestart will wait forever until a graceful
 // stop. Returns an error if the container cannot be found, or if
 // 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)
 	if err != nil {
 		return err
 	}
-	if seconds == nil {
-		stopTimeout := ctr.StopTimeout()
-		seconds = &stopTimeout
-	}
-	if err := daemon.containerRestart(ctr, *seconds); err != nil {
+	err = daemon.containerRestart(ctx, ctr, options)
+	if err != nil {
 		return fmt.Errorf("Cannot restart container %s: %v", name, err)
 	}
 	return nil
@@ -34,8 +32,7 @@ func (daemon *Daemon) ContainerRestart(name string, seconds *int) error {
 // container. When stopping, wait for the given duration in seconds to
 // gracefully stop, before forcefully terminating the container. If
 // 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.
 	actualIsolation := container.HostConfig.Isolation
 	if containertypes.Isolation.IsDefault(actualIsolation) {
@@ -60,7 +57,7 @@ func (daemon *Daemon) containerRestart(container *container.Container, seconds i
 		autoRemove := container.HostConfig.AutoRemove
 
 		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
 		container.HostConfig.AutoRemove = autoRemove
 		// containerStop will write HostConfig to disk, we shall restore AutoRemove

+ 29 - 29
daemon/seccomp_linux_test.go

@@ -7,10 +7,10 @@ import (
 	"testing"
 
 	coci "github.com/containerd/containerd/oci"
-	config "github.com/docker/docker/api/types/container"
+	containertypes "github.com/docker/docker/api/types/container"
 	"github.com/docker/docker/container"
 	dconfig "github.com/docker/docker/daemon/config"
-	doci "github.com/docker/docker/oci"
+	"github.com/docker/docker/oci"
 	"github.com/docker/docker/pkg/sysinfo"
 	"github.com/docker/docker/profiles/seccomp"
 	specs "github.com/opencontainers/runtime-spec/specs-go"
@@ -36,12 +36,12 @@ func TestWithSeccomp(t *testing.T) {
 			},
 			c: &container.Container{
 				SeccompProfile: dconfig.SeccompProfileUnconfined,
-				HostConfig: &config.HostConfig{
+				HostConfig: &containertypes.HostConfig{
 					Privileged: false,
 				},
 			},
-			inSpec:  doci.DefaultLinuxSpec(),
-			outSpec: doci.DefaultLinuxSpec(),
+			inSpec:  oci.DefaultLinuxSpec(),
+			outSpec: oci.DefaultLinuxSpec(),
 		},
 		{
 			comment: "privileged container w/ custom profile runs unconfined",
@@ -50,12 +50,12 @@ func TestWithSeccomp(t *testing.T) {
 			},
 			c: &container.Container{
 				SeccompProfile: "{ \"defaultAction\": \"SCMP_ACT_LOG\" }",
-				HostConfig: &config.HostConfig{
+				HostConfig: &containertypes.HostConfig{
 					Privileged: true,
 				},
 			},
-			inSpec:  doci.DefaultLinuxSpec(),
-			outSpec: doci.DefaultLinuxSpec(),
+			inSpec:  oci.DefaultLinuxSpec(),
+			outSpec: oci.DefaultLinuxSpec(),
 		},
 		{
 			comment: "privileged container w/ default runs unconfined",
@@ -64,12 +64,12 @@ func TestWithSeccomp(t *testing.T) {
 			},
 			c: &container.Container{
 				SeccompProfile: "",
-				HostConfig: &config.HostConfig{
+				HostConfig: &containertypes.HostConfig{
 					Privileged: true,
 				},
 			},
-			inSpec:  doci.DefaultLinuxSpec(),
-			outSpec: doci.DefaultLinuxSpec(),
+			inSpec:  oci.DefaultLinuxSpec(),
+			outSpec: oci.DefaultLinuxSpec(),
 		},
 		{
 			comment: "privileged container w/ daemon profile runs unconfined",
@@ -79,12 +79,12 @@ func TestWithSeccomp(t *testing.T) {
 			},
 			c: &container.Container{
 				SeccompProfile: "",
-				HostConfig: &config.HostConfig{
+				HostConfig: &containertypes.HostConfig{
 					Privileged: true,
 				},
 			},
-			inSpec:  doci.DefaultLinuxSpec(),
-			outSpec: doci.DefaultLinuxSpec(),
+			inSpec:  oci.DefaultLinuxSpec(),
+			outSpec: oci.DefaultLinuxSpec(),
 		},
 		{
 			comment: "custom profile when seccomp is disabled returns error",
@@ -93,12 +93,12 @@ func TestWithSeccomp(t *testing.T) {
 			},
 			c: &container.Container{
 				SeccompProfile: "{ \"defaultAction\": \"SCMP_ACT_ERRNO\" }",
-				HostConfig: &config.HostConfig{
+				HostConfig: &containertypes.HostConfig{
 					Privileged: false,
 				},
 			},
-			inSpec:  doci.DefaultLinuxSpec(),
-			outSpec: doci.DefaultLinuxSpec(),
+			inSpec:  oci.DefaultLinuxSpec(),
+			outSpec: oci.DefaultLinuxSpec(),
 			err:     "seccomp is not enabled in your kernel, cannot run a custom seccomp profile",
 		},
 		{
@@ -108,13 +108,13 @@ func TestWithSeccomp(t *testing.T) {
 			},
 			c: &container.Container{
 				SeccompProfile: "",
-				HostConfig: &config.HostConfig{
+				HostConfig: &containertypes.HostConfig{
 					Privileged: false,
 				},
 			},
-			inSpec: doci.DefaultLinuxSpec(),
+			inSpec: oci.DefaultLinuxSpec(),
 			outSpec: func() coci.Spec {
-				s := doci.DefaultLinuxSpec()
+				s := oci.DefaultLinuxSpec()
 				profile, _ := seccomp.GetDefaultProfile(&s)
 				s.Linux.Seccomp = profile
 				return s
@@ -127,13 +127,13 @@ func TestWithSeccomp(t *testing.T) {
 			},
 			c: &container.Container{
 				SeccompProfile: "{ \"defaultAction\": \"SCMP_ACT_ERRNO\" }",
-				HostConfig: &config.HostConfig{
+				HostConfig: &containertypes.HostConfig{
 					Privileged: false,
 				},
 			},
-			inSpec: doci.DefaultLinuxSpec(),
+			inSpec: oci.DefaultLinuxSpec(),
 			outSpec: func() coci.Spec {
-				s := doci.DefaultLinuxSpec()
+				s := oci.DefaultLinuxSpec()
 				profile := &specs.LinuxSeccomp{
 					DefaultAction: specs.LinuxSeccompAction("SCMP_ACT_ERRNO"),
 				}
@@ -149,13 +149,13 @@ func TestWithSeccomp(t *testing.T) {
 			},
 			c: &container.Container{
 				SeccompProfile: "",
-				HostConfig: &config.HostConfig{
+				HostConfig: &containertypes.HostConfig{
 					Privileged: false,
 				},
 			},
-			inSpec: doci.DefaultLinuxSpec(),
+			inSpec: oci.DefaultLinuxSpec(),
 			outSpec: func() coci.Spec {
-				s := doci.DefaultLinuxSpec()
+				s := oci.DefaultLinuxSpec()
 				profile := &specs.LinuxSeccomp{
 					DefaultAction: specs.LinuxSeccompAction("SCMP_ACT_ERRNO"),
 				}
@@ -171,13 +171,13 @@ func TestWithSeccomp(t *testing.T) {
 			},
 			c: &container.Container{
 				SeccompProfile: "{ \"defaultAction\": \"SCMP_ACT_LOG\" }",
-				HostConfig: &config.HostConfig{
+				HostConfig: &containertypes.HostConfig{
 					Privileged: false,
 				},
 			},
-			inSpec: doci.DefaultLinuxSpec(),
+			inSpec: oci.DefaultLinuxSpec(),
 			outSpec: func() coci.Spec {
-				s := doci.DefaultLinuxSpec()
+				s := oci.DefaultLinuxSpec()
 				profile := &specs.LinuxSeccomp{
 					DefaultAction: specs.LinuxSeccompAction("SCMP_ACT_LOG"),
 				}

+ 50 - 36
daemon/stop.go

@@ -4,8 +4,10 @@ import (
 	"context"
 	"time"
 
-	containerpkg "github.com/docker/docker/container"
+	containertypes "github.com/docker/docker/api/types/container"
+	"github.com/docker/docker/container"
 	"github.com/docker/docker/errdefs"
+	"github.com/moby/sys/signal"
 	"github.com/pkg/errors"
 	"github.com/sirupsen/logrus"
 )
@@ -18,84 +20,96 @@ import (
 // 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,
 // meaning no timeout, i.e. no forceful termination is performed.
-func (daemon *Daemon) ContainerStop(name string, timeout *int) error {
-	container, err := daemon.GetContainer(name)
+func (daemon *Daemon) ContainerStop(ctx context.Context, name string, options containertypes.StopOptions) error {
+	ctr, err := daemon.GetContainer(name)
 	if err != nil {
 		return err
 	}
-	if !container.IsRunning() {
-		return containerNotModifiedError{running: false}
+	if !ctr.IsRunning() {
+		return containerNotModifiedError{}
 	}
-	if timeout == nil {
-		stopTimeout := container.StopTimeout()
-		timeout = &stopTimeout
-	}
-	if err := daemon.containerStop(container, *timeout); err != nil {
+	err = daemon.containerStop(ctx, ctr, options)
+	if err != nil {
 		return errdefs.System(errors.Wrapf(err, "cannot stop container: %s", name))
 	}
 	return nil
 }
 
 // containerStop sends a stop signal, waits, sends a kill signal.
-func (daemon *Daemon) containerStop(container *containerpkg.Container, seconds int) error {
-	// TODO propagate a context down to this function
-	ctx := context.TODO()
-	if !container.IsRunning() {
+func (daemon *Daemon) containerStop(ctx context.Context, ctr *container.Container, options containertypes.StopOptions) (retErr error) {
+	if !ctr.IsRunning() {
 		return nil
 	}
-	var wait time.Duration
-	if seconds >= 0 {
-		wait = time.Duration(seconds) * time.Second
+
+	var (
+		stopSignal  = ctr.StopSignal()
+		stopTimeout = ctr.StopTimeout()
+	)
+	if options.Signal != "" {
+		sig, err := signal.ParseSignal(options.Signal)
+		if err != nil {
+			return errdefs.InvalidParameter(err)
+		}
+		stopSignal = int(sig)
 	}
-	success := func() error {
-		daemon.LogContainerEvent(container, "stop")
-		return nil
+	if options.Timeout != nil {
+		stopTimeout = *options.Timeout
+	}
+
+	var wait time.Duration
+	if stopTimeout >= 0 {
+		wait = time.Duration(stopTimeout) * time.Second
 	}
-	stopSignal := container.StopSignal()
+	defer func() {
+		if retErr == nil {
+			daemon.LogContainerEvent(ctr, "stop")
+		}
+	}()
 
 	// 1. Send a stop signal
-	err := daemon.killPossiblyDeadProcess(container, stopSignal)
+	err := daemon.killPossiblyDeadProcess(ctr, stopSignal)
 	if err != nil {
 		wait = 2 * time.Second
 	}
 
 	var subCtx context.Context
 	var cancel context.CancelFunc
-	if seconds >= 0 {
+	if stopTimeout >= 0 {
 		subCtx, cancel = context.WithTimeout(ctx, wait)
 	} else {
 		subCtx, cancel = context.WithCancel(ctx)
 	}
 	defer cancel()
 
-	if status := <-container.Wait(subCtx, containerpkg.WaitConditionNotRunning); status.Err() == nil {
+	if status := <-ctr.Wait(subCtx, container.WaitConditionNotRunning); status.Err() == nil {
 		// container did exit, so ignore any previous errors and return
-		return success()
+		return nil
 	}
 
 	if err != nil {
 		// the container has still not exited, and the kill function errored, so log the error here:
-		logrus.WithError(err).WithField("container", container.ID).Errorf("Error sending stop (signal %d) to container", stopSignal)
+		logrus.WithError(err).WithField("container", ctr.ID).Errorf("Error sending stop (signal %d) to container", stopSignal)
 	}
-	if seconds < 0 {
+	if stopTimeout < 0 {
 		// if the client requested that we never kill / wait forever, but container.Wait was still
 		// interrupted (parent context cancelled, for example), we should propagate the signal failure
 		return err
 	}
 
-	logrus.WithField("container", container.ID).Infof("Container failed to exit within %s of signal %d - using the force", wait, stopSignal)
-	// Stop either failed or container didnt exit, so fallback to kill.
-	if err := daemon.Kill(container); err != nil {
+	logrus.WithField("container", ctr.ID).Infof("Container failed to exit within %s of signal %d - using the force", wait, stopSignal)
+
+	// Stop either failed or container didn't exit, so fallback to kill.
+	if err := daemon.Kill(ctr); err != nil {
 		// got a kill error, but give container 2 more seconds to exit just in case
 		subCtx, cancel := context.WithTimeout(ctx, 2*time.Second)
 		defer cancel()
-		if status := <-container.Wait(subCtx, containerpkg.WaitConditionNotRunning); status.Err() == nil {
-			// container did exit, so ignore error and return
-			return success()
+		status := <-ctr.Wait(subCtx, container.WaitConditionNotRunning)
+		if status.Err() != nil {
+			logrus.WithError(err).WithField("container", ctr.ID).Errorf("error killing container: %v", status.Err())
+			return err
 		}
-		logrus.WithError(err).WithField("container", container.ID).Error("Error killing the container")
-		return err
+		// container did exit, so ignore previous errors and continue
 	}
 
-	return success()
+	return nil
 }

+ 3 - 0
docs/api/version-history.md

@@ -21,6 +21,9 @@ keywords: "API, Docker, rcli, REST, documentation"
   was introduced in API 1.31 as part of an experimental feature, and no longer
   used since API 1.40.
   Use field `BuildCache` instead to track storage used by the builder component.
+* `POST /containers/{id}/stop` and `POST /containers/{id}/restart` now accept a
+  `signal` query parameter, which allows overriding the container's default stop-
+  signal.
 * `GET /images/json` now accepts query parameter `shared-size`. When set `true`,
   images returned will include `SharedSize`, which provides the size on disk shared
   with other images present on the system.

+ 168 - 164
integration-cli/docker_api_containers_test.go

@@ -17,9 +17,9 @@ import (
 	"time"
 
 	"github.com/docker/docker/api/types"
-	containertypes "github.com/docker/docker/api/types/container"
-	mounttypes "github.com/docker/docker/api/types/mount"
-	networktypes "github.com/docker/docker/api/types/network"
+	"github.com/docker/docker/api/types/container"
+	"github.com/docker/docker/api/types/mount"
+	"github.com/docker/docker/api/types/network"
 	"github.com/docker/docker/api/types/versions"
 	"github.com/docker/docker/client"
 	dconfig "github.com/docker/docker/daemon/config"
@@ -476,7 +476,7 @@ func (s *DockerSuite) TestContainerAPICommitWithLabelInConfig(c *testing.T) {
 	assert.NilError(c, err)
 	defer cli.Close()
 
-	config := containertypes.Config{
+	config := container.Config{
 		Labels: map[string]string{"key1": "value1", "key2": "value2"}}
 
 	options := types.ContainerCommitOptions{
@@ -504,12 +504,12 @@ func (s *DockerSuite) TestContainerAPIBadPort(c *testing.T) {
 	// TODO Windows to Windows CI - Port this test
 	testRequires(c, DaemonIsLinux)
 
-	config := containertypes.Config{
+	config := container.Config{
 		Image: "busybox",
 		Cmd:   []string{"/bin/sh", "-c", "echo test"},
 	}
 
-	hostConfig := containertypes.HostConfig{
+	hostConfig := container.HostConfig{
 		PortBindings: nat.PortMap{
 			"8080/tcp": []nat.PortBinding{
 				{
@@ -523,12 +523,12 @@ func (s *DockerSuite) TestContainerAPIBadPort(c *testing.T) {
 	assert.NilError(c, err)
 	defer cli.Close()
 
-	_, err = cli.ContainerCreate(context.Background(), &config, &hostConfig, &networktypes.NetworkingConfig{}, nil, "")
+	_, err = cli.ContainerCreate(context.Background(), &config, &hostConfig, &network.NetworkingConfig{}, nil, "")
 	assert.ErrorContains(c, err, `invalid port specification: "aa80"`)
 }
 
 func (s *DockerSuite) TestContainerAPICreate(c *testing.T) {
-	config := containertypes.Config{
+	config := container.Config{
 		Image: "busybox",
 		Cmd:   []string{"/bin/sh", "-c", "touch /test && ls /test"},
 	}
@@ -537,7 +537,7 @@ func (s *DockerSuite) TestContainerAPICreate(c *testing.T) {
 	assert.NilError(c, err)
 	defer cli.Close()
 
-	container, err := cli.ContainerCreate(context.Background(), &config, &containertypes.HostConfig{}, &networktypes.NetworkingConfig{}, nil, "")
+	container, err := cli.ContainerCreate(context.Background(), &config, &container.HostConfig{}, &network.NetworkingConfig{}, nil, "")
 	assert.NilError(c, err)
 
 	out, _ := dockerCmd(c, "start", "-a", container.ID)
@@ -550,7 +550,7 @@ func (s *DockerSuite) TestContainerAPICreateEmptyConfig(c *testing.T) {
 	assert.NilError(c, err)
 	defer cli.Close()
 
-	_, err = cli.ContainerCreate(context.Background(), &containertypes.Config{}, &containertypes.HostConfig{}, &networktypes.NetworkingConfig{}, nil, "")
+	_, err = cli.ContainerCreate(context.Background(), &container.Config{}, &container.HostConfig{}, &network.NetworkingConfig{}, nil, "")
 
 	expected := "No command specified"
 	assert.ErrorContains(c, err, expected)
@@ -558,12 +558,12 @@ func (s *DockerSuite) TestContainerAPICreateEmptyConfig(c *testing.T) {
 
 func (s *DockerSuite) TestContainerAPICreateMultipleNetworksConfig(c *testing.T) {
 	// Container creation must fail if client specified configurations for more than one network
-	config := containertypes.Config{
+	config := container.Config{
 		Image: "busybox",
 	}
 
-	networkingConfig := networktypes.NetworkingConfig{
-		EndpointsConfig: map[string]*networktypes.EndpointSettings{
+	networkingConfig := network.NetworkingConfig{
+		EndpointsConfig: map[string]*network.EndpointSettings{
 			"net1": {},
 			"net2": {},
 			"net3": {},
@@ -574,7 +574,7 @@ func (s *DockerSuite) TestContainerAPICreateMultipleNetworksConfig(c *testing.T)
 	assert.NilError(c, err)
 	defer cli.Close()
 
-	_, err = cli.ContainerCreate(context.Background(), &config, &containertypes.HostConfig{}, &networkingConfig, nil, "")
+	_, err = cli.ContainerCreate(context.Background(), &config, &container.HostConfig{}, &networkingConfig, nil, "")
 	msg := err.Error()
 	// network name order in error message is not deterministic
 	assert.Assert(c, strings.Contains(msg, "Container cannot be connected to network endpoints"))
@@ -596,12 +596,12 @@ func (s *DockerSuite) TestContainerAPICreateOtherNetworkModes(c *testing.T) {
 	UtilCreateNetworkMode(c, "container:web1")
 }
 
-func UtilCreateNetworkMode(c *testing.T, networkMode containertypes.NetworkMode) {
-	config := containertypes.Config{
+func UtilCreateNetworkMode(c *testing.T, networkMode container.NetworkMode) {
+	config := container.Config{
 		Image: "busybox",
 	}
 
-	hostConfig := containertypes.HostConfig{
+	hostConfig := container.HostConfig{
 		NetworkMode: networkMode,
 	}
 
@@ -609,7 +609,7 @@ func UtilCreateNetworkMode(c *testing.T, networkMode containertypes.NetworkMode)
 	assert.NilError(c, err)
 	defer cli.Close()
 
-	container, err := cli.ContainerCreate(context.Background(), &config, &hostConfig, &networktypes.NetworkingConfig{}, nil, "")
+	container, err := cli.ContainerCreate(context.Background(), &config, &hostConfig, &network.NetworkingConfig{}, nil, "")
 	assert.NilError(c, err)
 
 	containerJSON, err := cli.ContainerInspect(context.Background(), container.ID)
@@ -621,12 +621,12 @@ func UtilCreateNetworkMode(c *testing.T, networkMode containertypes.NetworkMode)
 func (s *DockerSuite) TestContainerAPICreateWithCpuSharesCpuset(c *testing.T) {
 	// TODO Windows to Windows CI. The CpuShares part could be ported.
 	testRequires(c, DaemonIsLinux)
-	config := containertypes.Config{
+	config := container.Config{
 		Image: "busybox",
 	}
 
-	hostConfig := containertypes.HostConfig{
-		Resources: containertypes.Resources{
+	hostConfig := container.HostConfig{
+		Resources: container.Resources{
 			CPUShares:  512,
 			CpusetCpus: "0",
 		},
@@ -636,7 +636,7 @@ func (s *DockerSuite) TestContainerAPICreateWithCpuSharesCpuset(c *testing.T) {
 	assert.NilError(c, err)
 	defer cli.Close()
 
-	container, err := cli.ContainerCreate(context.Background(), &config, &hostConfig, &networktypes.NetworkingConfig{}, nil, "")
+	container, err := cli.ContainerCreate(context.Background(), &config, &hostConfig, &network.NetworkingConfig{}, nil, "")
 	assert.NilError(c, err)
 
 	containerJSON, err := cli.ContainerInspect(context.Background(), container.ID)
@@ -913,8 +913,8 @@ func (s *DockerSuite) TestContainerAPIRestart(c *testing.T) {
 	assert.NilError(c, err)
 	defer cli.Close()
 
-	timeout := 1 * time.Second
-	err = cli.ContainerRestart(context.Background(), name, &timeout)
+	timeout := 1
+	err = cli.ContainerRestart(context.Background(), name, container.StopOptions{Timeout: &timeout})
 	assert.NilError(c, err)
 
 	assert.Assert(c, waitInspect(name, "{{ .State.Restarting  }} {{ .State.Running  }}", "false true", 15*time.Second) == nil)
@@ -930,7 +930,7 @@ func (s *DockerSuite) TestContainerAPIRestartNotimeoutParam(c *testing.T) {
 	assert.NilError(c, err)
 	defer cli.Close()
 
-	err = cli.ContainerRestart(context.Background(), name, nil)
+	err = cli.ContainerRestart(context.Background(), name, container.StopOptions{})
 	assert.NilError(c, err)
 
 	assert.Assert(c, waitInspect(name, "{{ .State.Restarting  }} {{ .State.Running  }}", "false true", 15*time.Second) == nil)
@@ -938,7 +938,7 @@ func (s *DockerSuite) TestContainerAPIRestartNotimeoutParam(c *testing.T) {
 
 func (s *DockerSuite) TestContainerAPIStart(c *testing.T) {
 	name := "testing-start"
-	config := containertypes.Config{
+	config := container.Config{
 		Image:     "busybox",
 		Cmd:       append([]string{"/bin/sh", "-c"}, sleepCommandForDaemonPlatform()...),
 		OpenStdin: true,
@@ -948,7 +948,7 @@ func (s *DockerSuite) TestContainerAPIStart(c *testing.T) {
 	assert.NilError(c, err)
 	defer cli.Close()
 
-	_, err = cli.ContainerCreate(context.Background(), &config, &containertypes.HostConfig{}, &networktypes.NetworkingConfig{}, nil, name)
+	_, err = cli.ContainerCreate(context.Background(), &config, &container.HostConfig{}, &network.NetworkingConfig{}, nil, name)
 	assert.NilError(c, err)
 
 	err = cli.ContainerStart(context.Background(), name, types.ContainerStartOptions{})
@@ -965,19 +965,23 @@ func (s *DockerSuite) TestContainerAPIStart(c *testing.T) {
 func (s *DockerSuite) TestContainerAPIStop(c *testing.T) {
 	name := "test-api-stop"
 	runSleepingContainer(c, "-i", "--name", name)
-	timeout := 30 * time.Second
+	timeout := 30
 
 	cli, err := client.NewClientWithOpts(client.FromEnv)
 	assert.NilError(c, err)
 	defer cli.Close()
 
-	err = cli.ContainerStop(context.Background(), name, &timeout)
+	err = cli.ContainerStop(context.Background(), name, container.StopOptions{
+		Timeout: &timeout,
+	})
 	assert.NilError(c, err)
 	assert.Assert(c, waitInspect(name, "{{ .State.Running  }}", "false", 60*time.Second) == nil)
 
 	// second call to start should give 304
 	// maybe add ContainerStartWithRaw to test it
-	err = cli.ContainerStop(context.Background(), name, &timeout)
+	err = cli.ContainerStop(context.Background(), name, container.StopOptions{
+		Timeout: &timeout,
+	})
 	assert.NilError(c, err)
 }
 
@@ -1255,14 +1259,14 @@ func (s *DockerSuite) TestContainerAPIPostContainerStop(c *testing.T) {
 	assert.NilError(c, err)
 	defer cli.Close()
 
-	err = cli.ContainerStop(context.Background(), containerID, nil)
+	err = cli.ContainerStop(context.Background(), containerID, container.StopOptions{})
 	assert.NilError(c, err)
 	assert.Assert(c, waitInspect(containerID, "{{ .State.Running  }}", "false", 60*time.Second) == nil)
 }
 
 // #14170
 func (s *DockerSuite) TestPostContainerAPICreateWithStringOrSliceEntrypoint(c *testing.T) {
-	config := containertypes.Config{
+	config := container.Config{
 		Image:      "busybox",
 		Entrypoint: []string{"echo"},
 		Cmd:        []string{"hello", "world"},
@@ -1272,7 +1276,7 @@ func (s *DockerSuite) TestPostContainerAPICreateWithStringOrSliceEntrypoint(c *t
 	assert.NilError(c, err)
 	defer cli.Close()
 
-	_, err = cli.ContainerCreate(context.Background(), &config, &containertypes.HostConfig{}, &networktypes.NetworkingConfig{}, nil, "echotest")
+	_, err = cli.ContainerCreate(context.Background(), &config, &container.HostConfig{}, &network.NetworkingConfig{}, nil, "echotest")
 	assert.NilError(c, err)
 	out, _ := dockerCmd(c, "start", "-a", "echotest")
 	assert.Equal(c, strings.TrimSpace(out), "hello world")
@@ -1290,7 +1294,7 @@ func (s *DockerSuite) TestPostContainerAPICreateWithStringOrSliceEntrypoint(c *t
 
 // #14170
 func (s *DockerSuite) TestPostContainersCreateWithStringOrSliceCmd(c *testing.T) {
-	config := containertypes.Config{
+	config := container.Config{
 		Image: "busybox",
 		Cmd:   []string{"echo", "hello", "world"},
 	}
@@ -1299,7 +1303,7 @@ func (s *DockerSuite) TestPostContainersCreateWithStringOrSliceCmd(c *testing.T)
 	assert.NilError(c, err)
 	defer cli.Close()
 
-	_, err = cli.ContainerCreate(context.Background(), &config, &containertypes.HostConfig{}, &networktypes.NetworkingConfig{}, nil, "echotest")
+	_, err = cli.ContainerCreate(context.Background(), &config, &container.HostConfig{}, &network.NetworkingConfig{}, nil, "echotest")
 	assert.NilError(c, err)
 	out, _ := dockerCmd(c, "start", "-a", "echotest")
 	assert.Equal(c, strings.TrimSpace(out), "hello world")
@@ -1330,10 +1334,10 @@ func (s *DockerSuite) TestPostContainersCreateWithStringOrSliceCapAddDrop(c *tes
 	assert.NilError(c, err)
 	assert.Equal(c, res.StatusCode, http.StatusCreated)
 
-	config2 := containertypes.Config{
+	config2 := container.Config{
 		Image: "busybox",
 	}
-	hostConfig := containertypes.HostConfig{
+	hostConfig := container.HostConfig{
 		CapAdd:  []string{"net_admin", "SYS_ADMIN"},
 		CapDrop: []string{"SETGID", "CAP_SETPCAP"},
 	}
@@ -1342,21 +1346,21 @@ func (s *DockerSuite) TestPostContainersCreateWithStringOrSliceCapAddDrop(c *tes
 	assert.NilError(c, err)
 	defer cli.Close()
 
-	_, err = cli.ContainerCreate(context.Background(), &config2, &hostConfig, &networktypes.NetworkingConfig{}, nil, "capaddtest1")
+	_, err = cli.ContainerCreate(context.Background(), &config2, &hostConfig, &network.NetworkingConfig{}, nil, "capaddtest1")
 	assert.NilError(c, err)
 }
 
 // #14915
 func (s *DockerSuite) TestContainerAPICreateNoHostConfig118(c *testing.T) {
 	testRequires(c, DaemonIsLinux) // Windows only support 1.25 or later
-	config := containertypes.Config{
+	config := container.Config{
 		Image: "busybox",
 	}
 
 	cli, err := client.NewClientWithOpts(client.FromEnv, client.WithVersion("v1.18"))
 	assert.NilError(c, err)
 
-	_, err = cli.ContainerCreate(context.Background(), &config, &containertypes.HostConfig{}, &networktypes.NetworkingConfig{}, nil, "")
+	_, err = cli.ContainerCreate(context.Background(), &config, &container.HostConfig{}, &network.NetworkingConfig{}, nil, "")
 	assert.NilError(c, err)
 }
 
@@ -1397,27 +1401,27 @@ func (s *DockerSuite) TestPostContainersCreateWithWrongCpusetValues(c *testing.T
 	assert.NilError(c, err)
 	defer cli.Close()
 
-	config := containertypes.Config{
+	config := container.Config{
 		Image: "busybox",
 	}
-	hostConfig1 := containertypes.HostConfig{
-		Resources: containertypes.Resources{
+	hostConfig1 := container.HostConfig{
+		Resources: container.Resources{
 			CpusetCpus: "1-42,,",
 		},
 	}
 	name := "wrong-cpuset-cpus"
 
-	_, err = cli.ContainerCreate(context.Background(), &config, &hostConfig1, &networktypes.NetworkingConfig{}, nil, name)
+	_, err = cli.ContainerCreate(context.Background(), &config, &hostConfig1, &network.NetworkingConfig{}, nil, name)
 	expected := "Invalid value 1-42,, for cpuset cpus"
 	assert.ErrorContains(c, err, expected)
 
-	hostConfig2 := containertypes.HostConfig{
-		Resources: containertypes.Resources{
+	hostConfig2 := container.HostConfig{
+		Resources: container.Resources{
 			CpusetMems: "42-3,1--",
 		},
 	}
 	name = "wrong-cpuset-mems"
-	_, err = cli.ContainerCreate(context.Background(), &config, &hostConfig2, &networktypes.NetworkingConfig{}, nil, name)
+	_, err = cli.ContainerCreate(context.Background(), &config, &hostConfig2, &network.NetworkingConfig{}, nil, name)
 	expected = "Invalid value 42-3,1-- for cpuset mems"
 	assert.ErrorContains(c, err, expected)
 }
@@ -1425,10 +1429,10 @@ func (s *DockerSuite) TestPostContainersCreateWithWrongCpusetValues(c *testing.T
 func (s *DockerSuite) TestPostContainersCreateShmSizeNegative(c *testing.T) {
 	// ShmSize is not supported on Windows
 	testRequires(c, DaemonIsLinux)
-	config := containertypes.Config{
+	config := container.Config{
 		Image: "busybox",
 	}
-	hostConfig := containertypes.HostConfig{
+	hostConfig := container.HostConfig{
 		ShmSize: -1,
 	}
 
@@ -1436,7 +1440,7 @@ func (s *DockerSuite) TestPostContainersCreateShmSizeNegative(c *testing.T) {
 	assert.NilError(c, err)
 	defer cli.Close()
 
-	_, err = cli.ContainerCreate(context.Background(), &config, &hostConfig, &networktypes.NetworkingConfig{}, nil, "")
+	_, err = cli.ContainerCreate(context.Background(), &config, &hostConfig, &network.NetworkingConfig{}, nil, "")
 	assert.ErrorContains(c, err, "SHM size can not be less than 0")
 }
 
@@ -1444,7 +1448,7 @@ func (s *DockerSuite) TestPostContainersCreateShmSizeHostConfigOmitted(c *testin
 	// ShmSize is not supported on Windows
 	testRequires(c, DaemonIsLinux)
 
-	config := containertypes.Config{
+	config := container.Config{
 		Image: "busybox",
 		Cmd:   []string{"mount"},
 	}
@@ -1453,7 +1457,7 @@ func (s *DockerSuite) TestPostContainersCreateShmSizeHostConfigOmitted(c *testin
 	assert.NilError(c, err)
 	defer cli.Close()
 
-	container, err := cli.ContainerCreate(context.Background(), &config, &containertypes.HostConfig{}, &networktypes.NetworkingConfig{}, nil, "")
+	container, err := cli.ContainerCreate(context.Background(), &config, &container.HostConfig{}, &network.NetworkingConfig{}, nil, "")
 	assert.NilError(c, err)
 
 	containerJSON, err := cli.ContainerInspect(context.Background(), container.ID)
@@ -1471,7 +1475,7 @@ func (s *DockerSuite) TestPostContainersCreateShmSizeHostConfigOmitted(c *testin
 func (s *DockerSuite) TestPostContainersCreateShmSizeOmitted(c *testing.T) {
 	// ShmSize is not supported on Windows
 	testRequires(c, DaemonIsLinux)
-	config := containertypes.Config{
+	config := container.Config{
 		Image: "busybox",
 		Cmd:   []string{"mount"},
 	}
@@ -1480,7 +1484,7 @@ func (s *DockerSuite) TestPostContainersCreateShmSizeOmitted(c *testing.T) {
 	assert.NilError(c, err)
 	defer cli.Close()
 
-	container, err := cli.ContainerCreate(context.Background(), &config, &containertypes.HostConfig{}, &networktypes.NetworkingConfig{}, nil, "")
+	container, err := cli.ContainerCreate(context.Background(), &config, &container.HostConfig{}, &network.NetworkingConfig{}, nil, "")
 	assert.NilError(c, err)
 
 	containerJSON, err := cli.ContainerInspect(context.Background(), container.ID)
@@ -1498,12 +1502,12 @@ func (s *DockerSuite) TestPostContainersCreateShmSizeOmitted(c *testing.T) {
 func (s *DockerSuite) TestPostContainersCreateWithShmSize(c *testing.T) {
 	// ShmSize is not supported on Windows
 	testRequires(c, DaemonIsLinux)
-	config := containertypes.Config{
+	config := container.Config{
 		Image: "busybox",
 		Cmd:   []string{"mount"},
 	}
 
-	hostConfig := containertypes.HostConfig{
+	hostConfig := container.HostConfig{
 		ShmSize: 1073741824,
 	}
 
@@ -1511,7 +1515,7 @@ func (s *DockerSuite) TestPostContainersCreateWithShmSize(c *testing.T) {
 	assert.NilError(c, err)
 	defer cli.Close()
 
-	container, err := cli.ContainerCreate(context.Background(), &config, &hostConfig, &networktypes.NetworkingConfig{}, nil, "")
+	container, err := cli.ContainerCreate(context.Background(), &config, &hostConfig, &network.NetworkingConfig{}, nil, "")
 	assert.NilError(c, err)
 
 	containerJSON, err := cli.ContainerInspect(context.Background(), container.ID)
@@ -1529,7 +1533,7 @@ func (s *DockerSuite) TestPostContainersCreateWithShmSize(c *testing.T) {
 func (s *DockerSuite) TestPostContainersCreateMemorySwappinessHostConfigOmitted(c *testing.T) {
 	// Swappiness is not supported on Windows
 	testRequires(c, DaemonIsLinux)
-	config := containertypes.Config{
+	config := container.Config{
 		Image: "busybox",
 	}
 
@@ -1537,7 +1541,7 @@ func (s *DockerSuite) TestPostContainersCreateMemorySwappinessHostConfigOmitted(
 	assert.NilError(c, err)
 	defer cli.Close()
 
-	container, err := cli.ContainerCreate(context.Background(), &config, &containertypes.HostConfig{}, &networktypes.NetworkingConfig{}, nil, "")
+	container, err := cli.ContainerCreate(context.Background(), &config, &container.HostConfig{}, &network.NetworkingConfig{}, nil, "")
 	assert.NilError(c, err)
 
 	containerJSON, err := cli.ContainerInspect(context.Background(), container.ID)
@@ -1555,11 +1559,11 @@ func (s *DockerSuite) TestPostContainersCreateWithOomScoreAdjInvalidRange(c *tes
 	// OomScoreAdj is not supported on Windows
 	testRequires(c, DaemonIsLinux)
 
-	config := containertypes.Config{
+	config := container.Config{
 		Image: "busybox",
 	}
 
-	hostConfig := containertypes.HostConfig{
+	hostConfig := container.HostConfig{
 		OomScoreAdj: 1001,
 	}
 
@@ -1568,17 +1572,17 @@ func (s *DockerSuite) TestPostContainersCreateWithOomScoreAdjInvalidRange(c *tes
 	defer cli.Close()
 
 	name := "oomscoreadj-over"
-	_, err = cli.ContainerCreate(context.Background(), &config, &hostConfig, &networktypes.NetworkingConfig{}, nil, name)
+	_, err = cli.ContainerCreate(context.Background(), &config, &hostConfig, &network.NetworkingConfig{}, nil, name)
 
 	expected := "Invalid value 1001, range for oom score adj is [-1000, 1000]"
 	assert.ErrorContains(c, err, expected)
 
-	hostConfig = containertypes.HostConfig{
+	hostConfig = container.HostConfig{
 		OomScoreAdj: -1001,
 	}
 
 	name = "oomscoreadj-low"
-	_, err = cli.ContainerCreate(context.Background(), &config, &hostConfig, &networktypes.NetworkingConfig{}, nil, name)
+	_, err = cli.ContainerCreate(context.Background(), &config, &hostConfig, &network.NetworkingConfig{}, nil, name)
 
 	expected = "Invalid value -1001, range for oom score adj is [-1000, 1000]"
 	assert.ErrorContains(c, err, expected)
@@ -1600,7 +1604,7 @@ func (s *DockerSuite) TestContainerAPIStatsWithNetworkDisabled(c *testing.T) {
 
 	name := "testing-network-disabled"
 
-	config := containertypes.Config{
+	config := container.Config{
 		Image:           "busybox",
 		Cmd:             []string{"top"},
 		NetworkDisabled: true,
@@ -1610,7 +1614,7 @@ func (s *DockerSuite) TestContainerAPIStatsWithNetworkDisabled(c *testing.T) {
 	assert.NilError(c, err)
 	defer cli.Close()
 
-	_, err = cli.ContainerCreate(context.Background(), &config, &containertypes.HostConfig{}, &networktypes.NetworkingConfig{}, nil, name)
+	_, err = cli.ContainerCreate(context.Background(), &config, &container.HostConfig{}, &network.NetworkingConfig{}, nil, name)
 	assert.NilError(c, err)
 
 	err = cli.ContainerStart(context.Background(), name, types.ContainerStartOptions{})
@@ -1645,8 +1649,8 @@ func (s *DockerSuite) TestContainerAPIStatsWithNetworkDisabled(c *testing.T) {
 
 func (s *DockerSuite) TestContainersAPICreateMountsValidation(c *testing.T) {
 	type testCase struct {
-		config     containertypes.Config
-		hostConfig containertypes.HostConfig
+		config     container.Config
+		hostConfig container.HostConfig
 		msg        string
 	}
 
@@ -1656,11 +1660,11 @@ func (s *DockerSuite) TestContainersAPICreateMountsValidation(c *testing.T) {
 
 	cases := []testCase{
 		{
-			config: containertypes.Config{
+			config: container.Config{
 				Image: "busybox",
 			},
-			hostConfig: containertypes.HostConfig{
-				Mounts: []mounttypes.Mount{{
+			hostConfig: container.HostConfig{
+				Mounts: []mount.Mount{{
 					Type:   "notreal",
 					Target: destPath,
 				},
@@ -1670,30 +1674,30 @@ func (s *DockerSuite) TestContainersAPICreateMountsValidation(c *testing.T) {
 			msg: "mount type unknown",
 		},
 		{
-			config: containertypes.Config{
+			config: container.Config{
 				Image: "busybox",
 			},
-			hostConfig: containertypes.HostConfig{
-				Mounts: []mounttypes.Mount{{
+			hostConfig: container.HostConfig{
+				Mounts: []mount.Mount{{
 					Type: "bind"}}},
 			msg: "Target must not be empty",
 		},
 		{
-			config: containertypes.Config{
+			config: container.Config{
 				Image: "busybox",
 			},
-			hostConfig: containertypes.HostConfig{
-				Mounts: []mounttypes.Mount{{
+			hostConfig: container.HostConfig{
+				Mounts: []mount.Mount{{
 					Type:   "bind",
 					Target: destPath}}},
 			msg: "Source must not be empty",
 		},
 		{
-			config: containertypes.Config{
+			config: container.Config{
 				Image: "busybox",
 			},
-			hostConfig: containertypes.HostConfig{
-				Mounts: []mounttypes.Mount{{
+			hostConfig: container.HostConfig{
+				Mounts: []mount.Mount{{
 					Type:   "bind",
 					Source: notExistPath,
 					Target: destPath}}},
@@ -1702,36 +1706,36 @@ func (s *DockerSuite) TestContainersAPICreateMountsValidation(c *testing.T) {
 			// msg: "source path does not exist: " + notExistPath,
 		},
 		{
-			config: containertypes.Config{
+			config: container.Config{
 				Image: "busybox",
 			},
-			hostConfig: containertypes.HostConfig{
-				Mounts: []mounttypes.Mount{{
+			hostConfig: container.HostConfig{
+				Mounts: []mount.Mount{{
 					Type: "volume"}}},
 			msg: "Target must not be empty",
 		},
 		{
-			config: containertypes.Config{
+			config: container.Config{
 				Image: "busybox",
 			},
-			hostConfig: containertypes.HostConfig{
-				Mounts: []mounttypes.Mount{{
+			hostConfig: container.HostConfig{
+				Mounts: []mount.Mount{{
 					Type:   "volume",
 					Source: "hello",
 					Target: destPath}}},
 			msg: "",
 		},
 		{
-			config: containertypes.Config{
+			config: container.Config{
 				Image: "busybox",
 			},
-			hostConfig: containertypes.HostConfig{
-				Mounts: []mounttypes.Mount{{
+			hostConfig: container.HostConfig{
+				Mounts: []mount.Mount{{
 					Type:   "volume",
 					Source: "hello2",
 					Target: destPath,
-					VolumeOptions: &mounttypes.VolumeOptions{
-						DriverConfig: &mounttypes.Driver{
+					VolumeOptions: &mount.VolumeOptions{
+						DriverConfig: &mount.Driver{
 							Name: "local"}}}}},
 			msg: "",
 		},
@@ -1743,26 +1747,26 @@ func (s *DockerSuite) TestContainersAPICreateMountsValidation(c *testing.T) {
 		defer os.RemoveAll(tmpDir)
 		cases = append(cases, []testCase{
 			{
-				config: containertypes.Config{
+				config: container.Config{
 					Image: "busybox",
 				},
-				hostConfig: containertypes.HostConfig{
-					Mounts: []mounttypes.Mount{{
+				hostConfig: container.HostConfig{
+					Mounts: []mount.Mount{{
 						Type:   "bind",
 						Source: tmpDir,
 						Target: destPath}}},
 				msg: "",
 			},
 			{
-				config: containertypes.Config{
+				config: container.Config{
 					Image: "busybox",
 				},
-				hostConfig: containertypes.HostConfig{
-					Mounts: []mounttypes.Mount{{
+				hostConfig: container.HostConfig{
+					Mounts: []mount.Mount{{
 						Type:          "bind",
 						Source:        tmpDir,
 						Target:        destPath,
-						VolumeOptions: &mounttypes.VolumeOptions{}}}},
+						VolumeOptions: &mount.VolumeOptions{}}}},
 				msg: "VolumeOptions must not be specified",
 			},
 		}...)
@@ -1771,17 +1775,17 @@ func (s *DockerSuite) TestContainersAPICreateMountsValidation(c *testing.T) {
 	if DaemonIsWindows() {
 		cases = append(cases, []testCase{
 			{
-				config: containertypes.Config{
+				config: container.Config{
 					Image: "busybox",
 				},
-				hostConfig: containertypes.HostConfig{
-					Mounts: []mounttypes.Mount{
+				hostConfig: container.HostConfig{
+					Mounts: []mount.Mount{
 						{
 							Type:   "volume",
 							Source: "not-supported-on-windows",
 							Target: destPath,
-							VolumeOptions: &mounttypes.VolumeOptions{
-								DriverConfig: &mounttypes.Driver{
+							VolumeOptions: &mount.VolumeOptions{
+								DriverConfig: &mount.Driver{
 									Name:    "local",
 									Options: map[string]string{"type": "tmpfs"},
 								},
@@ -1797,17 +1801,17 @@ func (s *DockerSuite) TestContainersAPICreateMountsValidation(c *testing.T) {
 	if DaemonIsLinux() {
 		cases = append(cases, []testCase{
 			{
-				config: containertypes.Config{
+				config: container.Config{
 					Image: "busybox",
 				},
-				hostConfig: containertypes.HostConfig{
-					Mounts: []mounttypes.Mount{
+				hostConfig: container.HostConfig{
+					Mounts: []mount.Mount{
 						{
 							Type:   "volume",
 							Source: "missing-device-opt",
 							Target: destPath,
-							VolumeOptions: &mounttypes.VolumeOptions{
-								DriverConfig: &mounttypes.Driver{
+							VolumeOptions: &mount.VolumeOptions{
+								DriverConfig: &mount.Driver{
 									Name:    "local",
 									Options: map[string]string{"foobar": "foobaz"},
 								},
@@ -1818,17 +1822,17 @@ func (s *DockerSuite) TestContainersAPICreateMountsValidation(c *testing.T) {
 				msg: `invalid option: "foobar"`,
 			},
 			{
-				config: containertypes.Config{
+				config: container.Config{
 					Image: "busybox",
 				},
-				hostConfig: containertypes.HostConfig{
-					Mounts: []mounttypes.Mount{
+				hostConfig: container.HostConfig{
+					Mounts: []mount.Mount{
 						{
 							Type:   "volume",
 							Source: "missing-device-opt",
 							Target: destPath,
-							VolumeOptions: &mounttypes.VolumeOptions{
-								DriverConfig: &mounttypes.Driver{
+							VolumeOptions: &mount.VolumeOptions{
+								DriverConfig: &mount.Driver{
 									Name:    "local",
 									Options: map[string]string{"type": "tmpfs"},
 								},
@@ -1839,17 +1843,17 @@ func (s *DockerSuite) TestContainersAPICreateMountsValidation(c *testing.T) {
 				msg: `missing required option: "device"`,
 			},
 			{
-				config: containertypes.Config{
+				config: container.Config{
 					Image: "busybox",
 				},
-				hostConfig: containertypes.HostConfig{
-					Mounts: []mounttypes.Mount{
+				hostConfig: container.HostConfig{
+					Mounts: []mount.Mount{
 						{
 							Type:   "volume",
 							Source: "missing-type-opt",
 							Target: destPath,
-							VolumeOptions: &mounttypes.VolumeOptions{
-								DriverConfig: &mounttypes.Driver{
+							VolumeOptions: &mount.VolumeOptions{
+								DriverConfig: &mount.Driver{
 									Name:    "local",
 									Options: map[string]string{"device": "tmpfs"},
 								},
@@ -1860,17 +1864,17 @@ func (s *DockerSuite) TestContainersAPICreateMountsValidation(c *testing.T) {
 				msg: `missing required option: "type"`,
 			},
 			{
-				config: containertypes.Config{
+				config: container.Config{
 					Image: "busybox",
 				},
-				hostConfig: containertypes.HostConfig{
-					Mounts: []mounttypes.Mount{
+				hostConfig: container.HostConfig{
+					Mounts: []mount.Mount{
 						{
 							Type:   "volume",
 							Source: "hello4",
 							Target: destPath,
-							VolumeOptions: &mounttypes.VolumeOptions{
-								DriverConfig: &mounttypes.Driver{
+							VolumeOptions: &mount.VolumeOptions{
+								DriverConfig: &mount.Driver{
 									Name:    "local",
 									Options: map[string]string{"o": "size=1", "type": "tmpfs", "device": "tmpfs"},
 								},
@@ -1881,35 +1885,35 @@ func (s *DockerSuite) TestContainersAPICreateMountsValidation(c *testing.T) {
 				msg: "",
 			},
 			{
-				config: containertypes.Config{
+				config: container.Config{
 					Image: "busybox",
 				},
-				hostConfig: containertypes.HostConfig{
-					Mounts: []mounttypes.Mount{{
+				hostConfig: container.HostConfig{
+					Mounts: []mount.Mount{{
 						Type:   "tmpfs",
 						Target: destPath}}},
 				msg: "",
 			},
 			{
-				config: containertypes.Config{
+				config: container.Config{
 					Image: "busybox",
 				},
-				hostConfig: containertypes.HostConfig{
-					Mounts: []mounttypes.Mount{{
+				hostConfig: container.HostConfig{
+					Mounts: []mount.Mount{{
 						Type:   "tmpfs",
 						Target: destPath,
-						TmpfsOptions: &mounttypes.TmpfsOptions{
+						TmpfsOptions: &mount.TmpfsOptions{
 							SizeBytes: 4096 * 1024,
 							Mode:      0700,
 						}}}},
 				msg: "",
 			},
 			{
-				config: containertypes.Config{
+				config: container.Config{
 					Image: "busybox",
 				},
-				hostConfig: containertypes.HostConfig{
-					Mounts: []mounttypes.Mount{{
+				hostConfig: container.HostConfig{
+					Mounts: []mount.Mount{{
 						Type:   "tmpfs",
 						Source: "/shouldnotbespecified",
 						Target: destPath}}},
@@ -1926,7 +1930,7 @@ func (s *DockerSuite) TestContainersAPICreateMountsValidation(c *testing.T) {
 	for i, x := range cases {
 		x := x
 		c.Run(fmt.Sprintf("case %d", i), func(c *testing.T) {
-			_, err = apiClient.ContainerCreate(context.Background(), &x.config, &x.hostConfig, &networktypes.NetworkingConfig{}, nil, "")
+			_, err = apiClient.ContainerCreate(context.Background(), &x.config, &x.hostConfig, &network.NetworkingConfig{}, nil, "")
 			if len(x.msg) > 0 {
 				assert.ErrorContains(c, err, x.msg, "%v", cases[i].config)
 			} else {
@@ -1946,12 +1950,12 @@ func (s *DockerSuite) TestContainerAPICreateMountsBindRead(c *testing.T) {
 	defer os.RemoveAll(tmpDir)
 	err = os.WriteFile(filepath.Join(tmpDir, "bar"), []byte("hello"), 0666)
 	assert.NilError(c, err)
-	config := containertypes.Config{
+	config := container.Config{
 		Image: "busybox",
 		Cmd:   []string{"/bin/sh", "-c", "cat /foo/bar"},
 	}
-	hostConfig := containertypes.HostConfig{
-		Mounts: []mounttypes.Mount{
+	hostConfig := container.HostConfig{
+		Mounts: []mount.Mount{
 			{Type: "bind", Source: tmpDir, Target: destPath},
 		},
 	}
@@ -1959,7 +1963,7 @@ func (s *DockerSuite) TestContainerAPICreateMountsBindRead(c *testing.T) {
 	assert.NilError(c, err)
 	defer cli.Close()
 
-	_, err = cli.ContainerCreate(context.Background(), &config, &hostConfig, &networktypes.NetworkingConfig{}, nil, "test")
+	_, err = cli.ContainerCreate(context.Background(), &config, &hostConfig, &network.NetworkingConfig{}, nil, "test")
 	assert.NilError(c, err)
 
 	out, _ := dockerCmd(c, "start", "-a", "test")
@@ -1986,7 +1990,7 @@ func (s *DockerSuite) TestContainersAPICreateMountsCreate(c *testing.T) {
 	}
 
 	type testCase struct {
-		spec     mounttypes.Mount
+		spec     mount.Mount
 		expected types.MountPoint
 	}
 
@@ -2004,23 +2008,23 @@ func (s *DockerSuite) TestContainersAPICreateMountsCreate(c *testing.T) {
 		// use literal strings here for `Type` instead of the defined constants in the volume package to keep this honest
 		// Validation of the actual `Mount` struct is done in another test is not needed here
 		{
-			spec:     mounttypes.Mount{Type: "volume", Target: destPath},
+			spec:     mount.Mount{Type: "volume", Target: destPath},
 			expected: types.MountPoint{Driver: volume.DefaultDriverName, Type: "volume", RW: true, Destination: destPath, Mode: selinuxSharedLabel},
 		},
 		{
-			spec:     mounttypes.Mount{Type: "volume", Target: destPath + slash},
+			spec:     mount.Mount{Type: "volume", Target: destPath + slash},
 			expected: types.MountPoint{Driver: volume.DefaultDriverName, Type: "volume", RW: true, Destination: destPath, Mode: selinuxSharedLabel},
 		},
 		{
-			spec:     mounttypes.Mount{Type: "volume", Target: destPath, Source: "test1"},
+			spec:     mount.Mount{Type: "volume", Target: destPath, Source: "test1"},
 			expected: types.MountPoint{Type: "volume", Name: "test1", RW: true, Destination: destPath, Mode: selinuxSharedLabel},
 		},
 		{
-			spec:     mounttypes.Mount{Type: "volume", Target: destPath, ReadOnly: true, Source: "test2"},
+			spec:     mount.Mount{Type: "volume", Target: destPath, ReadOnly: true, Source: "test2"},
 			expected: types.MountPoint{Type: "volume", Name: "test2", RW: false, Destination: destPath, Mode: selinuxSharedLabel},
 		},
 		{
-			spec:     mounttypes.Mount{Type: "volume", Target: destPath, Source: "test3", VolumeOptions: &mounttypes.VolumeOptions{DriverConfig: &mounttypes.Driver{Name: volume.DefaultDriverName}}},
+			spec:     mount.Mount{Type: "volume", Target: destPath, Source: "test3", VolumeOptions: &mount.VolumeOptions{DriverConfig: &mount.Driver{Name: volume.DefaultDriverName}}},
 			expected: types.MountPoint{Driver: volume.DefaultDriverName, Type: "volume", Name: "test3", RW: true, Destination: destPath, Mode: selinuxSharedLabel},
 		},
 	}
@@ -2032,7 +2036,7 @@ func (s *DockerSuite) TestContainersAPICreateMountsCreate(c *testing.T) {
 		defer os.RemoveAll(tmpDir1)
 		cases = append(cases, []testCase{
 			{
-				spec: mounttypes.Mount{
+				spec: mount.Mount{
 					Type:   "bind",
 					Source: tmpDir1,
 					Target: destPath,
@@ -2045,7 +2049,7 @@ func (s *DockerSuite) TestContainersAPICreateMountsCreate(c *testing.T) {
 				},
 			},
 			{
-				spec:     mounttypes.Mount{Type: "bind", Source: tmpDir1, Target: destPath, ReadOnly: true},
+				spec:     mount.Mount{Type: "bind", Source: tmpDir1, Target: destPath, ReadOnly: true},
 				expected: types.MountPoint{Type: "bind", RW: false, Destination: destPath, Source: tmpDir1},
 			},
 		}...)
@@ -2060,15 +2064,15 @@ func (s *DockerSuite) TestContainersAPICreateMountsCreate(c *testing.T) {
 
 			cases = append(cases, []testCase{
 				{
-					spec:     mounttypes.Mount{Type: "bind", Source: tmpDir3, Target: destPath},
+					spec:     mount.Mount{Type: "bind", Source: tmpDir3, Target: destPath},
 					expected: types.MountPoint{Type: "bind", RW: true, Destination: destPath, Source: tmpDir3},
 				},
 				{
-					spec:     mounttypes.Mount{Type: "bind", Source: tmpDir3, Target: destPath, ReadOnly: true},
+					spec:     mount.Mount{Type: "bind", Source: tmpDir3, Target: destPath, ReadOnly: true},
 					expected: types.MountPoint{Type: "bind", RW: false, Destination: destPath, Source: tmpDir3},
 				},
 				{
-					spec:     mounttypes.Mount{Type: "bind", Source: tmpDir3, Target: destPath, ReadOnly: true, BindOptions: &mounttypes.BindOptions{Propagation: "shared"}},
+					spec:     mount.Mount{Type: "bind", Source: tmpDir3, Target: destPath, ReadOnly: true, BindOptions: &mount.BindOptions{Propagation: "shared"}},
 					expected: types.MountPoint{Type: "bind", RW: false, Destination: destPath, Source: tmpDir3, Propagation: "shared"},
 				},
 			}...)
@@ -2078,19 +2082,19 @@ func (s *DockerSuite) TestContainersAPICreateMountsCreate(c *testing.T) {
 	if testEnv.OSType != "windows" { // Windows does not support volume populate
 		cases = append(cases, []testCase{
 			{
-				spec:     mounttypes.Mount{Type: "volume", Target: destPath, VolumeOptions: &mounttypes.VolumeOptions{NoCopy: true}},
+				spec:     mount.Mount{Type: "volume", Target: destPath, VolumeOptions: &mount.VolumeOptions{NoCopy: true}},
 				expected: types.MountPoint{Driver: volume.DefaultDriverName, Type: "volume", RW: true, Destination: destPath, Mode: selinuxSharedLabel},
 			},
 			{
-				spec:     mounttypes.Mount{Type: "volume", Target: destPath + slash, VolumeOptions: &mounttypes.VolumeOptions{NoCopy: true}},
+				spec:     mount.Mount{Type: "volume", Target: destPath + slash, VolumeOptions: &mount.VolumeOptions{NoCopy: true}},
 				expected: types.MountPoint{Driver: volume.DefaultDriverName, Type: "volume", RW: true, Destination: destPath, Mode: selinuxSharedLabel},
 			},
 			{
-				spec:     mounttypes.Mount{Type: "volume", Target: destPath, Source: "test4", VolumeOptions: &mounttypes.VolumeOptions{NoCopy: true}},
+				spec:     mount.Mount{Type: "volume", Target: destPath, Source: "test4", VolumeOptions: &mount.VolumeOptions{NoCopy: true}},
 				expected: types.MountPoint{Type: "volume", Name: "test4", RW: true, Destination: destPath, Mode: selinuxSharedLabel},
 			},
 			{
-				spec:     mounttypes.Mount{Type: "volume", Target: destPath, Source: "test5", ReadOnly: true, VolumeOptions: &mounttypes.VolumeOptions{NoCopy: true}},
+				spec:     mount.Mount{Type: "volume", Target: destPath, Source: "test5", ReadOnly: true, VolumeOptions: &mount.VolumeOptions{NoCopy: true}},
 				expected: types.MountPoint{Type: "volume", Name: "test5", RW: false, Destination: destPath, Mode: selinuxSharedLabel},
 			},
 		}...)
@@ -2103,9 +2107,9 @@ func (s *DockerSuite) TestContainersAPICreateMountsCreate(c *testing.T) {
 		c.Run(fmt.Sprintf("%d config: %v", i, x.spec), func(c *testing.T) {
 			container, err := apiclient.ContainerCreate(
 				ctx,
-				&containertypes.Config{Image: testImg},
-				&containertypes.HostConfig{Mounts: []mounttypes.Mount{x.spec}},
-				&networktypes.NetworkingConfig{},
+				&container.Config{Image: testImg},
+				&container.HostConfig{Mounts: []mount.Mount{x.spec}},
+				&network.NetworkingConfig{},
 				nil,
 				"")
 			assert.NilError(c, err)
@@ -2179,22 +2183,22 @@ func containerExit(apiclient client.APIClient, name string) func(poll.LogT) poll
 func (s *DockerSuite) TestContainersAPICreateMountsTmpfs(c *testing.T) {
 	testRequires(c, DaemonIsLinux)
 	type testCase struct {
-		cfg             mounttypes.Mount
+		cfg             mount.Mount
 		expectedOptions []string
 	}
 	target := "/foo"
 	cases := []testCase{
 		{
-			cfg: mounttypes.Mount{
+			cfg: mount.Mount{
 				Type:   "tmpfs",
 				Target: target},
 			expectedOptions: []string{"rw", "nosuid", "nodev", "noexec", "relatime"},
 		},
 		{
-			cfg: mounttypes.Mount{
+			cfg: mount.Mount{
 				Type:   "tmpfs",
 				Target: target,
-				TmpfsOptions: &mounttypes.TmpfsOptions{
+				TmpfsOptions: &mount.TmpfsOptions{
 					SizeBytes: 4096 * 1024, Mode: 0700}},
 			expectedOptions: []string{"rw", "nosuid", "nodev", "noexec", "relatime", "size=4096k", "mode=700"},
 		},
@@ -2204,17 +2208,17 @@ func (s *DockerSuite) TestContainersAPICreateMountsTmpfs(c *testing.T) {
 	assert.NilError(c, err)
 	defer cli.Close()
 
-	config := containertypes.Config{
+	config := container.Config{
 		Image: "busybox",
 		Cmd:   []string{"/bin/sh", "-c", fmt.Sprintf("mount | grep 'tmpfs on %s'", target)},
 	}
 	for i, x := range cases {
 		cName := fmt.Sprintf("test-tmpfs-%d", i)
-		hostConfig := containertypes.HostConfig{
-			Mounts: []mounttypes.Mount{x.cfg},
+		hostConfig := container.HostConfig{
+			Mounts: []mount.Mount{x.cfg},
 		}
 
-		_, err = cli.ContainerCreate(context.Background(), &config, &hostConfig, &networktypes.NetworkingConfig{}, nil, cName)
+		_, err = cli.ContainerCreate(context.Background(), &config, &hostConfig, &network.NetworkingConfig{}, nil, cName)
 		assert.NilError(c, err)
 		out, _ := dockerCmd(c, "start", "-a", cName)
 		for _, option := range x.expectedOptions {

+ 2 - 1
integration/container/pause_test.go

@@ -8,6 +8,7 @@ import (
 
 	containerderrdefs "github.com/containerd/containerd/errdefs"
 	"github.com/docker/docker/api/types"
+	containertypes "github.com/docker/docker/api/types/container"
 	"github.com/docker/docker/api/types/events"
 	"github.com/docker/docker/api/types/filters"
 	"github.com/docker/docker/api/types/versions"
@@ -80,7 +81,7 @@ func TestPauseStopPausedContainer(t *testing.T) {
 	err := client.ContainerPause(ctx, cID)
 	assert.NilError(t, err)
 
-	err = client.ContainerStop(ctx, cID, nil)
+	err = client.ContainerStop(ctx, cID, containertypes.StopOptions{})
 	assert.NilError(t, err)
 
 	poll.WaitOn(t, container.IsStopped(ctx, client, cID), poll.WithDelay(100*time.Millisecond))

+ 1 - 1
integration/container/rename_test.go

@@ -143,7 +143,7 @@ func TestRenameAnonymousContainer(t *testing.T) {
 	assert.NilError(t, err)
 	// Stop/Start the container to get registered
 	// FIXME(vdemeester) this is a really weird behavior as it fails otherwise
-	err = client.ContainerStop(ctx, container1Name, nil)
+	err = client.ContainerStop(ctx, container1Name, containertypes.StopOptions{})
 	assert.NilError(t, err)
 	err = client.ContainerStart(ctx, container1Name, types.ContainerStartOptions{})
 	assert.NilError(t, err)

+ 2 - 2
integration/container/stop_linux_test.go

@@ -9,6 +9,7 @@ import (
 	"time"
 
 	"github.com/docker/docker/api/types"
+	containertypes "github.com/docker/docker/api/types/container"
 	"github.com/docker/docker/integration/internal/container"
 	"gotest.tools/v3/assert"
 	"gotest.tools/v3/icmd"
@@ -56,8 +57,7 @@ func TestStopContainerWithTimeout(t *testing.T) {
 			t.Parallel()
 			id := container.Run(ctx, t, client, testCmd)
 
-			timeout := time.Duration(d.timeout) * time.Second
-			err := client.ContainerStop(ctx, id, &timeout)
+			err := client.ContainerStop(ctx, id, containertypes.StopOptions{Timeout: &d.timeout})
 			assert.NilError(t, err)
 
 			poll.WaitOn(t, container.IsStopped(ctx, client, id),

+ 2 - 1
integration/container/stop_test.go

@@ -5,6 +5,7 @@ import (
 	"testing"
 	"time"
 
+	containertypes "github.com/docker/docker/api/types/container"
 	"github.com/docker/docker/integration/internal/container"
 	"gotest.tools/v3/assert"
 	"gotest.tools/v3/poll"
@@ -29,7 +30,7 @@ func TestStopContainerWithRestartPolicyAlways(t *testing.T) {
 	}
 
 	for _, name := range names {
-		err := client.ContainerStop(ctx, name, nil)
+		err := client.ContainerStop(ctx, name, containertypes.StopOptions{})
 		assert.NilError(t, err)
 	}
 

+ 2 - 2
integration/container/stop_windows_test.go

@@ -6,6 +6,7 @@ import (
 	"testing"
 	"time"
 
+	containertypes "github.com/docker/docker/api/types/container"
 	"github.com/docker/docker/integration/internal/container"
 	"gotest.tools/v3/assert"
 	"gotest.tools/v3/poll"
@@ -53,8 +54,7 @@ func TestStopContainerWithTimeout(t *testing.T) {
 			t.Parallel()
 			id := container.Run(ctx, t, client, testCmd)
 
-			timeout := time.Duration(d.timeout) * time.Second
-			err := client.ContainerStop(ctx, id, &timeout)
+			err := client.ContainerStop(ctx, id, containertypes.StopOptions{Timeout: &d.timeout})
 			assert.NilError(t, err)
 
 			poll.WaitOn(t, container.IsStopped(ctx, client, id),

+ 2 - 1
integration/container/wait_test.go

@@ -5,6 +5,7 @@ import (
 	"testing"
 	"time"
 
+	containertypes "github.com/docker/docker/api/types/container"
 	"github.com/docker/docker/integration/internal/container"
 	"github.com/docker/docker/testutil/request"
 	"gotest.tools/v3/assert"
@@ -86,7 +87,7 @@ func TestWaitBlocked(t *testing.T) {
 
 			waitResC, errC := cli.ContainerWait(ctx, containerID, "")
 
-			err := cli.ContainerStop(ctx, containerID, nil)
+			err := cli.ContainerStop(ctx, containerID, containertypes.StopOptions{})
 			assert.NilError(t, err)
 
 			select {