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
This commit is contained in:
Sebastiaan van Stijn 2022-04-21 16:08:43 +02:00 committed by GitHub
commit 1a0c15abbb
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
28 changed files with 428 additions and 359 deletions

View file

@ -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)

View file

@ -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
}

View file

@ -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"]

View file

@ -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.

View file

@ -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)
}

View file

@ -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()
}
}
}

View file

@ -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)

View file

@ -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)
}

View file

@ -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)

View file

@ -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)
}

View file

@ -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)
}

View file

@ -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

View file

@ -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 {

View file

@ -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
}

View file

@ -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")
}
}
}()

View file

@ -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)
}

View file

@ -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 {

View file

@ -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

View file

@ -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"),
}

View file

@ -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 (
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)
}
if options.Timeout != nil {
stopTimeout = *options.Timeout
}
var wait time.Duration
if seconds >= 0 {
wait = time.Duration(seconds) * time.Second
if stopTimeout >= 0 {
wait = time.Duration(stopTimeout) * time.Second
}
success := func() error {
daemon.LogContainerEvent(container, "stop")
return nil
defer func() {
if retErr == nil {
daemon.LogContainerEvent(ctr, "stop")
}
stopSignal := container.StopSignal()
}()
// 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()
}
logrus.WithError(err).WithField("container", container.ID).Error("Error killing the container")
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
}
// container did exit, so ignore previous errors and continue
}
return success()
return nil
}

View file

@ -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.

View file

@ -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 {

View file

@ -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))

View file

@ -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)

View file

@ -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),

View file

@ -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)
}

View file

@ -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),

View file

@ -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 {