Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
| ... | ... |
@@ -3,18 +3,22 @@ package client // import "github.com/docker/docker/client" |
| 3 | 3 |
import ( |
| 4 | 4 |
"context" |
| 5 | 5 |
"net/url" |
| 6 |
- "time" |
|
| 6 |
+ "strconv" |
|
| 7 | 7 |
|
| 8 |
- timetypes "github.com/docker/docker/api/types/time" |
|
| 8 |
+ "github.com/docker/docker/api/types/container" |
|
| 9 |
+ "github.com/docker/docker/api/types/versions" |
|
| 9 | 10 |
) |
| 10 | 11 |
|
| 11 | 12 |
// ContainerRestart stops and starts a container again. |
| 12 | 13 |
// It makes the daemon wait for the container to be up again for |
| 13 | 14 |
// a specific amount of time, given the timeout. |
| 14 |
-func (cli *Client) ContainerRestart(ctx context.Context, containerID string, timeout *time.Duration) error {
|
|
| 15 |
+func (cli *Client) ContainerRestart(ctx context.Context, containerID string, options container.StopOptions) error {
|
|
| 15 | 16 |
query := url.Values{}
|
| 16 |
- if timeout != nil {
|
|
| 17 |
- query.Set("t", timetypes.DurationToSecondsString(*timeout))
|
|
| 17 |
+ if options.Timeout != nil {
|
|
| 18 |
+ query.Set("t", strconv.Itoa(*options.Timeout))
|
|
| 19 |
+ } |
|
| 20 |
+ if options.Signal != "" && versions.GreaterThanOrEqualTo(cli.version, "1.42") {
|
|
| 21 |
+ query.Set("signal", options.Signal)
|
|
| 18 | 22 |
} |
| 19 | 23 |
resp, err := cli.post(ctx, "/containers/"+containerID+"/restart", query, nil, nil) |
| 20 | 24 |
ensureReaderClosed(resp) |
| ... | ... |
@@ -8,8 +8,8 @@ import ( |
| 8 | 8 |
"net/http" |
| 9 | 9 |
"strings" |
| 10 | 10 |
"testing" |
| 11 |
- "time" |
|
| 12 | 11 |
|
| 12 |
+ "github.com/docker/docker/api/types/container" |
|
| 13 | 13 |
"github.com/docker/docker/errdefs" |
| 14 | 14 |
) |
| 15 | 15 |
|
| ... | ... |
@@ -17,20 +17,23 @@ func TestContainerRestartError(t *testing.T) {
|
| 17 | 17 |
client := &Client{
|
| 18 | 18 |
client: newMockClient(errorMock(http.StatusInternalServerError, "Server error")), |
| 19 | 19 |
} |
| 20 |
- timeout := 0 * time.Second |
|
| 21 |
- err := client.ContainerRestart(context.Background(), "nothing", &timeout) |
|
| 20 |
+ err := client.ContainerRestart(context.Background(), "nothing", container.StopOptions{})
|
|
| 22 | 21 |
if !errdefs.IsSystem(err) {
|
| 23 | 22 |
t.Fatalf("expected a Server Error, got %[1]T: %[1]v", err)
|
| 24 | 23 |
} |
| 25 | 24 |
} |
| 26 | 25 |
|
| 27 | 26 |
func TestContainerRestart(t *testing.T) {
|
| 28 |
- expectedURL := "/containers/container_id/restart" |
|
| 27 |
+ const expectedURL = "/v1.42/containers/container_id/restart" |
|
| 29 | 28 |
client := &Client{
|
| 30 | 29 |
client: newMockClient(func(req *http.Request) (*http.Response, error) {
|
| 31 | 30 |
if !strings.HasPrefix(req.URL.Path, expectedURL) {
|
| 32 | 31 |
return nil, fmt.Errorf("Expected URL '%s', got '%s'", expectedURL, req.URL)
|
| 33 | 32 |
} |
| 33 |
+ s := req.URL.Query().Get("signal")
|
|
| 34 |
+ if s != "SIGKILL" {
|
|
| 35 |
+ return nil, fmt.Errorf("signal not set in URL query. Expected 'SIGKILL', got '%s'", s)
|
|
| 36 |
+ } |
|
| 34 | 37 |
t := req.URL.Query().Get("t")
|
| 35 | 38 |
if t != "100" {
|
| 36 | 39 |
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) {
|
| 40 | 40 |
Body: io.NopCloser(bytes.NewReader([]byte(""))),
|
| 41 | 41 |
}, nil |
| 42 | 42 |
}), |
| 43 |
+ version: "1.42", |
|
| 43 | 44 |
} |
| 44 |
- timeout := 100 * time.Second |
|
| 45 |
- err := client.ContainerRestart(context.Background(), "container_id", &timeout) |
|
| 45 |
+ timeout := 100 |
|
| 46 |
+ err := client.ContainerRestart(context.Background(), "container_id", container.StopOptions{
|
|
| 47 |
+ Signal: "SIGKILL", |
|
| 48 |
+ Timeout: &timeout, |
|
| 49 |
+ }) |
|
| 46 | 50 |
if err != nil {
|
| 47 | 51 |
t.Fatal(err) |
| 48 | 52 |
} |
| ... | ... |
@@ -3,9 +3,10 @@ package client // import "github.com/docker/docker/client" |
| 3 | 3 |
import ( |
| 4 | 4 |
"context" |
| 5 | 5 |
"net/url" |
| 6 |
- "time" |
|
| 6 |
+ "strconv" |
|
| 7 | 7 |
|
| 8 |
- timetypes "github.com/docker/docker/api/types/time" |
|
| 8 |
+ "github.com/docker/docker/api/types/container" |
|
| 9 |
+ "github.com/docker/docker/api/types/versions" |
|
| 9 | 10 |
) |
| 10 | 11 |
|
| 11 | 12 |
// ContainerStop stops a container. In case the container fails to stop |
| ... | ... |
@@ -15,10 +16,13 @@ import ( |
| 15 | 15 |
// If the timeout is nil, the container's StopTimeout value is used, if set, |
| 16 | 16 |
// otherwise the engine default. A negative timeout value can be specified, |
| 17 | 17 |
// meaning no timeout, i.e. no forceful termination is performed. |
| 18 |
-func (cli *Client) ContainerStop(ctx context.Context, containerID string, timeout *time.Duration) error {
|
|
| 18 |
+func (cli *Client) ContainerStop(ctx context.Context, containerID string, options container.StopOptions) error {
|
|
| 19 | 19 |
query := url.Values{}
|
| 20 |
- if timeout != nil {
|
|
| 21 |
- query.Set("t", timetypes.DurationToSecondsString(*timeout))
|
|
| 20 |
+ if options.Timeout != nil {
|
|
| 21 |
+ query.Set("t", strconv.Itoa(*options.Timeout))
|
|
| 22 |
+ } |
|
| 23 |
+ if options.Signal != "" && versions.GreaterThanOrEqualTo(cli.version, "1.42") {
|
|
| 24 |
+ query.Set("signal", options.Signal)
|
|
| 22 | 25 |
} |
| 23 | 26 |
resp, err := cli.post(ctx, "/containers/"+containerID+"/stop", query, nil, nil) |
| 24 | 27 |
ensureReaderClosed(resp) |
| ... | ... |
@@ -8,8 +8,8 @@ import ( |
| 8 | 8 |
"net/http" |
| 9 | 9 |
"strings" |
| 10 | 10 |
"testing" |
| 11 |
- "time" |
|
| 12 | 11 |
|
| 12 |
+ "github.com/docker/docker/api/types/container" |
|
| 13 | 13 |
"github.com/docker/docker/errdefs" |
| 14 | 14 |
) |
| 15 | 15 |
|
| ... | ... |
@@ -17,20 +17,23 @@ func TestContainerStopError(t *testing.T) {
|
| 17 | 17 |
client := &Client{
|
| 18 | 18 |
client: newMockClient(errorMock(http.StatusInternalServerError, "Server error")), |
| 19 | 19 |
} |
| 20 |
- timeout := 0 * time.Second |
|
| 21 |
- err := client.ContainerStop(context.Background(), "nothing", &timeout) |
|
| 20 |
+ err := client.ContainerStop(context.Background(), "nothing", container.StopOptions{})
|
|
| 22 | 21 |
if !errdefs.IsSystem(err) {
|
| 23 | 22 |
t.Fatalf("expected a Server Error, got %[1]T: %[1]v", err)
|
| 24 | 23 |
} |
| 25 | 24 |
} |
| 26 | 25 |
|
| 27 | 26 |
func TestContainerStop(t *testing.T) {
|
| 28 |
- expectedURL := "/containers/container_id/stop" |
|
| 27 |
+ const expectedURL = "/v1.42/containers/container_id/stop" |
|
| 29 | 28 |
client := &Client{
|
| 30 | 29 |
client: newMockClient(func(req *http.Request) (*http.Response, error) {
|
| 31 | 30 |
if !strings.HasPrefix(req.URL.Path, expectedURL) {
|
| 32 | 31 |
return nil, fmt.Errorf("Expected URL '%s', got '%s'", expectedURL, req.URL)
|
| 33 | 32 |
} |
| 33 |
+ s := req.URL.Query().Get("signal")
|
|
| 34 |
+ if s != "SIGKILL" {
|
|
| 35 |
+ return nil, fmt.Errorf("signal not set in URL query. Expected 'SIGKILL', got '%s'", s)
|
|
| 36 |
+ } |
|
| 34 | 37 |
t := req.URL.Query().Get("t")
|
| 35 | 38 |
if t != "100" {
|
| 36 | 39 |
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) {
|
| 40 | 40 |
Body: io.NopCloser(bytes.NewReader([]byte(""))),
|
| 41 | 41 |
}, nil |
| 42 | 42 |
}), |
| 43 |
+ version: "1.42", |
|
| 43 | 44 |
} |
| 44 |
- timeout := 100 * time.Second |
|
| 45 |
- err := client.ContainerStop(context.Background(), "container_id", &timeout) |
|
| 45 |
+ timeout := 100 |
|
| 46 |
+ err := client.ContainerStop(context.Background(), "container_id", container.StopOptions{
|
|
| 47 |
+ Signal: "SIGKILL", |
|
| 48 |
+ Timeout: &timeout, |
|
| 49 |
+ }) |
|
| 46 | 50 |
if err != nil {
|
| 47 | 51 |
t.Fatal(err) |
| 48 | 52 |
} |
| ... | ... |
@@ -5,7 +5,6 @@ import ( |
| 5 | 5 |
"io" |
| 6 | 6 |
"net" |
| 7 | 7 |
"net/http" |
| 8 |
- "time" |
|
| 9 | 8 |
|
| 10 | 9 |
"github.com/docker/docker/api/types" |
| 11 | 10 |
"github.com/docker/docker/api/types/container" |
| ... | ... |
@@ -65,12 +64,12 @@ type ContainerAPIClient interface {
|
| 65 | 65 |
ContainerRemove(ctx context.Context, container string, options types.ContainerRemoveOptions) error |
| 66 | 66 |
ContainerRename(ctx context.Context, container, newContainerName string) error |
| 67 | 67 |
ContainerResize(ctx context.Context, container string, options types.ResizeOptions) error |
| 68 |
- ContainerRestart(ctx context.Context, container string, timeout *time.Duration) error |
|
| 68 |
+ ContainerRestart(ctx context.Context, container string, options container.StopOptions) error |
|
| 69 | 69 |
ContainerStatPath(ctx context.Context, container, path string) (types.ContainerPathStat, error) |
| 70 | 70 |
ContainerStats(ctx context.Context, container string, stream bool) (types.ContainerStats, error) |
| 71 | 71 |
ContainerStatsOneShot(ctx context.Context, container string) (types.ContainerStats, error) |
| 72 | 72 |
ContainerStart(ctx context.Context, container string, options types.ContainerStartOptions) error |
| 73 |
- ContainerStop(ctx context.Context, container string, timeout *time.Duration) error |
|
| 73 |
+ ContainerStop(ctx context.Context, container string, options container.StopOptions) error |
|
| 74 | 74 |
ContainerTop(ctx context.Context, container string, arguments []string) (container.ContainerTopOKBody, error) |
| 75 | 75 |
ContainerUnpause(ctx context.Context, container string) error |
| 76 | 76 |
ContainerUpdate(ctx context.Context, container string, updateConfig container.UpdateConfig) (container.ContainerUpdateOKBody, error) |
| ... | ... |
@@ -913,8 +913,8 @@ func (s *DockerSuite) TestContainerAPIRestart(c *testing.T) {
|
| 913 | 913 |
assert.NilError(c, err) |
| 914 | 914 |
defer cli.Close() |
| 915 | 915 |
|
| 916 |
- timeout := 1 * time.Second |
|
| 917 |
- err = cli.ContainerRestart(context.Background(), name, &timeout) |
|
| 916 |
+ timeout := 1 |
|
| 917 |
+ err = cli.ContainerRestart(context.Background(), name, container.StopOptions{Timeout: &timeout})
|
|
| 918 | 918 |
assert.NilError(c, err) |
| 919 | 919 |
|
| 920 | 920 |
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) {
|
| 930 | 930 |
assert.NilError(c, err) |
| 931 | 931 |
defer cli.Close() |
| 932 | 932 |
|
| 933 |
- err = cli.ContainerRestart(context.Background(), name, nil) |
|
| 933 |
+ err = cli.ContainerRestart(context.Background(), name, container.StopOptions{})
|
|
| 934 | 934 |
assert.NilError(c, err) |
| 935 | 935 |
|
| 936 | 936 |
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) {
|
| 965 | 965 |
func (s *DockerSuite) TestContainerAPIStop(c *testing.T) {
|
| 966 | 966 |
name := "test-api-stop" |
| 967 | 967 |
runSleepingContainer(c, "-i", "--name", name) |
| 968 |
- timeout := 30 * time.Second |
|
| 968 |
+ timeout := 30 |
|
| 969 | 969 |
|
| 970 | 970 |
cli, err := client.NewClientWithOpts(client.FromEnv) |
| 971 | 971 |
assert.NilError(c, err) |
| 972 | 972 |
defer cli.Close() |
| 973 | 973 |
|
| 974 |
- err = cli.ContainerStop(context.Background(), name, &timeout) |
|
| 974 |
+ err = cli.ContainerStop(context.Background(), name, container.StopOptions{
|
|
| 975 |
+ Timeout: &timeout, |
|
| 976 |
+ }) |
|
| 975 | 977 |
assert.NilError(c, err) |
| 976 | 978 |
assert.Assert(c, waitInspect(name, "{{ .State.Running }}", "false", 60*time.Second) == nil)
|
| 977 | 979 |
|
| 978 | 980 |
// second call to start should give 304 |
| 979 | 981 |
// maybe add ContainerStartWithRaw to test it |
| 980 |
- err = cli.ContainerStop(context.Background(), name, &timeout) |
|
| 982 |
+ err = cli.ContainerStop(context.Background(), name, container.StopOptions{
|
|
| 983 |
+ Timeout: &timeout, |
|
| 984 |
+ }) |
|
| 981 | 985 |
assert.NilError(c, err) |
| 982 | 986 |
} |
| 983 | 987 |
|
| ... | ... |
@@ -1255,7 +1259,7 @@ func (s *DockerSuite) TestContainerAPIPostContainerStop(c *testing.T) {
|
| 1255 | 1255 |
assert.NilError(c, err) |
| 1256 | 1256 |
defer cli.Close() |
| 1257 | 1257 |
|
| 1258 |
- err = cli.ContainerStop(context.Background(), containerID, nil) |
|
| 1258 |
+ err = cli.ContainerStop(context.Background(), containerID, container.StopOptions{})
|
|
| 1259 | 1259 |
assert.NilError(c, err) |
| 1260 | 1260 |
assert.Assert(c, waitInspect(containerID, "{{ .State.Running }}", "false", 60*time.Second) == nil)
|
| 1261 | 1261 |
} |
| ... | ... |
@@ -8,6 +8,7 @@ import ( |
| 8 | 8 |
|
| 9 | 9 |
containerderrdefs "github.com/containerd/containerd/errdefs" |
| 10 | 10 |
"github.com/docker/docker/api/types" |
| 11 |
+ containertypes "github.com/docker/docker/api/types/container" |
|
| 11 | 12 |
"github.com/docker/docker/api/types/events" |
| 12 | 13 |
"github.com/docker/docker/api/types/filters" |
| 13 | 14 |
"github.com/docker/docker/api/types/versions" |
| ... | ... |
@@ -80,7 +81,7 @@ func TestPauseStopPausedContainer(t *testing.T) {
|
| 80 | 80 |
err := client.ContainerPause(ctx, cID) |
| 81 | 81 |
assert.NilError(t, err) |
| 82 | 82 |
|
| 83 |
- err = client.ContainerStop(ctx, cID, nil) |
|
| 83 |
+ err = client.ContainerStop(ctx, cID, containertypes.StopOptions{})
|
|
| 84 | 84 |
assert.NilError(t, err) |
| 85 | 85 |
|
| 86 | 86 |
poll.WaitOn(t, container.IsStopped(ctx, client, cID), poll.WithDelay(100*time.Millisecond)) |
| ... | ... |
@@ -143,7 +143,7 @@ func TestRenameAnonymousContainer(t *testing.T) {
|
| 143 | 143 |
assert.NilError(t, err) |
| 144 | 144 |
// Stop/Start the container to get registered |
| 145 | 145 |
// FIXME(vdemeester) this is a really weird behavior as it fails otherwise |
| 146 |
- err = client.ContainerStop(ctx, container1Name, nil) |
|
| 146 |
+ err = client.ContainerStop(ctx, container1Name, containertypes.StopOptions{})
|
|
| 147 | 147 |
assert.NilError(t, err) |
| 148 | 148 |
err = client.ContainerStart(ctx, container1Name, types.ContainerStartOptions{})
|
| 149 | 149 |
assert.NilError(t, err) |
| ... | ... |
@@ -9,6 +9,7 @@ import ( |
| 9 | 9 |
"time" |
| 10 | 10 |
|
| 11 | 11 |
"github.com/docker/docker/api/types" |
| 12 |
+ containertypes "github.com/docker/docker/api/types/container" |
|
| 12 | 13 |
"github.com/docker/docker/integration/internal/container" |
| 13 | 14 |
"gotest.tools/v3/assert" |
| 14 | 15 |
"gotest.tools/v3/icmd" |
| ... | ... |
@@ -56,8 +57,7 @@ func TestStopContainerWithTimeout(t *testing.T) {
|
| 56 | 56 |
t.Parallel() |
| 57 | 57 |
id := container.Run(ctx, t, client, testCmd) |
| 58 | 58 |
|
| 59 |
- timeout := time.Duration(d.timeout) * time.Second |
|
| 60 |
- err := client.ContainerStop(ctx, id, &timeout) |
|
| 59 |
+ err := client.ContainerStop(ctx, id, containertypes.StopOptions{Timeout: &d.timeout})
|
|
| 61 | 60 |
assert.NilError(t, err) |
| 62 | 61 |
|
| 63 | 62 |
poll.WaitOn(t, container.IsStopped(ctx, client, id), |
| ... | ... |
@@ -5,6 +5,7 @@ import ( |
| 5 | 5 |
"testing" |
| 6 | 6 |
"time" |
| 7 | 7 |
|
| 8 |
+ containertypes "github.com/docker/docker/api/types/container" |
|
| 8 | 9 |
"github.com/docker/docker/integration/internal/container" |
| 9 | 10 |
"gotest.tools/v3/assert" |
| 10 | 11 |
"gotest.tools/v3/poll" |
| ... | ... |
@@ -29,7 +30,7 @@ func TestStopContainerWithRestartPolicyAlways(t *testing.T) {
|
| 29 | 29 |
} |
| 30 | 30 |
|
| 31 | 31 |
for _, name := range names {
|
| 32 |
- err := client.ContainerStop(ctx, name, nil) |
|
| 32 |
+ err := client.ContainerStop(ctx, name, containertypes.StopOptions{})
|
|
| 33 | 33 |
assert.NilError(t, err) |
| 34 | 34 |
} |
| 35 | 35 |
|
| ... | ... |
@@ -6,6 +6,7 @@ import ( |
| 6 | 6 |
"testing" |
| 7 | 7 |
"time" |
| 8 | 8 |
|
| 9 |
+ containertypes "github.com/docker/docker/api/types/container" |
|
| 9 | 10 |
"github.com/docker/docker/integration/internal/container" |
| 10 | 11 |
"gotest.tools/v3/assert" |
| 11 | 12 |
"gotest.tools/v3/poll" |
| ... | ... |
@@ -53,8 +54,7 @@ func TestStopContainerWithTimeout(t *testing.T) {
|
| 53 | 53 |
t.Parallel() |
| 54 | 54 |
id := container.Run(ctx, t, client, testCmd) |
| 55 | 55 |
|
| 56 |
- timeout := time.Duration(d.timeout) * time.Second |
|
| 57 |
- err := client.ContainerStop(ctx, id, &timeout) |
|
| 56 |
+ err := client.ContainerStop(ctx, id, containertypes.StopOptions{Timeout: &d.timeout})
|
|
| 58 | 57 |
assert.NilError(t, err) |
| 59 | 58 |
|
| 60 | 59 |
poll.WaitOn(t, container.IsStopped(ctx, client, id), |
| ... | ... |
@@ -5,6 +5,7 @@ import ( |
| 5 | 5 |
"testing" |
| 6 | 6 |
"time" |
| 7 | 7 |
|
| 8 |
+ containertypes "github.com/docker/docker/api/types/container" |
|
| 8 | 9 |
"github.com/docker/docker/integration/internal/container" |
| 9 | 10 |
"github.com/docker/docker/testutil/request" |
| 10 | 11 |
"gotest.tools/v3/assert" |
| ... | ... |
@@ -86,7 +87,7 @@ func TestWaitBlocked(t *testing.T) {
|
| 86 | 86 |
|
| 87 | 87 |
waitResC, errC := cli.ContainerWait(ctx, containerID, "") |
| 88 | 88 |
|
| 89 |
- err := cli.ContainerStop(ctx, containerID, nil) |
|
| 89 |
+ err := cli.ContainerStop(ctx, containerID, containertypes.StopOptions{})
|
|
| 90 | 90 |
assert.NilError(t, err) |
| 91 | 91 |
|
| 92 | 92 |
select {
|