瀏覽代碼

client: ContainerStop(), ContainerRestart(): support stop-signal

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
Sebastiaan van Stijn 3 年之前
父節點
當前提交
e8fa708ae5

+ 9 - 5
client/container_restart.go

@@ -3,18 +3,22 @@ package client // import "github.com/docker/docker/client"
 import (
 import (
 	"context"
 	"context"
 	"net/url"
 	"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.
 // ContainerRestart stops and starts a container again.
 // It makes the daemon wait for the container to be up again for
 // It makes the daemon wait for the container to be up again for
 // a specific amount of time, given the timeout.
 // 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{}
 	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)
 	resp, err := cli.post(ctx, "/containers/"+containerID+"/restart", query, nil, nil)
 	ensureReaderClosed(resp)
 	ensureReaderClosed(resp)

+ 13 - 6
client/container_restart_test.go

@@ -8,8 +8,8 @@ import (
 	"net/http"
 	"net/http"
 	"strings"
 	"strings"
 	"testing"
 	"testing"
-	"time"
 
 
+	"github.com/docker/docker/api/types/container"
 	"github.com/docker/docker/errdefs"
 	"github.com/docker/docker/errdefs"
 )
 )
 
 
@@ -17,20 +17,23 @@ func TestContainerRestartError(t *testing.T) {
 	client := &Client{
 	client := &Client{
 		client: newMockClient(errorMock(http.StatusInternalServerError, "Server error")),
 		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) {
 	if !errdefs.IsSystem(err) {
 		t.Fatalf("expected a Server Error, got %[1]T: %[1]v", err)
 		t.Fatalf("expected a Server Error, got %[1]T: %[1]v", err)
 	}
 	}
 }
 }
 
 
 func TestContainerRestart(t *testing.T) {
 func TestContainerRestart(t *testing.T) {
-	expectedURL := "/containers/container_id/restart"
+	const expectedURL = "/v1.42/containers/container_id/restart"
 	client := &Client{
 	client := &Client{
 		client: newMockClient(func(req *http.Request) (*http.Response, error) {
 		client: newMockClient(func(req *http.Request) (*http.Response, error) {
 			if !strings.HasPrefix(req.URL.Path, expectedURL) {
 			if !strings.HasPrefix(req.URL.Path, expectedURL) {
 				return nil, fmt.Errorf("Expected URL '%s', got '%s'", expectedURL, req.URL)
 				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")
 			t := req.URL.Query().Get("t")
 			if t != "100" {
 			if t != "100" {
 				return nil, fmt.Errorf("t (timeout) not set in URL query properly. Expected '100', got %s", t)
 				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(""))),
 				Body:       io.NopCloser(bytes.NewReader([]byte(""))),
 			}, nil
 			}, 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 {
 	if err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}

+ 9 - 5
client/container_stop.go

@@ -3,9 +3,10 @@ package client // import "github.com/docker/docker/client"
 import (
 import (
 	"context"
 	"context"
 	"net/url"
 	"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
 // 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,
 // 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 (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{}
 	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)
 	resp, err := cli.post(ctx, "/containers/"+containerID+"/stop", query, nil, nil)
 	ensureReaderClosed(resp)
 	ensureReaderClosed(resp)

+ 13 - 6
client/container_stop_test.go

@@ -8,8 +8,8 @@ import (
 	"net/http"
 	"net/http"
 	"strings"
 	"strings"
 	"testing"
 	"testing"
-	"time"
 
 
+	"github.com/docker/docker/api/types/container"
 	"github.com/docker/docker/errdefs"
 	"github.com/docker/docker/errdefs"
 )
 )
 
 
@@ -17,20 +17,23 @@ func TestContainerStopError(t *testing.T) {
 	client := &Client{
 	client := &Client{
 		client: newMockClient(errorMock(http.StatusInternalServerError, "Server error")),
 		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) {
 	if !errdefs.IsSystem(err) {
 		t.Fatalf("expected a Server Error, got %[1]T: %[1]v", err)
 		t.Fatalf("expected a Server Error, got %[1]T: %[1]v", err)
 	}
 	}
 }
 }
 
 
 func TestContainerStop(t *testing.T) {
 func TestContainerStop(t *testing.T) {
-	expectedURL := "/containers/container_id/stop"
+	const expectedURL = "/v1.42/containers/container_id/stop"
 	client := &Client{
 	client := &Client{
 		client: newMockClient(func(req *http.Request) (*http.Response, error) {
 		client: newMockClient(func(req *http.Request) (*http.Response, error) {
 			if !strings.HasPrefix(req.URL.Path, expectedURL) {
 			if !strings.HasPrefix(req.URL.Path, expectedURL) {
 				return nil, fmt.Errorf("Expected URL '%s', got '%s'", expectedURL, req.URL)
 				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")
 			t := req.URL.Query().Get("t")
 			if t != "100" {
 			if t != "100" {
 				return nil, fmt.Errorf("t (timeout) not set in URL query properly. Expected '100', got %s", t)
 				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(""))),
 				Body:       io.NopCloser(bytes.NewReader([]byte(""))),
 			}, nil
 			}, 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 {
 	if err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}

+ 2 - 3
client/interface.go

@@ -5,7 +5,6 @@ import (
 	"io"
 	"io"
 	"net"
 	"net"
 	"net/http"
 	"net/http"
-	"time"
 
 
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types/container"
 	"github.com/docker/docker/api/types/container"
@@ -65,12 +64,12 @@ type ContainerAPIClient interface {
 	ContainerRemove(ctx context.Context, container string, options types.ContainerRemoveOptions) error
 	ContainerRemove(ctx context.Context, container string, options types.ContainerRemoveOptions) error
 	ContainerRename(ctx context.Context, container, newContainerName string) error
 	ContainerRename(ctx context.Context, container, newContainerName string) error
 	ContainerResize(ctx context.Context, container string, options types.ResizeOptions) 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)
 	ContainerStatPath(ctx context.Context, container, path string) (types.ContainerPathStat, error)
 	ContainerStats(ctx context.Context, container string, stream bool) (types.ContainerStats, error)
 	ContainerStats(ctx context.Context, container string, stream bool) (types.ContainerStats, error)
 	ContainerStatsOneShot(ctx context.Context, container string) (types.ContainerStats, error)
 	ContainerStatsOneShot(ctx context.Context, container string) (types.ContainerStats, error)
 	ContainerStart(ctx context.Context, container string, options types.ContainerStartOptions) error
 	ContainerStart(ctx context.Context, container string, options types.ContainerStartOptions) error
-	ContainerStop(ctx context.Context, container string, timeout *time.Duration) error
+	ContainerStop(ctx context.Context, container string, options container.StopOptions) error
 	ContainerTop(ctx context.Context, container string, arguments []string) (container.ContainerTopOKBody, error)
 	ContainerTop(ctx context.Context, container string, arguments []string) (container.ContainerTopOKBody, error)
 	ContainerUnpause(ctx context.Context, container string) error
 	ContainerUnpause(ctx context.Context, container string) error
 	ContainerUpdate(ctx context.Context, container string, updateConfig container.UpdateConfig) (container.ContainerUpdateOKBody, error)
 	ContainerUpdate(ctx context.Context, container string, updateConfig container.UpdateConfig) (container.ContainerUpdateOKBody, error)

+ 11 - 7
integration-cli/docker_api_containers_test.go

@@ -913,8 +913,8 @@ func (s *DockerSuite) TestContainerAPIRestart(c *testing.T) {
 	assert.NilError(c, err)
 	assert.NilError(c, err)
 	defer cli.Close()
 	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.NilError(c, err)
 
 
 	assert.Assert(c, waitInspect(name, "{{ .State.Restarting  }} {{ .State.Running  }}", "false true", 15*time.Second) == nil)
 	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)
 	assert.NilError(c, err)
 	defer cli.Close()
 	defer cli.Close()
 
 
-	err = cli.ContainerRestart(context.Background(), name, nil)
+	err = cli.ContainerRestart(context.Background(), name, container.StopOptions{})
 	assert.NilError(c, err)
 	assert.NilError(c, err)
 
 
 	assert.Assert(c, waitInspect(name, "{{ .State.Restarting  }} {{ .State.Running  }}", "false true", 15*time.Second) == nil)
 	assert.Assert(c, waitInspect(name, "{{ .State.Restarting  }} {{ .State.Running  }}", "false true", 15*time.Second) == nil)
@@ -965,19 +965,23 @@ func (s *DockerSuite) TestContainerAPIStart(c *testing.T) {
 func (s *DockerSuite) TestContainerAPIStop(c *testing.T) {
 func (s *DockerSuite) TestContainerAPIStop(c *testing.T) {
 	name := "test-api-stop"
 	name := "test-api-stop"
 	runSleepingContainer(c, "-i", "--name", name)
 	runSleepingContainer(c, "-i", "--name", name)
-	timeout := 30 * time.Second
+	timeout := 30
 
 
 	cli, err := client.NewClientWithOpts(client.FromEnv)
 	cli, err := client.NewClientWithOpts(client.FromEnv)
 	assert.NilError(c, err)
 	assert.NilError(c, err)
 	defer cli.Close()
 	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.NilError(c, err)
 	assert.Assert(c, waitInspect(name, "{{ .State.Running  }}", "false", 60*time.Second) == nil)
 	assert.Assert(c, waitInspect(name, "{{ .State.Running  }}", "false", 60*time.Second) == nil)
 
 
 	// second call to start should give 304
 	// second call to start should give 304
 	// maybe add ContainerStartWithRaw to test it
 	// 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)
 	assert.NilError(c, err)
 }
 }
 
 
@@ -1255,7 +1259,7 @@ func (s *DockerSuite) TestContainerAPIPostContainerStop(c *testing.T) {
 	assert.NilError(c, err)
 	assert.NilError(c, err)
 	defer cli.Close()
 	defer cli.Close()
 
 
-	err = cli.ContainerStop(context.Background(), containerID, nil)
+	err = cli.ContainerStop(context.Background(), containerID, container.StopOptions{})
 	assert.NilError(c, err)
 	assert.NilError(c, err)
 	assert.Assert(c, waitInspect(containerID, "{{ .State.Running  }}", "false", 60*time.Second) == nil)
 	assert.Assert(c, waitInspect(containerID, "{{ .State.Running  }}", "false", 60*time.Second) == nil)
 }
 }

+ 2 - 1
integration/container/pause_test.go

@@ -8,6 +8,7 @@ import (
 
 
 	containerderrdefs "github.com/containerd/containerd/errdefs"
 	containerderrdefs "github.com/containerd/containerd/errdefs"
 	"github.com/docker/docker/api/types"
 	"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/events"
 	"github.com/docker/docker/api/types/filters"
 	"github.com/docker/docker/api/types/filters"
 	"github.com/docker/docker/api/types/versions"
 	"github.com/docker/docker/api/types/versions"
@@ -80,7 +81,7 @@ func TestPauseStopPausedContainer(t *testing.T) {
 	err := client.ContainerPause(ctx, cID)
 	err := client.ContainerPause(ctx, cID)
 	assert.NilError(t, err)
 	assert.NilError(t, err)
 
 
-	err = client.ContainerStop(ctx, cID, nil)
+	err = client.ContainerStop(ctx, cID, containertypes.StopOptions{})
 	assert.NilError(t, err)
 	assert.NilError(t, err)
 
 
 	poll.WaitOn(t, container.IsStopped(ctx, client, cID), poll.WithDelay(100*time.Millisecond))
 	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)
 	assert.NilError(t, err)
 	// Stop/Start the container to get registered
 	// Stop/Start the container to get registered
 	// FIXME(vdemeester) this is a really weird behavior as it fails otherwise
 	// 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)
 	assert.NilError(t, err)
 	err = client.ContainerStart(ctx, container1Name, types.ContainerStartOptions{})
 	err = client.ContainerStart(ctx, container1Name, types.ContainerStartOptions{})
 	assert.NilError(t, err)
 	assert.NilError(t, err)

+ 2 - 2
integration/container/stop_linux_test.go

@@ -9,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/integration/internal/container"
 	"github.com/docker/docker/integration/internal/container"
 	"gotest.tools/v3/assert"
 	"gotest.tools/v3/assert"
 	"gotest.tools/v3/icmd"
 	"gotest.tools/v3/icmd"
@@ -56,8 +57,7 @@ func TestStopContainerWithTimeout(t *testing.T) {
 			t.Parallel()
 			t.Parallel()
 			id := container.Run(ctx, t, client, testCmd)
 			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)
 			assert.NilError(t, err)
 
 
 			poll.WaitOn(t, container.IsStopped(ctx, client, id),
 			poll.WaitOn(t, container.IsStopped(ctx, client, id),

+ 2 - 1
integration/container/stop_test.go

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

+ 2 - 2
integration/container/stop_windows_test.go

@@ -6,6 +6,7 @@ import (
 	"testing"
 	"testing"
 	"time"
 	"time"
 
 
+	containertypes "github.com/docker/docker/api/types/container"
 	"github.com/docker/docker/integration/internal/container"
 	"github.com/docker/docker/integration/internal/container"
 	"gotest.tools/v3/assert"
 	"gotest.tools/v3/assert"
 	"gotest.tools/v3/poll"
 	"gotest.tools/v3/poll"
@@ -53,8 +54,7 @@ func TestStopContainerWithTimeout(t *testing.T) {
 			t.Parallel()
 			t.Parallel()
 			id := container.Run(ctx, t, client, testCmd)
 			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)
 			assert.NilError(t, err)
 
 
 			poll.WaitOn(t, container.IsStopped(ctx, client, id),
 			poll.WaitOn(t, container.IsStopped(ctx, client, id),

+ 2 - 1
integration/container/wait_test.go

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