api: remove handling of HostConfig on POST /containers/{id}/start (api < v1.24)
API v1.20 (Docker Engine v1.11.0) and older allowed a HostConfig to be passed when starting a container. This feature was deprecated in API v1.21 (Docker Engine v1.10.0) in3e7405aea8
, and removed in API v1.23 (Docker Engine v1.12.0) in commit0a8386c8be
. API v1.23 and older are deprecated, and this patch removes the feature. Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
This commit is contained in:
parent
ffd877f948
commit
8758d08bb4
17 changed files with 12 additions and 462 deletions
|
@ -12,5 +12,4 @@ import (
|
||||||
// container configuration.
|
// container configuration.
|
||||||
type ContainerDecoder interface {
|
type ContainerDecoder interface {
|
||||||
DecodeConfig(src io.Reader) (*container.Config, *container.HostConfig, *network.NetworkingConfig, error)
|
DecodeConfig(src io.Reader) (*container.Config, *container.HostConfig, *network.NetworkingConfig, error)
|
||||||
DecodeHostConfig(src io.Reader) (*container.HostConfig, error)
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -38,7 +38,7 @@ type stateBackend interface {
|
||||||
ContainerResize(name string, height, width int) error
|
ContainerResize(name string, height, width int) error
|
||||||
ContainerRestart(ctx context.Context, name string, options container.StopOptions) error
|
ContainerRestart(ctx context.Context, name string, options container.StopOptions) error
|
||||||
ContainerRm(name string, config *backend.ContainerRmConfig) error
|
ContainerRm(name string, config *backend.ContainerRmConfig) error
|
||||||
ContainerStart(ctx context.Context, name string, hostConfig *container.HostConfig, checkpoint string, checkpointDir string) error
|
ContainerStart(ctx context.Context, name string, checkpoint string, checkpointDir string) error
|
||||||
ContainerStop(ctx context.Context, name string, options container.StopOptions) error
|
ContainerStop(ctx context.Context, name string, options container.StopOptions) error
|
||||||
ContainerUnpause(name string) error
|
ContainerUnpause(name string) error
|
||||||
ContainerUpdate(name string, hostConfig *container.HostConfig) (container.ContainerUpdateOKBody, error)
|
ContainerUpdate(name string, hostConfig *container.HostConfig) (container.ContainerUpdateOKBody, error)
|
||||||
|
|
|
@ -168,14 +168,6 @@ func (s *containerRouter) getContainersExport(ctx context.Context, w http.Respon
|
||||||
return s.backend.ContainerExport(ctx, vars["name"], w)
|
return s.backend.ContainerExport(ctx, vars["name"], w)
|
||||||
}
|
}
|
||||||
|
|
||||||
type bodyOnStartError struct{}
|
|
||||||
|
|
||||||
func (bodyOnStartError) Error() string {
|
|
||||||
return "starting container with non-empty request body was deprecated since API v1.22 and removed in v1.24"
|
|
||||||
}
|
|
||||||
|
|
||||||
func (bodyOnStartError) InvalidParameter() {}
|
|
||||||
|
|
||||||
func (s *containerRouter) postContainersStart(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
func (s *containerRouter) postContainersStart(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||||
// If contentLength is -1, we can assumed chunked encoding
|
// If contentLength is -1, we can assumed chunked encoding
|
||||||
// or more technically that the length is unknown
|
// or more technically that the length is unknown
|
||||||
|
@ -183,33 +175,17 @@ func (s *containerRouter) postContainersStart(ctx context.Context, w http.Respon
|
||||||
// net/http otherwise seems to swallow any headers related to chunked encoding
|
// net/http otherwise seems to swallow any headers related to chunked encoding
|
||||||
// including r.TransferEncoding
|
// including r.TransferEncoding
|
||||||
// allow a nil body for backwards compatibility
|
// allow a nil body for backwards compatibility
|
||||||
|
//
|
||||||
version := httputils.VersionFromContext(ctx)
|
|
||||||
var hostConfig *container.HostConfig
|
|
||||||
// A non-nil json object is at least 7 characters.
|
// A non-nil json object is at least 7 characters.
|
||||||
if r.ContentLength > 7 || r.ContentLength == -1 {
|
if r.ContentLength > 7 || r.ContentLength == -1 {
|
||||||
if versions.GreaterThanOrEqualTo(version, "1.24") {
|
return errdefs.InvalidParameter(errors.New("starting container with non-empty request body was deprecated since API v1.22 and removed in v1.24"))
|
||||||
return bodyOnStartError{}
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := httputils.CheckForJSON(r); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
c, err := s.decoder.DecodeHostConfig(r.Body)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
hostConfig = c
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := httputils.ParseForm(r); err != nil {
|
if err := httputils.ParseForm(r); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
checkpoint := r.Form.Get("checkpoint")
|
if err := s.backend.ContainerStart(ctx, vars["name"], r.Form.Get("checkpoint"), r.Form.Get("checkpoint-dir")); err != nil {
|
||||||
checkpointDir := r.Form.Get("checkpoint-dir")
|
|
||||||
if err := s.backend.ContainerStart(ctx, vars["name"], hostConfig, checkpoint, checkpointDir); err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -64,7 +64,7 @@ type ExecBackend interface {
|
||||||
// ContainerRm removes a container specified by `id`.
|
// ContainerRm removes a container specified by `id`.
|
||||||
ContainerRm(name string, config *backend.ContainerRmConfig) error
|
ContainerRm(name string, config *backend.ContainerRmConfig) error
|
||||||
// ContainerStart starts a new container
|
// ContainerStart starts a new container
|
||||||
ContainerStart(ctx context.Context, containerID string, hostConfig *container.HostConfig, checkpoint string, checkpointDir string) error
|
ContainerStart(ctx context.Context, containerID string, checkpoint string, checkpointDir string) error
|
||||||
// ContainerWait stops processing until the given container is stopped.
|
// ContainerWait stops processing until the given container is stopped.
|
||||||
ContainerWait(ctx context.Context, name string, condition containerpkg.WaitCondition) (<-chan containerpkg.StateStatus, error)
|
ContainerWait(ctx context.Context, name string, condition containerpkg.WaitCondition) (<-chan containerpkg.StateStatus, error)
|
||||||
}
|
}
|
||||||
|
|
|
@ -72,7 +72,7 @@ func (c *containerManager) Run(ctx context.Context, cID string, stdout, stderr i
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
if err := c.backend.ContainerStart(ctx, cID, nil, "", ""); err != nil {
|
if err := c.backend.ContainerStart(ctx, cID, "", ""); err != nil {
|
||||||
close(finished)
|
close(finished)
|
||||||
logCancellationError(cancelErrCh, "error from ContainerStart: "+err.Error())
|
logCancellationError(cancelErrCh, "error from ContainerStart: "+err.Error())
|
||||||
return err
|
return err
|
||||||
|
|
|
@ -46,7 +46,7 @@ func (m *MockBackend) CommitBuildStep(ctx context.Context, c backend.CommitConfi
|
||||||
return "", nil
|
return "", nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *MockBackend) ContainerStart(ctx context.Context, containerID string, hostConfig *container.HostConfig, checkpoint string, checkpointDir string) error {
|
func (m *MockBackend) ContainerStart(ctx context.Context, containerID string, checkpoint string, checkpointDir string) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -38,7 +38,7 @@ type Backend interface {
|
||||||
SetupIngress(clustertypes.NetworkCreateRequest, string) (<-chan struct{}, error)
|
SetupIngress(clustertypes.NetworkCreateRequest, string) (<-chan struct{}, error)
|
||||||
ReleaseIngress() (<-chan struct{}, error)
|
ReleaseIngress() (<-chan struct{}, error)
|
||||||
CreateManagedContainer(ctx context.Context, config backend.ContainerCreateConfig) (container.CreateResponse, error)
|
CreateManagedContainer(ctx context.Context, config backend.ContainerCreateConfig) (container.CreateResponse, error)
|
||||||
ContainerStart(ctx context.Context, name string, hostConfig *container.HostConfig, checkpoint string, checkpointDir string) error
|
ContainerStart(ctx context.Context, name string, checkpoint string, checkpointDir string) error
|
||||||
ContainerStop(ctx context.Context, name string, config container.StopOptions) error
|
ContainerStop(ctx context.Context, name string, config container.StopOptions) error
|
||||||
ContainerLogs(ctx context.Context, name string, config *container.LogsOptions) (msgs <-chan *backend.LogMessage, tty bool, err error)
|
ContainerLogs(ctx context.Context, name string, config *container.LogsOptions) (msgs <-chan *backend.LogMessage, tty bool, err error)
|
||||||
ConnectContainerToNetwork(containerName, networkName string, endpointConfig *network.EndpointSettings) error
|
ConnectContainerToNetwork(containerName, networkName string, endpointConfig *network.EndpointSettings) error
|
||||||
|
|
|
@ -347,7 +347,7 @@ func (c *containerAdapter) start(ctx context.Context) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return c.backend.ContainerStart(ctx, c.container.name(), nil, "", "")
|
return c.backend.ContainerStart(ctx, c.container.name(), "", "")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *containerAdapter) inspect(ctx context.Context) (types.ContainerJSON, error) {
|
func (c *containerAdapter) inspect(ctx context.Context) (types.ContainerJSON, error) {
|
||||||
|
|
|
@ -2,12 +2,10 @@ package daemon // import "github.com/docker/docker/daemon"
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"runtime"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/containerd/log"
|
"github.com/containerd/log"
|
||||||
"github.com/docker/docker/api/types/backend"
|
"github.com/docker/docker/api/types/backend"
|
||||||
containertypes "github.com/docker/docker/api/types/container"
|
|
||||||
"github.com/docker/docker/api/types/events"
|
"github.com/docker/docker/api/types/events"
|
||||||
"github.com/docker/docker/container"
|
"github.com/docker/docker/container"
|
||||||
"github.com/docker/docker/errdefs"
|
"github.com/docker/docker/errdefs"
|
||||||
|
@ -41,7 +39,7 @@ func validateState(ctr *container.Container) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// ContainerStart starts a container.
|
// ContainerStart starts a container.
|
||||||
func (daemon *Daemon) ContainerStart(ctx context.Context, name string, hostConfig *containertypes.HostConfig, checkpoint string, checkpointDir string) error {
|
func (daemon *Daemon) ContainerStart(ctx context.Context, name string, checkpoint string, checkpointDir string) error {
|
||||||
daemonCfg := daemon.config()
|
daemonCfg := daemon.config()
|
||||||
if checkpoint != "" && !daemonCfg.Experimental {
|
if checkpoint != "" && !daemonCfg.Experimental {
|
||||||
return errdefs.InvalidParameter(errors.New("checkpoint is only supported in experimental mode"))
|
return errdefs.InvalidParameter(errors.New("checkpoint is only supported in experimental mode"))
|
||||||
|
@ -55,51 +53,12 @@ func (daemon *Daemon) ContainerStart(ctx context.Context, name string, hostConfi
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Windows does not have the backwards compatibility issue here.
|
|
||||||
if runtime.GOOS != "windows" {
|
|
||||||
// This is kept for backward compatibility - hostconfig should be passed when
|
|
||||||
// creating a container, not during start.
|
|
||||||
if hostConfig != nil {
|
|
||||||
log.G(ctx).Warn("DEPRECATED: Setting host configuration options when the container starts is deprecated and has been removed in Docker 1.12")
|
|
||||||
oldNetworkMode := ctr.HostConfig.NetworkMode
|
|
||||||
if err := daemon.setSecurityOptions(&daemonCfg.Config, ctr, hostConfig); err != nil {
|
|
||||||
return errdefs.InvalidParameter(err)
|
|
||||||
}
|
|
||||||
if err := daemon.mergeAndVerifyLogConfig(&hostConfig.LogConfig); err != nil {
|
|
||||||
return errdefs.InvalidParameter(err)
|
|
||||||
}
|
|
||||||
if err := daemon.setHostConfig(ctr, hostConfig); err != nil {
|
|
||||||
return errdefs.InvalidParameter(err)
|
|
||||||
}
|
|
||||||
newNetworkMode := ctr.HostConfig.NetworkMode
|
|
||||||
if string(oldNetworkMode) != string(newNetworkMode) {
|
|
||||||
// if user has change the network mode on starting, clean up the
|
|
||||||
// old networks. It is a deprecated feature and has been removed in Docker 1.12
|
|
||||||
ctr.NetworkSettings.Networks = nil
|
|
||||||
}
|
|
||||||
if err := ctr.CheckpointTo(daemon.containersReplica); err != nil {
|
|
||||||
return errdefs.System(err)
|
|
||||||
}
|
|
||||||
ctr.InitDNSHostConfig()
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if hostConfig != nil {
|
|
||||||
return errdefs.InvalidParameter(errors.New("Supplying a hostconfig on start is not supported. It should be supplied on create"))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// check if hostConfig is in line with the current system settings.
|
// check if hostConfig is in line with the current system settings.
|
||||||
// It may happen cgroups are umounted or the like.
|
// It may happen cgroups are unmounted or the like.
|
||||||
if _, err = daemon.verifyContainerSettings(daemonCfg, ctr.HostConfig, nil, false); err != nil {
|
if _, err = daemon.verifyContainerSettings(daemonCfg, ctr.HostConfig, nil, false); err != nil {
|
||||||
return errdefs.InvalidParameter(err)
|
return errdefs.InvalidParameter(err)
|
||||||
}
|
}
|
||||||
// Adapt for old containers in case we have updates in this function and
|
|
||||||
// old containers never have chance to call the new function in create stage.
|
|
||||||
if hostConfig != nil {
|
|
||||||
if err := daemon.adaptContainerSettings(&daemonCfg.Config, ctr.HostConfig); err != nil {
|
|
||||||
return errdefs.InvalidParameter(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return daemon.containerStart(ctx, daemonCfg, ctr, checkpoint, checkpointDir, true)
|
return daemon.containerStart(ctx, daemonCfg, ctr, checkpoint, checkpointDir, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,239 +0,0 @@
|
||||||
// This file will be removed when we completely drop support for
|
|
||||||
// passing HostConfig to container start API.
|
|
||||||
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"net/http"
|
|
||||||
"strings"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/docker/docker/integration-cli/cli"
|
|
||||||
"github.com/docker/docker/testutil"
|
|
||||||
"github.com/docker/docker/testutil/request"
|
|
||||||
"gotest.tools/v3/assert"
|
|
||||||
is "gotest.tools/v3/assert/cmp"
|
|
||||||
)
|
|
||||||
|
|
||||||
func formatV123StartAPIURL(url string) string {
|
|
||||||
return "/v1.23" + url
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *DockerAPISuite) TestDeprecatedContainerAPIStartHostConfig(c *testing.T) {
|
|
||||||
name := "test-deprecated-api-124"
|
|
||||||
cli.DockerCmd(c, "create", "--name", name, "busybox")
|
|
||||||
config := map[string]interface{}{
|
|
||||||
"Binds": []string{"/aa:/bb"},
|
|
||||||
}
|
|
||||||
res, body, err := request.Post(testutil.GetContext(c), "/containers/"+name+"/start", request.JSONBody(config))
|
|
||||||
assert.NilError(c, err)
|
|
||||||
assert.Equal(c, res.StatusCode, http.StatusBadRequest)
|
|
||||||
buf, err := request.ReadBody(body)
|
|
||||||
assert.NilError(c, err)
|
|
||||||
|
|
||||||
assert.Equal(c, res.StatusCode, http.StatusBadRequest)
|
|
||||||
assert.Assert(c, strings.Contains(string(buf), "was deprecated since API v1.22"))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *DockerAPISuite) TestDeprecatedContainerAPIStartVolumeBinds(c *testing.T) {
|
|
||||||
// TODO Windows CI: Investigate further why this fails on Windows to Windows CI.
|
|
||||||
testRequires(c, DaemonIsLinux)
|
|
||||||
path := "/foo"
|
|
||||||
if testEnv.DaemonInfo.OSType == "windows" {
|
|
||||||
path = `c:\foo`
|
|
||||||
}
|
|
||||||
name := "testing"
|
|
||||||
config := map[string]interface{}{
|
|
||||||
"Image": "busybox",
|
|
||||||
"Volumes": map[string]struct{}{path: {}},
|
|
||||||
}
|
|
||||||
|
|
||||||
res, _, err := request.Post(testutil.GetContext(c), formatV123StartAPIURL("/containers/create?name="+name), request.JSONBody(config))
|
|
||||||
assert.NilError(c, err)
|
|
||||||
assert.Equal(c, res.StatusCode, http.StatusCreated)
|
|
||||||
|
|
||||||
bindPath := RandomTmpDirPath("test", testEnv.DaemonInfo.OSType)
|
|
||||||
config = map[string]interface{}{
|
|
||||||
"Binds": []string{bindPath + ":" + path},
|
|
||||||
}
|
|
||||||
res, _, err = request.Post(testutil.GetContext(c), formatV123StartAPIURL("/containers/"+name+"/start"), request.JSONBody(config))
|
|
||||||
assert.NilError(c, err)
|
|
||||||
assert.Equal(c, res.StatusCode, http.StatusNoContent)
|
|
||||||
|
|
||||||
pth, err := inspectMountSourceField(name, path)
|
|
||||||
assert.NilError(c, err)
|
|
||||||
assert.Equal(c, pth, bindPath, "expected volume host path to be %s, got %s", bindPath, pth)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test for GH#10618
|
|
||||||
func (s *DockerAPISuite) TestDeprecatedContainerAPIStartDupVolumeBinds(c *testing.T) {
|
|
||||||
// TODO Windows to Windows CI - Port this
|
|
||||||
testRequires(c, DaemonIsLinux)
|
|
||||||
name := "testdups"
|
|
||||||
config := map[string]interface{}{
|
|
||||||
"Image": "busybox",
|
|
||||||
"Volumes": map[string]struct{}{"/tmp": {}},
|
|
||||||
}
|
|
||||||
|
|
||||||
res, _, err := request.Post(testutil.GetContext(c), formatV123StartAPIURL("/containers/create?name="+name), request.JSONBody(config))
|
|
||||||
assert.NilError(c, err)
|
|
||||||
assert.Equal(c, res.StatusCode, http.StatusCreated)
|
|
||||||
|
|
||||||
bindPath1 := RandomTmpDirPath("test1", testEnv.DaemonInfo.OSType)
|
|
||||||
bindPath2 := RandomTmpDirPath("test2", testEnv.DaemonInfo.OSType)
|
|
||||||
|
|
||||||
config = map[string]interface{}{
|
|
||||||
"Binds": []string{bindPath1 + ":/tmp", bindPath2 + ":/tmp"},
|
|
||||||
}
|
|
||||||
res, body, err := request.Post(testutil.GetContext(c), formatV123StartAPIURL("/containers/"+name+"/start"), request.JSONBody(config))
|
|
||||||
assert.NilError(c, err)
|
|
||||||
|
|
||||||
buf, err := request.ReadBody(body)
|
|
||||||
assert.NilError(c, err)
|
|
||||||
assert.Equal(c, res.StatusCode, http.StatusBadRequest)
|
|
||||||
assert.Assert(c, strings.Contains(string(buf), "Duplicate mount point"), "Expected failure due to duplicate bind mounts to same path, instead got: %q with error: %v", string(buf), err)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *DockerAPISuite) TestDeprecatedContainerAPIStartVolumesFrom(c *testing.T) {
|
|
||||||
// TODO Windows to Windows CI - Port this
|
|
||||||
testRequires(c, DaemonIsLinux)
|
|
||||||
volName := "voltst"
|
|
||||||
volPath := "/tmp"
|
|
||||||
|
|
||||||
cli.DockerCmd(c, "run", "--name", volName, "-v", volPath, "busybox")
|
|
||||||
|
|
||||||
name := "TestContainerAPIStartVolumesFrom"
|
|
||||||
config := map[string]interface{}{
|
|
||||||
"Image": "busybox",
|
|
||||||
"Volumes": map[string]struct{}{volPath: {}},
|
|
||||||
}
|
|
||||||
|
|
||||||
res, _, err := request.Post(testutil.GetContext(c), formatV123StartAPIURL("/containers/create?name="+name), request.JSONBody(config))
|
|
||||||
assert.NilError(c, err)
|
|
||||||
assert.Equal(c, res.StatusCode, http.StatusCreated)
|
|
||||||
|
|
||||||
config = map[string]interface{}{
|
|
||||||
"VolumesFrom": []string{volName},
|
|
||||||
}
|
|
||||||
res, _, err = request.Post(testutil.GetContext(c), formatV123StartAPIURL("/containers/"+name+"/start"), request.JSONBody(config))
|
|
||||||
assert.NilError(c, err)
|
|
||||||
assert.Equal(c, res.StatusCode, http.StatusNoContent)
|
|
||||||
|
|
||||||
pth, err := inspectMountSourceField(name, volPath)
|
|
||||||
assert.NilError(c, err)
|
|
||||||
pth2, err := inspectMountSourceField(volName, volPath)
|
|
||||||
assert.NilError(c, err)
|
|
||||||
assert.Equal(c, pth, pth2, "expected volume host path to be %s, got %s", pth, pth2)
|
|
||||||
}
|
|
||||||
|
|
||||||
// #9981 - Allow a docker created volume (ie, one in /var/lib/docker/volumes) to be used to overwrite (via passing in Binds on api start) an existing volume
|
|
||||||
func (s *DockerAPISuite) TestDeprecatedPostContainerBindNormalVolume(c *testing.T) {
|
|
||||||
// TODO Windows to Windows CI - Port this
|
|
||||||
testRequires(c, DaemonIsLinux)
|
|
||||||
cli.DockerCmd(c, "create", "-v", "/foo", "--name=one", "busybox")
|
|
||||||
|
|
||||||
fooDir, err := inspectMountSourceField("one", "/foo")
|
|
||||||
assert.NilError(c, err)
|
|
||||||
|
|
||||||
cli.DockerCmd(c, "create", "-v", "/foo", "--name=two", "busybox")
|
|
||||||
|
|
||||||
bindSpec := map[string][]string{"Binds": {fooDir + ":/foo"}}
|
|
||||||
res, _, err := request.Post(testutil.GetContext(c), formatV123StartAPIURL("/containers/two/start"), request.JSONBody(bindSpec))
|
|
||||||
assert.NilError(c, err)
|
|
||||||
assert.Equal(c, res.StatusCode, http.StatusNoContent)
|
|
||||||
|
|
||||||
fooDir2, err := inspectMountSourceField("two", "/foo")
|
|
||||||
assert.NilError(c, err)
|
|
||||||
assert.Equal(c, fooDir2, fooDir, "expected volume path to be %s, got: %s", fooDir, fooDir2)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *DockerAPISuite) TestDeprecatedStartWithTooLowMemoryLimit(c *testing.T) {
|
|
||||||
// TODO Windows: Port once memory is supported
|
|
||||||
testRequires(c, DaemonIsLinux)
|
|
||||||
containerID := cli.DockerCmd(c, "create", "busybox").Stdout()
|
|
||||||
containerID = strings.TrimSpace(containerID)
|
|
||||||
|
|
||||||
const config = `{
|
|
||||||
"CpuShares": 100,
|
|
||||||
"Memory": 524287
|
|
||||||
}`
|
|
||||||
|
|
||||||
res, body, err := request.Post(testutil.GetContext(c), formatV123StartAPIURL("/containers/"+containerID+"/start"), request.RawString(config), request.JSON)
|
|
||||||
assert.NilError(c, err)
|
|
||||||
b, err := request.ReadBody(body)
|
|
||||||
assert.NilError(c, err)
|
|
||||||
assert.Equal(c, res.StatusCode, http.StatusBadRequest)
|
|
||||||
assert.Assert(c, is.Contains(string(b), "Minimum memory limit allowed is 6MB"))
|
|
||||||
}
|
|
||||||
|
|
||||||
// #14640
|
|
||||||
func (s *DockerAPISuite) TestDeprecatedPostContainersStartWithoutLinksInHostConfig(c *testing.T) {
|
|
||||||
// TODO Windows: Windows doesn't support supplying a hostconfig on start.
|
|
||||||
// An alternate test could be written to validate the negative testing aspect of this
|
|
||||||
testRequires(c, DaemonIsLinux)
|
|
||||||
name := "test-host-config-links"
|
|
||||||
cli.DockerCmd(c, append([]string{"create", "--name", name, "busybox"}, sleepCommandForDaemonPlatform()...)...)
|
|
||||||
|
|
||||||
hc := inspectFieldJSON(c, name, "HostConfig")
|
|
||||||
config := `{"HostConfig":` + hc + `}`
|
|
||||||
|
|
||||||
res, b, err := request.Post(testutil.GetContext(c), formatV123StartAPIURL("/containers/"+name+"/start"), request.RawString(config), request.JSON)
|
|
||||||
assert.NilError(c, err)
|
|
||||||
assert.Equal(c, res.StatusCode, http.StatusNoContent)
|
|
||||||
b.Close()
|
|
||||||
}
|
|
||||||
|
|
||||||
// #14640
|
|
||||||
func (s *DockerAPISuite) TestDeprecatedPostContainersStartWithLinksInHostConfig(c *testing.T) {
|
|
||||||
// TODO Windows: Windows doesn't support supplying a hostconfig on start.
|
|
||||||
// An alternate test could be written to validate the negative testing aspect of this
|
|
||||||
testRequires(c, DaemonIsLinux)
|
|
||||||
name := "test-host-config-links"
|
|
||||||
cli.DockerCmd(c, "run", "--name", "foo", "-d", "busybox", "top")
|
|
||||||
cli.DockerCmd(c, "create", "--name", name, "--link", "foo:bar", "busybox", "top")
|
|
||||||
|
|
||||||
hc := inspectFieldJSON(c, name, "HostConfig")
|
|
||||||
config := `{"HostConfig":` + hc + `}`
|
|
||||||
|
|
||||||
res, b, err := request.Post(testutil.GetContext(c), formatV123StartAPIURL("/containers/"+name+"/start"), request.RawString(config), request.JSON)
|
|
||||||
assert.NilError(c, err)
|
|
||||||
assert.Equal(c, res.StatusCode, http.StatusNoContent)
|
|
||||||
b.Close()
|
|
||||||
}
|
|
||||||
|
|
||||||
// #14640
|
|
||||||
func (s *DockerAPISuite) TestDeprecatedPostContainersStartWithLinksInHostConfigIdLinked(c *testing.T) {
|
|
||||||
// Windows does not support links
|
|
||||||
testRequires(c, DaemonIsLinux)
|
|
||||||
const name = "test-host-config-links"
|
|
||||||
containerID := cli.DockerCmd(c, "run", "--name", "link0", "-d", "busybox", "top").Combined()
|
|
||||||
containerID = strings.TrimSpace(containerID)
|
|
||||||
defer cli.DockerCmd(c, "stop", "link0")
|
|
||||||
cli.DockerCmd(c, "create", "--name", name, "--link", containerID, "busybox", "top")
|
|
||||||
defer cli.DockerCmd(c, "stop", name)
|
|
||||||
|
|
||||||
hc := inspectFieldJSON(c, name, "HostConfig")
|
|
||||||
config := `{"HostConfig":` + hc + `}`
|
|
||||||
|
|
||||||
res, b, err := request.Post(testutil.GetContext(c), formatV123StartAPIURL("/containers/"+name+"/start"), request.RawString(config), request.JSON)
|
|
||||||
assert.NilError(c, err)
|
|
||||||
assert.Equal(c, res.StatusCode, http.StatusNoContent)
|
|
||||||
b.Close()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *DockerAPISuite) TestDeprecatedStartWithNilDNS(c *testing.T) {
|
|
||||||
// TODO Windows: Add once DNS is supported
|
|
||||||
testRequires(c, DaemonIsLinux)
|
|
||||||
containerID := cli.DockerCmd(c, "create", "busybox").Stdout()
|
|
||||||
containerID = strings.TrimSpace(containerID)
|
|
||||||
|
|
||||||
const config = `{"HostConfig": {"Dns": null}}`
|
|
||||||
|
|
||||||
res, b, err := request.Post(testutil.GetContext(c), formatV123StartAPIURL("/containers/"+containerID+"/start"), request.RawString(config), request.JSON)
|
|
||||||
assert.NilError(c, err)
|
|
||||||
assert.Equal(c, res.StatusCode, http.StatusNoContent)
|
|
||||||
b.Close()
|
|
||||||
|
|
||||||
dns := inspectFieldJSON(c, containerID, "HostConfig.Dns")
|
|
||||||
assert.Equal(c, dns, "[]")
|
|
||||||
}
|
|
|
@ -1,33 +0,0 @@
|
||||||
//go:build !windows
|
|
||||||
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"strings"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/docker/docker/integration-cli/cli"
|
|
||||||
"github.com/docker/docker/testutil"
|
|
||||||
"github.com/docker/docker/testutil/request"
|
|
||||||
"gotest.tools/v3/assert"
|
|
||||||
)
|
|
||||||
|
|
||||||
// #19100 This is a deprecated feature test, it should be removed in Docker 1.12
|
|
||||||
func (s *DockerNetworkSuite) TestDeprecatedDockerNetworkStartAPIWithHostconfig(c *testing.T) {
|
|
||||||
const netName = "test"
|
|
||||||
const conName = "foo"
|
|
||||||
cli.DockerCmd(c, "network", "create", netName)
|
|
||||||
cli.DockerCmd(c, "create", "--name", conName, "busybox", "top")
|
|
||||||
|
|
||||||
config := map[string]interface{}{
|
|
||||||
"HostConfig": map[string]interface{}{
|
|
||||||
"NetworkMode": netName,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
_, _, err := request.Post(testutil.GetContext(c), formatV123StartAPIURL("/containers/"+conName+"/start"), request.JSONBody(config))
|
|
||||||
assert.NilError(c, err)
|
|
||||||
cli.WaitRun(c, conName)
|
|
||||||
networks := inspectField(c, conName, "NetworkSettings.Networks")
|
|
||||||
assert.Assert(c, strings.Contains(networks, netName), "Should contain '%s' network", netName)
|
|
||||||
assert.Assert(c, !strings.Contains(networks, "bridge"), "Should not contain 'bridge' network")
|
|
||||||
}
|
|
|
@ -2,7 +2,6 @@ package container // import "github.com/docker/docker/integration/container"
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"net/http"
|
"net/http"
|
||||||
"runtime"
|
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/docker/docker/testutil"
|
"github.com/docker/docker/testutil"
|
||||||
|
@ -25,14 +24,6 @@ func TestContainerInvalidJSON(t *testing.T) {
|
||||||
"/exec/foobar/start",
|
"/exec/foobar/start",
|
||||||
}
|
}
|
||||||
|
|
||||||
// windows doesnt support API < v1.24
|
|
||||||
if runtime.GOOS != "windows" {
|
|
||||||
endpoints = append(
|
|
||||||
endpoints,
|
|
||||||
"/v1.23/containers/foobar/start", // accepts a body on API < v1.24
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, ep := range endpoints {
|
for _, ep := range endpoints {
|
||||||
ep := ep
|
ep := ep
|
||||||
t.Run(ep[1:], func(t *testing.T) {
|
t.Run(ep[1:], func(t *testing.T) {
|
||||||
|
|
|
@ -27,11 +27,6 @@ func (r ContainerDecoder) DecodeConfig(src io.Reader) (*container.Config, *conta
|
||||||
return decodeContainerConfig(src, si)
|
return decodeContainerConfig(src, si)
|
||||||
}
|
}
|
||||||
|
|
||||||
// DecodeHostConfig makes ContainerDecoder to implement httputils.ContainerDecoder
|
|
||||||
func (r ContainerDecoder) DecodeHostConfig(src io.Reader) (*container.HostConfig, error) {
|
|
||||||
return decodeHostConfig(src)
|
|
||||||
}
|
|
||||||
|
|
||||||
// decodeContainerConfig decodes a json encoded config into a ContainerConfigWrapper
|
// decodeContainerConfig decodes a json encoded config into a ContainerConfigWrapper
|
||||||
// struct and returns both a Config and a HostConfig struct, and performs some
|
// struct and returns both a Config and a HostConfig struct, and performs some
|
||||||
// validation. Certain parameters need daemon-side validation that cannot be done
|
// validation. Certain parameters need daemon-side validation that cannot be done
|
||||||
|
|
|
@ -1,18 +0,0 @@
|
||||||
{
|
|
||||||
"Binds": ["/tmp:/tmp"],
|
|
||||||
"ContainerIDFile": "",
|
|
||||||
"LxcConf": [],
|
|
||||||
"Privileged": false,
|
|
||||||
"PortBindings": {
|
|
||||||
"80/tcp": [
|
|
||||||
{
|
|
||||||
"HostIp": "0.0.0.0",
|
|
||||||
"HostPort": "49153"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"Links": ["/name:alias"],
|
|
||||||
"PublishAllPorts": false,
|
|
||||||
"CapAdd": ["NET_ADMIN"],
|
|
||||||
"CapDrop": ["MKNOD"]
|
|
||||||
}
|
|
|
@ -1,30 +0,0 @@
|
||||||
{
|
|
||||||
"Binds": ["/tmp:/tmp"],
|
|
||||||
"Links": ["redis3:redis"],
|
|
||||||
"LxcConf": {"lxc.utsname":"docker"},
|
|
||||||
"Memory": 0,
|
|
||||||
"MemorySwap": 0,
|
|
||||||
"CpuShares": 512,
|
|
||||||
"CpuPeriod": 100000,
|
|
||||||
"CpusetCpus": "0,1",
|
|
||||||
"CpusetMems": "0,1",
|
|
||||||
"BlkioWeight": 300,
|
|
||||||
"OomKillDisable": false,
|
|
||||||
"PortBindings": { "22/tcp": [{ "HostPort": "11022" }] },
|
|
||||||
"PublishAllPorts": false,
|
|
||||||
"Privileged": false,
|
|
||||||
"ReadonlyRootfs": false,
|
|
||||||
"Dns": ["8.8.8.8"],
|
|
||||||
"DnsSearch": [""],
|
|
||||||
"ExtraHosts": null,
|
|
||||||
"VolumesFrom": ["parent", "other:ro"],
|
|
||||||
"CapAdd": ["NET_ADMIN"],
|
|
||||||
"CapDrop": ["MKNOD"],
|
|
||||||
"RestartPolicy": { "Name": "", "MaximumRetryCount": 0 },
|
|
||||||
"NetworkMode": "bridge",
|
|
||||||
"Devices": [],
|
|
||||||
"Ulimits": [{}],
|
|
||||||
"LogConfig": { "Type": "json-file", "Config": {} },
|
|
||||||
"SecurityOpt": [""],
|
|
||||||
"CgroupParent": ""
|
|
||||||
}
|
|
|
@ -1,23 +1,12 @@
|
||||||
package runconfig // import "github.com/docker/docker/runconfig"
|
package runconfig // import "github.com/docker/docker/runconfig"
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"io"
|
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/docker/docker/api/types/container"
|
"github.com/docker/docker/api/types/container"
|
||||||
"github.com/docker/docker/api/types/network"
|
"github.com/docker/docker/api/types/network"
|
||||||
)
|
)
|
||||||
|
|
||||||
// DecodeHostConfig creates a HostConfig based on the specified Reader.
|
|
||||||
// It assumes the content of the reader will be JSON, and decodes it.
|
|
||||||
func decodeHostConfig(src io.Reader) (*container.HostConfig, error) {
|
|
||||||
var w ContainerConfigWrapper
|
|
||||||
if err := loadJSON(src, &w); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return w.getHostConfig(), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetDefaultNetModeIfBlank changes the NetworkMode in a HostConfig structure
|
// SetDefaultNetModeIfBlank changes the NetworkMode in a HostConfig structure
|
||||||
// to default if it is not populated. This ensures backwards compatibility after
|
// to default if it is not populated. This ensures backwards compatibility after
|
||||||
// the validation of the network mode was moved from the docker CLI to the
|
// the validation of the network mode was moved from the docker CLI to the
|
||||||
|
|
|
@ -3,51 +3,12 @@
|
||||||
package runconfig // import "github.com/docker/docker/runconfig"
|
package runconfig // import "github.com/docker/docker/runconfig"
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/docker/docker/api/types/container"
|
"github.com/docker/docker/api/types/container"
|
||||||
"github.com/docker/docker/pkg/sysinfo"
|
"github.com/docker/docker/pkg/sysinfo"
|
||||||
"gotest.tools/v3/assert"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestDecodeHostConfig(t *testing.T) {
|
|
||||||
fixtures := []struct {
|
|
||||||
file string
|
|
||||||
}{
|
|
||||||
{"fixtures/unix/container_hostconfig_1_14.json"},
|
|
||||||
{"fixtures/unix/container_hostconfig_1_19.json"},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, f := range fixtures {
|
|
||||||
b, err := os.ReadFile(f.file)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
c, err := decodeHostConfig(bytes.NewReader(b))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(fmt.Errorf("Error parsing %s: %v", f, err))
|
|
||||||
}
|
|
||||||
|
|
||||||
assert.Check(t, !c.Privileged)
|
|
||||||
|
|
||||||
if l := len(c.Binds); l != 1 {
|
|
||||||
t.Fatalf("Expected 1 bind, found %d\n", l)
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(c.CapAdd) != 1 && c.CapAdd[0] != "NET_ADMIN" {
|
|
||||||
t.Fatalf("Expected CapAdd NET_ADMIN, got %v", c.CapAdd)
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(c.CapDrop) != 1 && c.CapDrop[0] != "NET_ADMIN" {
|
|
||||||
t.Fatalf("Expected CapDrop NET_ADMIN, got %v", c.CapDrop)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestValidateResources(t *testing.T) {
|
func TestValidateResources(t *testing.T) {
|
||||||
type resourceTest struct {
|
type resourceTest struct {
|
||||||
ConfigCPURealtimePeriod int64
|
ConfigCPURealtimePeriod int64
|
||||||
|
|
Loading…
Reference in a new issue