Merge pull request #47155 from thaJeztah/remove_deprecated_api_versions
api: remove deprecated API versions (API < v1.24)
This commit is contained in:
commit
9e075f3808
58 changed files with 200 additions and 1445 deletions
|
@ -2,9 +2,18 @@ package api // import "github.com/docker/docker/api"
|
||||||
|
|
||||||
// Common constants for daemon and client.
|
// Common constants for daemon and client.
|
||||||
const (
|
const (
|
||||||
// DefaultVersion of Current REST API
|
// DefaultVersion of the current REST API.
|
||||||
DefaultVersion = "1.45"
|
DefaultVersion = "1.45"
|
||||||
|
|
||||||
|
// MinSupportedAPIVersion is the minimum API version that can be supported
|
||||||
|
// by the API server, specified as "major.minor". Note that the daemon
|
||||||
|
// may be configured with a different minimum API version, as returned
|
||||||
|
// in [github.com/docker/docker/api/types.Version.MinAPIVersion].
|
||||||
|
//
|
||||||
|
// API requests for API versions lower than the configured version produce
|
||||||
|
// an error.
|
||||||
|
MinSupportedAPIVersion = "1.24"
|
||||||
|
|
||||||
// NoBaseImageSpecifier is the symbol used by the FROM
|
// NoBaseImageSpecifier is the symbol used by the FROM
|
||||||
// command to specify that no base image is to be used.
|
// command to specify that no base image is to be used.
|
||||||
NoBaseImageSpecifier = "scratch"
|
NoBaseImageSpecifier = "scratch"
|
||||||
|
|
|
@ -1,34 +0,0 @@
|
||||||
package server
|
|
||||||
|
|
||||||
import (
|
|
||||||
"net/http"
|
|
||||||
|
|
||||||
"github.com/docker/docker/api/server/httpstatus"
|
|
||||||
"github.com/docker/docker/api/server/httputils"
|
|
||||||
"github.com/docker/docker/api/types"
|
|
||||||
"github.com/docker/docker/api/types/versions"
|
|
||||||
"github.com/gorilla/mux"
|
|
||||||
"google.golang.org/grpc/status"
|
|
||||||
)
|
|
||||||
|
|
||||||
// makeErrorHandler makes an HTTP handler that decodes a Docker error and
|
|
||||||
// returns it in the response.
|
|
||||||
func makeErrorHandler(err error) http.HandlerFunc {
|
|
||||||
return func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
statusCode := httpstatus.FromError(err)
|
|
||||||
vars := mux.Vars(r)
|
|
||||||
if apiVersionSupportsJSONErrors(vars["version"]) {
|
|
||||||
response := &types.ErrorResponse{
|
|
||||||
Message: err.Error(),
|
|
||||||
}
|
|
||||||
_ = httputils.WriteJSON(w, statusCode, response)
|
|
||||||
} else {
|
|
||||||
http.Error(w, status.Convert(err).Message(), statusCode)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func apiVersionSupportsJSONErrors(version string) bool {
|
|
||||||
const firstAPIVersionWithJSONErrors = "1.23"
|
|
||||||
return version == "" || versions.GreaterThan(version, firstAPIVersionWithJSONErrors)
|
|
||||||
}
|
|
|
@ -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)
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,7 @@ import (
|
||||||
"net/http"
|
"net/http"
|
||||||
"runtime"
|
"runtime"
|
||||||
|
|
||||||
|
"github.com/docker/docker/api"
|
||||||
"github.com/docker/docker/api/server/httputils"
|
"github.com/docker/docker/api/server/httputils"
|
||||||
"github.com/docker/docker/api/types/versions"
|
"github.com/docker/docker/api/types/versions"
|
||||||
)
|
)
|
||||||
|
@ -13,19 +14,40 @@ import (
|
||||||
// VersionMiddleware is a middleware that
|
// VersionMiddleware is a middleware that
|
||||||
// validates the client and server versions.
|
// validates the client and server versions.
|
||||||
type VersionMiddleware struct {
|
type VersionMiddleware struct {
|
||||||
serverVersion string
|
serverVersion string
|
||||||
defaultVersion string
|
|
||||||
minVersion string
|
// defaultAPIVersion is the default API version provided by the API server,
|
||||||
|
// specified as "major.minor". It is usually configured to the latest API
|
||||||
|
// version [github.com/docker/docker/api.DefaultVersion].
|
||||||
|
//
|
||||||
|
// API requests for API versions greater than this version are rejected by
|
||||||
|
// the server and produce a [versionUnsupportedError].
|
||||||
|
defaultAPIVersion string
|
||||||
|
|
||||||
|
// minAPIVersion is the minimum API version provided by the API server,
|
||||||
|
// specified as "major.minor".
|
||||||
|
//
|
||||||
|
// API requests for API versions lower than this version are rejected by
|
||||||
|
// the server and produce a [versionUnsupportedError].
|
||||||
|
minAPIVersion string
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewVersionMiddleware creates a new VersionMiddleware
|
// NewVersionMiddleware creates a VersionMiddleware with the given versions.
|
||||||
// with the default versions.
|
func NewVersionMiddleware(serverVersion, defaultAPIVersion, minAPIVersion string) (*VersionMiddleware, error) {
|
||||||
func NewVersionMiddleware(s, d, m string) VersionMiddleware {
|
if versions.LessThan(defaultAPIVersion, api.MinSupportedAPIVersion) || versions.GreaterThan(defaultAPIVersion, api.DefaultVersion) {
|
||||||
return VersionMiddleware{
|
return nil, fmt.Errorf("invalid default API version (%s): must be between %s and %s", defaultAPIVersion, api.MinSupportedAPIVersion, api.DefaultVersion)
|
||||||
serverVersion: s,
|
|
||||||
defaultVersion: d,
|
|
||||||
minVersion: m,
|
|
||||||
}
|
}
|
||||||
|
if versions.LessThan(minAPIVersion, api.MinSupportedAPIVersion) || versions.GreaterThan(minAPIVersion, api.DefaultVersion) {
|
||||||
|
return nil, fmt.Errorf("invalid minimum API version (%s): must be between %s and %s", minAPIVersion, api.MinSupportedAPIVersion, api.DefaultVersion)
|
||||||
|
}
|
||||||
|
if versions.GreaterThan(minAPIVersion, defaultAPIVersion) {
|
||||||
|
return nil, fmt.Errorf("invalid API version: the minimum API version (%s) is higher than the default version (%s)", minAPIVersion, defaultAPIVersion)
|
||||||
|
}
|
||||||
|
return &VersionMiddleware{
|
||||||
|
serverVersion: serverVersion,
|
||||||
|
defaultAPIVersion: defaultAPIVersion,
|
||||||
|
minAPIVersion: minAPIVersion,
|
||||||
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type versionUnsupportedError struct {
|
type versionUnsupportedError struct {
|
||||||
|
@ -45,18 +67,18 @@ func (e versionUnsupportedError) InvalidParameter() {}
|
||||||
func (v VersionMiddleware) WrapHandler(handler func(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error) func(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
func (v VersionMiddleware) WrapHandler(handler func(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error) func(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||||
return func(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
return func(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||||
w.Header().Set("Server", fmt.Sprintf("Docker/%s (%s)", v.serverVersion, runtime.GOOS))
|
w.Header().Set("Server", fmt.Sprintf("Docker/%s (%s)", v.serverVersion, runtime.GOOS))
|
||||||
w.Header().Set("API-Version", v.defaultVersion)
|
w.Header().Set("API-Version", v.defaultAPIVersion)
|
||||||
w.Header().Set("OSType", runtime.GOOS)
|
w.Header().Set("OSType", runtime.GOOS)
|
||||||
|
|
||||||
apiVersion := vars["version"]
|
apiVersion := vars["version"]
|
||||||
if apiVersion == "" {
|
if apiVersion == "" {
|
||||||
apiVersion = v.defaultVersion
|
apiVersion = v.defaultAPIVersion
|
||||||
}
|
}
|
||||||
if versions.LessThan(apiVersion, v.minVersion) {
|
if versions.LessThan(apiVersion, v.minAPIVersion) {
|
||||||
return versionUnsupportedError{version: apiVersion, minVersion: v.minVersion}
|
return versionUnsupportedError{version: apiVersion, minVersion: v.minAPIVersion}
|
||||||
}
|
}
|
||||||
if versions.GreaterThan(apiVersion, v.defaultVersion) {
|
if versions.GreaterThan(apiVersion, v.defaultAPIVersion) {
|
||||||
return versionUnsupportedError{version: apiVersion, maxVersion: v.defaultVersion}
|
return versionUnsupportedError{version: apiVersion, maxVersion: v.defaultAPIVersion}
|
||||||
}
|
}
|
||||||
ctx = context.WithValue(ctx, httputils.APIVersionKey{}, apiVersion)
|
ctx = context.WithValue(ctx, httputils.APIVersionKey{}, apiVersion)
|
||||||
return handler(ctx, w, r, vars)
|
return handler(ctx, w, r, vars)
|
||||||
|
|
|
@ -2,27 +2,82 @@ package middleware // import "github.com/docker/docker/api/server/middleware"
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/http/httptest"
|
"net/http/httptest"
|
||||||
"runtime"
|
"runtime"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/docker/docker/api"
|
||||||
"github.com/docker/docker/api/server/httputils"
|
"github.com/docker/docker/api/server/httputils"
|
||||||
"gotest.tools/v3/assert"
|
"gotest.tools/v3/assert"
|
||||||
is "gotest.tools/v3/assert/cmp"
|
is "gotest.tools/v3/assert/cmp"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func TestNewVersionMiddlewareValidation(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
doc, defaultVersion, minVersion, expectedErr string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
doc: "defaults",
|
||||||
|
defaultVersion: api.DefaultVersion,
|
||||||
|
minVersion: api.MinSupportedAPIVersion,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
doc: "invalid default lower than min",
|
||||||
|
defaultVersion: api.MinSupportedAPIVersion,
|
||||||
|
minVersion: api.DefaultVersion,
|
||||||
|
expectedErr: fmt.Sprintf("invalid API version: the minimum API version (%s) is higher than the default version (%s)", api.DefaultVersion, api.MinSupportedAPIVersion),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
doc: "invalid default too low",
|
||||||
|
defaultVersion: "0.1",
|
||||||
|
minVersion: api.MinSupportedAPIVersion,
|
||||||
|
expectedErr: fmt.Sprintf("invalid default API version (0.1): must be between %s and %s", api.MinSupportedAPIVersion, api.DefaultVersion),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
doc: "invalid default too high",
|
||||||
|
defaultVersion: "9999.9999",
|
||||||
|
minVersion: api.DefaultVersion,
|
||||||
|
expectedErr: fmt.Sprintf("invalid default API version (9999.9999): must be between %s and %s", api.MinSupportedAPIVersion, api.DefaultVersion),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
doc: "invalid minimum too low",
|
||||||
|
defaultVersion: api.MinSupportedAPIVersion,
|
||||||
|
minVersion: "0.1",
|
||||||
|
expectedErr: fmt.Sprintf("invalid minimum API version (0.1): must be between %s and %s", api.MinSupportedAPIVersion, api.DefaultVersion),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
doc: "invalid minimum too high",
|
||||||
|
defaultVersion: api.DefaultVersion,
|
||||||
|
minVersion: "9999.9999",
|
||||||
|
expectedErr: fmt.Sprintf("invalid minimum API version (9999.9999): must be between %s and %s", api.MinSupportedAPIVersion, api.DefaultVersion),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range tests {
|
||||||
|
tc := tc
|
||||||
|
t.Run(tc.doc, func(t *testing.T) {
|
||||||
|
_, err := NewVersionMiddleware("1.2.3", tc.defaultVersion, tc.minVersion)
|
||||||
|
if tc.expectedErr == "" {
|
||||||
|
assert.Check(t, err)
|
||||||
|
} else {
|
||||||
|
assert.Check(t, is.Error(err, tc.expectedErr))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestVersionMiddlewareVersion(t *testing.T) {
|
func TestVersionMiddlewareVersion(t *testing.T) {
|
||||||
defaultVersion := "1.10.0"
|
expectedVersion := "<not set>"
|
||||||
minVersion := "1.2.0"
|
|
||||||
expectedVersion := defaultVersion
|
|
||||||
handler := func(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
handler := func(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||||
v := httputils.VersionFromContext(ctx)
|
v := httputils.VersionFromContext(ctx)
|
||||||
assert.Check(t, is.Equal(expectedVersion, v))
|
assert.Check(t, is.Equal(expectedVersion, v))
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
m := NewVersionMiddleware(defaultVersion, defaultVersion, minVersion)
|
m, err := NewVersionMiddleware("1.2.3", api.DefaultVersion, api.MinSupportedAPIVersion)
|
||||||
|
assert.NilError(t, err)
|
||||||
h := m.WrapHandler(handler)
|
h := m.WrapHandler(handler)
|
||||||
|
|
||||||
req, _ := http.NewRequest(http.MethodGet, "/containers/json", nil)
|
req, _ := http.NewRequest(http.MethodGet, "/containers/json", nil)
|
||||||
|
@ -35,19 +90,19 @@ func TestVersionMiddlewareVersion(t *testing.T) {
|
||||||
errString string
|
errString string
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
expectedVersion: "1.10.0",
|
expectedVersion: api.DefaultVersion,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
reqVersion: "1.9.0",
|
reqVersion: api.MinSupportedAPIVersion,
|
||||||
expectedVersion: "1.9.0",
|
expectedVersion: api.MinSupportedAPIVersion,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
reqVersion: "0.1",
|
reqVersion: "0.1",
|
||||||
errString: "client version 0.1 is too old. Minimum supported API version is 1.2.0, please upgrade your client to a newer version",
|
errString: fmt.Sprintf("client version 0.1 is too old. Minimum supported API version is %s, please upgrade your client to a newer version", api.MinSupportedAPIVersion),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
reqVersion: "9999.9999",
|
reqVersion: "9999.9999",
|
||||||
errString: "client version 9999.9999 is too new. Maximum supported API version is 1.10.0",
|
errString: fmt.Sprintf("client version 9999.9999 is too new. Maximum supported API version is %s", api.DefaultVersion),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -71,9 +126,8 @@ func TestVersionMiddlewareWithErrorsReturnsHeaders(t *testing.T) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
defaultVersion := "1.10.0"
|
m, err := NewVersionMiddleware("1.2.3", api.DefaultVersion, api.MinSupportedAPIVersion)
|
||||||
minVersion := "1.2.0"
|
assert.NilError(t, err)
|
||||||
m := NewVersionMiddleware(defaultVersion, defaultVersion, minVersion)
|
|
||||||
h := m.WrapHandler(handler)
|
h := m.WrapHandler(handler)
|
||||||
|
|
||||||
req, _ := http.NewRequest(http.MethodGet, "/containers/json", nil)
|
req, _ := http.NewRequest(http.MethodGet, "/containers/json", nil)
|
||||||
|
@ -81,12 +135,12 @@ func TestVersionMiddlewareWithErrorsReturnsHeaders(t *testing.T) {
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
|
|
||||||
vars := map[string]string{"version": "0.1"}
|
vars := map[string]string{"version": "0.1"}
|
||||||
err := h(ctx, resp, req, vars)
|
err = h(ctx, resp, req, vars)
|
||||||
assert.Check(t, is.ErrorContains(err, ""))
|
assert.Check(t, is.ErrorContains(err, ""))
|
||||||
|
|
||||||
hdr := resp.Result().Header
|
hdr := resp.Result().Header
|
||||||
assert.Check(t, is.Contains(hdr.Get("Server"), "Docker/"+defaultVersion))
|
assert.Check(t, is.Contains(hdr.Get("Server"), "Docker/1.2.3"))
|
||||||
assert.Check(t, is.Contains(hdr.Get("Server"), runtime.GOOS))
|
assert.Check(t, is.Contains(hdr.Get("Server"), runtime.GOOS))
|
||||||
assert.Check(t, is.Equal(hdr.Get("API-Version"), defaultVersion))
|
assert.Check(t, is.Equal(hdr.Get("API-Version"), api.DefaultVersion))
|
||||||
assert.Check(t, is.Equal(hdr.Get("OSType"), runtime.GOOS))
|
assert.Check(t, is.Equal(hdr.Get("OSType"), runtime.GOOS))
|
||||||
}
|
}
|
||||||
|
|
|
@ -42,6 +42,7 @@ func newImageBuildOptions(ctx context.Context, r *http.Request) (*types.ImageBui
|
||||||
SuppressOutput: httputils.BoolValue(r, "q"),
|
SuppressOutput: httputils.BoolValue(r, "q"),
|
||||||
NoCache: httputils.BoolValue(r, "nocache"),
|
NoCache: httputils.BoolValue(r, "nocache"),
|
||||||
ForceRemove: httputils.BoolValue(r, "forcerm"),
|
ForceRemove: httputils.BoolValue(r, "forcerm"),
|
||||||
|
PullParent: httputils.BoolValue(r, "pull"),
|
||||||
MemorySwap: httputils.Int64ValueOrZero(r, "memswap"),
|
MemorySwap: httputils.Int64ValueOrZero(r, "memswap"),
|
||||||
Memory: httputils.Int64ValueOrZero(r, "memory"),
|
Memory: httputils.Int64ValueOrZero(r, "memory"),
|
||||||
CPUShares: httputils.Int64ValueOrZero(r, "cpushares"),
|
CPUShares: httputils.Int64ValueOrZero(r, "cpushares"),
|
||||||
|
@ -66,17 +67,14 @@ func newImageBuildOptions(ctx context.Context, r *http.Request) (*types.ImageBui
|
||||||
return nil, invalidParam{errors.New("security options are not supported on " + runtime.GOOS)}
|
return nil, invalidParam{errors.New("security options are not supported on " + runtime.GOOS)}
|
||||||
}
|
}
|
||||||
|
|
||||||
version := httputils.VersionFromContext(ctx)
|
if httputils.BoolValue(r, "forcerm") {
|
||||||
if httputils.BoolValue(r, "forcerm") && versions.GreaterThanOrEqualTo(version, "1.12") {
|
|
||||||
options.Remove = true
|
options.Remove = true
|
||||||
} else if r.FormValue("rm") == "" && versions.GreaterThanOrEqualTo(version, "1.12") {
|
} else if r.FormValue("rm") == "" {
|
||||||
options.Remove = true
|
options.Remove = true
|
||||||
} else {
|
} else {
|
||||||
options.Remove = httputils.BoolValue(r, "rm")
|
options.Remove = httputils.BoolValue(r, "rm")
|
||||||
}
|
}
|
||||||
if httputils.BoolValue(r, "pull") && versions.GreaterThanOrEqualTo(version, "1.16") {
|
version := httputils.VersionFromContext(ctx)
|
||||||
options.PullParent = true
|
|
||||||
}
|
|
||||||
if versions.GreaterThanOrEqualTo(version, "1.32") {
|
if versions.GreaterThanOrEqualTo(version, "1.32") {
|
||||||
options.Platform = r.FormValue("platform")
|
options.Platform = r.FormValue("platform")
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,7 +24,6 @@ type execBackend interface {
|
||||||
// copyBackend includes functions to implement to provide container copy functionality.
|
// copyBackend includes functions to implement to provide container copy functionality.
|
||||||
type copyBackend interface {
|
type copyBackend interface {
|
||||||
ContainerArchivePath(name string, path string) (content io.ReadCloser, stat *types.ContainerPathStat, err error)
|
ContainerArchivePath(name string, path string) (content io.ReadCloser, stat *types.ContainerPathStat, err error)
|
||||||
ContainerCopy(name string, res string) (io.ReadCloser, error)
|
|
||||||
ContainerExport(ctx context.Context, name string, out io.Writer) error
|
ContainerExport(ctx context.Context, name string, out io.Writer) error
|
||||||
ContainerExtractToDir(name, path string, copyUIDGID, noOverwriteDirNonDir bool, content io.Reader) error
|
ContainerExtractToDir(name, path string, copyUIDGID, noOverwriteDirNonDir bool, content io.Reader) error
|
||||||
ContainerStatPath(name string, path string) (stat *types.ContainerPathStat, err error)
|
ContainerStatPath(name string, path string) (stat *types.ContainerPathStat, err error)
|
||||||
|
@ -39,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)
|
||||||
|
|
|
@ -56,7 +56,6 @@ func (r *containerRouter) initRoutes() {
|
||||||
router.NewPostRoute("/containers/{name:.*}/wait", r.postContainersWait),
|
router.NewPostRoute("/containers/{name:.*}/wait", r.postContainersWait),
|
||||||
router.NewPostRoute("/containers/{name:.*}/resize", r.postContainersResize),
|
router.NewPostRoute("/containers/{name:.*}/resize", r.postContainersResize),
|
||||||
router.NewPostRoute("/containers/{name:.*}/attach", r.postContainersAttach),
|
router.NewPostRoute("/containers/{name:.*}/attach", r.postContainersAttach),
|
||||||
router.NewPostRoute("/containers/{name:.*}/copy", r.postContainersCopy), // Deprecated since 1.8 (API v1.20), errors out since 1.12 (API v1.24)
|
|
||||||
router.NewPostRoute("/containers/{name:.*}/exec", r.postContainerExecCreate),
|
router.NewPostRoute("/containers/{name:.*}/exec", r.postContainerExecCreate),
|
||||||
router.NewPostRoute("/exec/{name:.*}/start", r.postContainerExecStart),
|
router.NewPostRoute("/exec/{name:.*}/start", r.postContainerExecStart),
|
||||||
router.NewPostRoute("/exec/{name:.*}/resize", r.postContainerExecResize),
|
router.NewPostRoute("/exec/{name:.*}/resize", r.postContainerExecResize),
|
||||||
|
|
|
@ -39,13 +39,6 @@ func (s *containerRouter) postCommit(ctx context.Context, w http.ResponseWriter,
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: remove pause arg, and always pause in backend
|
|
||||||
pause := httputils.BoolValue(r, "pause")
|
|
||||||
version := httputils.VersionFromContext(ctx)
|
|
||||||
if r.FormValue("pause") == "" && versions.GreaterThanOrEqualTo(version, "1.13") {
|
|
||||||
pause = true
|
|
||||||
}
|
|
||||||
|
|
||||||
config, _, _, err := s.decoder.DecodeConfig(r.Body)
|
config, _, _, err := s.decoder.DecodeConfig(r.Body)
|
||||||
if err != nil && !errors.Is(err, io.EOF) { // Do not fail if body is empty.
|
if err != nil && !errors.Is(err, io.EOF) { // Do not fail if body is empty.
|
||||||
return err
|
return err
|
||||||
|
@ -57,7 +50,7 @@ func (s *containerRouter) postCommit(ctx context.Context, w http.ResponseWriter,
|
||||||
}
|
}
|
||||||
|
|
||||||
imgID, err := s.backend.CreateImageFromContainer(ctx, r.Form.Get("container"), &backend.CreateImageConfig{
|
imgID, err := s.backend.CreateImageFromContainer(ctx, r.Form.Get("container"), &backend.CreateImageConfig{
|
||||||
Pause: pause,
|
Pause: httputils.BoolValueOrDefault(r, "pause", true), // TODO(dnephin): remove pause arg, and always pause in backend
|
||||||
Tag: ref,
|
Tag: ref,
|
||||||
Author: r.Form.Get("author"),
|
Author: r.Form.Get("author"),
|
||||||
Comment: r.Form.Get("comment"),
|
Comment: r.Form.Get("comment"),
|
||||||
|
@ -118,14 +111,11 @@ func (s *containerRouter) getContainersStats(ctx context.Context, w http.Respons
|
||||||
oneShot = httputils.BoolValueOrDefault(r, "one-shot", false)
|
oneShot = httputils.BoolValueOrDefault(r, "one-shot", false)
|
||||||
}
|
}
|
||||||
|
|
||||||
config := &backend.ContainerStatsConfig{
|
return s.backend.ContainerStats(ctx, vars["name"], &backend.ContainerStatsConfig{
|
||||||
Stream: stream,
|
Stream: stream,
|
||||||
OneShot: oneShot,
|
OneShot: oneShot,
|
||||||
OutStream: w,
|
OutStream: w,
|
||||||
Version: httputils.VersionFromContext(ctx),
|
})
|
||||||
}
|
|
||||||
|
|
||||||
return s.backend.ContainerStats(ctx, vars["name"], config)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *containerRouter) getContainersLogs(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
func (s *containerRouter) getContainersLogs(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||||
|
@ -178,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
|
||||||
|
@ -193,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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -255,25 +221,14 @@ func (s *containerRouter) postContainersStop(ctx context.Context, w http.Respons
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *containerRouter) postContainersKill(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
func (s *containerRouter) postContainersKill(_ context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||||
if err := httputils.ParseForm(r); err != nil {
|
if err := httputils.ParseForm(r); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
name := vars["name"]
|
name := vars["name"]
|
||||||
if err := s.backend.ContainerKill(name, r.Form.Get("signal")); err != nil {
|
if err := s.backend.ContainerKill(name, r.Form.Get("signal")); err != nil {
|
||||||
var isStopped bool
|
return errors.Wrapf(err, "cannot kill container: %s", name)
|
||||||
if errdefs.IsConflict(err) {
|
|
||||||
isStopped = true
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return error that's not caused because the container is stopped.
|
|
||||||
// Return error if the container is not running and the api is >= 1.20
|
|
||||||
// to keep backwards compatibility.
|
|
||||||
version := httputils.VersionFromContext(ctx)
|
|
||||||
if versions.GreaterThanOrEqualTo(version, "1.20") || !isStopped {
|
|
||||||
return errors.Wrapf(err, "Cannot kill container: %s", name)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
w.WriteHeader(http.StatusNoContent)
|
w.WriteHeader(http.StatusNoContent)
|
||||||
|
@ -512,7 +467,6 @@ func (s *containerRouter) postContainersCreate(ctx context.Context, w http.Respo
|
||||||
}
|
}
|
||||||
|
|
||||||
version := httputils.VersionFromContext(ctx)
|
version := httputils.VersionFromContext(ctx)
|
||||||
adjustCPUShares := versions.LessThan(version, "1.19")
|
|
||||||
|
|
||||||
// When using API 1.24 and under, the client is responsible for removing the container
|
// When using API 1.24 and under, the client is responsible for removing the container
|
||||||
if versions.LessThan(version, "1.25") {
|
if versions.LessThan(version, "1.25") {
|
||||||
|
@ -656,7 +610,6 @@ func (s *containerRouter) postContainersCreate(ctx context.Context, w http.Respo
|
||||||
Config: config,
|
Config: config,
|
||||||
HostConfig: hostConfig,
|
HostConfig: hostConfig,
|
||||||
NetworkingConfig: networkingConfig,
|
NetworkingConfig: networkingConfig,
|
||||||
AdjustCPUShares: adjustCPUShares,
|
|
||||||
Platform: platform,
|
Platform: platform,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -11,49 +11,10 @@ import (
|
||||||
|
|
||||||
"github.com/docker/docker/api/server/httputils"
|
"github.com/docker/docker/api/server/httputils"
|
||||||
"github.com/docker/docker/api/types"
|
"github.com/docker/docker/api/types"
|
||||||
"github.com/docker/docker/api/types/versions"
|
|
||||||
gddohttputil "github.com/golang/gddo/httputil"
|
gddohttputil "github.com/golang/gddo/httputil"
|
||||||
)
|
)
|
||||||
|
|
||||||
type pathError struct{}
|
// setContainerPathStatHeader encodes the stat to JSON, base64 encode, and place in a header.
|
||||||
|
|
||||||
func (pathError) Error() string {
|
|
||||||
return "Path cannot be empty"
|
|
||||||
}
|
|
||||||
|
|
||||||
func (pathError) InvalidParameter() {}
|
|
||||||
|
|
||||||
// postContainersCopy is deprecated in favor of getContainersArchive.
|
|
||||||
//
|
|
||||||
// Deprecated since 1.8 (API v1.20), errors out since 1.12 (API v1.24)
|
|
||||||
func (s *containerRouter) postContainersCopy(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
||||||
version := httputils.VersionFromContext(ctx)
|
|
||||||
if versions.GreaterThanOrEqualTo(version, "1.24") {
|
|
||||||
w.WriteHeader(http.StatusNotFound)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
cfg := types.CopyConfig{}
|
|
||||||
if err := httputils.ReadJSON(r, &cfg); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if cfg.Resource == "" {
|
|
||||||
return pathError{}
|
|
||||||
}
|
|
||||||
|
|
||||||
data, err := s.backend.ContainerCopy(vars["name"], cfg.Resource)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer data.Close()
|
|
||||||
|
|
||||||
w.Header().Set("Content-Type", "application/x-tar")
|
|
||||||
_, err = io.Copy(w, data)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// // Encode the stat to JSON, base64 encode, and place in a header.
|
|
||||||
func setContainerPathStatHeader(stat *types.ContainerPathStat, header http.Header) error {
|
func setContainerPathStatHeader(stat *types.ContainerPathStat, header http.Header) error {
|
||||||
statJSON, err := json.Marshal(stat)
|
statJSON, err := json.Marshal(stat)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -71,15 +71,6 @@ func (s *containerRouter) postContainerExecStart(ctx context.Context, w http.Res
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
version := httputils.VersionFromContext(ctx)
|
|
||||||
if versions.LessThan(version, "1.22") {
|
|
||||||
// API versions before 1.22 did not enforce application/json content-type.
|
|
||||||
// Allow older clients to work by patching the content-type.
|
|
||||||
if r.Header.Get("Content-Type") != "application/json" {
|
|
||||||
r.Header.Set("Content-Type", "application/json")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
var (
|
||||||
execName = vars["name"]
|
execName = vars["name"]
|
||||||
stdin, inStream io.ReadCloser
|
stdin, inStream io.ReadCloser
|
||||||
|
@ -96,6 +87,8 @@ func (s *containerRouter) postContainerExecStart(ctx context.Context, w http.Res
|
||||||
}
|
}
|
||||||
|
|
||||||
if execStartCheck.ConsoleSize != nil {
|
if execStartCheck.ConsoleSize != nil {
|
||||||
|
version := httputils.VersionFromContext(ctx)
|
||||||
|
|
||||||
// Not supported before 1.42
|
// Not supported before 1.42
|
||||||
if versions.LessThan(version, "1.42") {
|
if versions.LessThan(version, "1.42") {
|
||||||
execStartCheck.ConsoleSize = nil
|
execStartCheck.ConsoleSize = nil
|
||||||
|
|
|
@ -10,6 +10,7 @@ import (
|
||||||
"github.com/docker/docker/api/server/middleware"
|
"github.com/docker/docker/api/server/middleware"
|
||||||
"github.com/docker/docker/api/server/router"
|
"github.com/docker/docker/api/server/router"
|
||||||
"github.com/docker/docker/api/server/router/debug"
|
"github.com/docker/docker/api/server/router/debug"
|
||||||
|
"github.com/docker/docker/api/types"
|
||||||
"github.com/docker/docker/dockerversion"
|
"github.com/docker/docker/dockerversion"
|
||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp"
|
"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp"
|
||||||
|
@ -57,19 +58,13 @@ func (s *Server) makeHTTPHandler(handler httputils.APIFunc, operation string) ht
|
||||||
if statusCode >= 500 {
|
if statusCode >= 500 {
|
||||||
log.G(ctx).Errorf("Handler for %s %s returned error: %v", r.Method, r.URL.Path, err)
|
log.G(ctx).Errorf("Handler for %s %s returned error: %v", r.Method, r.URL.Path, err)
|
||||||
}
|
}
|
||||||
makeErrorHandler(err)(w, r)
|
_ = httputils.WriteJSON(w, statusCode, &types.ErrorResponse{
|
||||||
|
Message: err.Error(),
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}), operation).ServeHTTP
|
}), operation).ServeHTTP
|
||||||
}
|
}
|
||||||
|
|
||||||
type pageNotFoundError struct{}
|
|
||||||
|
|
||||||
func (pageNotFoundError) Error() string {
|
|
||||||
return "page not found"
|
|
||||||
}
|
|
||||||
|
|
||||||
func (pageNotFoundError) NotFound() {}
|
|
||||||
|
|
||||||
// CreateMux returns a new mux with all the routers registered.
|
// CreateMux returns a new mux with all the routers registered.
|
||||||
func (s *Server) CreateMux(routers ...router.Router) *mux.Router {
|
func (s *Server) CreateMux(routers ...router.Router) *mux.Router {
|
||||||
m := mux.NewRouter()
|
m := mux.NewRouter()
|
||||||
|
@ -91,7 +86,12 @@ func (s *Server) CreateMux(routers ...router.Router) *mux.Router {
|
||||||
m.Path("/debug" + r.Path()).Handler(f)
|
m.Path("/debug" + r.Path()).Handler(f)
|
||||||
}
|
}
|
||||||
|
|
||||||
notFoundHandler := makeErrorHandler(pageNotFoundError{})
|
notFoundHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
_ = httputils.WriteJSON(w, http.StatusNotFound, &types.ErrorResponse{
|
||||||
|
Message: "page not found",
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
m.HandleFunc(versionMatcher+"/{path:.*}", notFoundHandler)
|
m.HandleFunc(versionMatcher+"/{path:.*}", notFoundHandler)
|
||||||
m.NotFoundHandler = notFoundHandler
|
m.NotFoundHandler = notFoundHandler
|
||||||
m.MethodNotAllowedHandler = notFoundHandler
|
m.MethodNotAllowedHandler = notFoundHandler
|
||||||
|
|
|
@ -15,8 +15,11 @@ import (
|
||||||
func TestMiddlewares(t *testing.T) {
|
func TestMiddlewares(t *testing.T) {
|
||||||
srv := &Server{}
|
srv := &Server{}
|
||||||
|
|
||||||
const apiMinVersion = "1.12"
|
m, err := middleware.NewVersionMiddleware("0.1omega2", api.DefaultVersion, api.MinSupportedAPIVersion)
|
||||||
srv.UseMiddleware(middleware.NewVersionMiddleware("0.1omega2", api.DefaultVersion, apiMinVersion))
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
srv.UseMiddleware(*m)
|
||||||
|
|
||||||
req, _ := http.NewRequest(http.MethodGet, "/containers/json", nil)
|
req, _ := http.NewRequest(http.MethodGet, "/containers/json", nil)
|
||||||
resp := httptest.NewRecorder()
|
resp := httptest.NewRecorder()
|
||||||
|
|
|
@ -18,7 +18,6 @@ type ContainerCreateConfig struct {
|
||||||
HostConfig *container.HostConfig
|
HostConfig *container.HostConfig
|
||||||
NetworkingConfig *network.NetworkingConfig
|
NetworkingConfig *network.NetworkingConfig
|
||||||
Platform *ocispec.Platform
|
Platform *ocispec.Platform
|
||||||
AdjustCPUShares bool
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ContainerRmConfig holds arguments for the container remove
|
// ContainerRmConfig holds arguments for the container remove
|
||||||
|
@ -90,7 +89,6 @@ type ContainerStatsConfig struct {
|
||||||
Stream bool
|
Stream bool
|
||||||
OneShot bool
|
OneShot bool
|
||||||
OutStream io.Writer
|
OutStream io.Writer
|
||||||
Version string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ExecInspect holds information about a running process started
|
// ExecInspect holds information about a running process started
|
||||||
|
|
|
@ -1,14 +0,0 @@
|
||||||
# Legacy API type versions
|
|
||||||
|
|
||||||
This package includes types for legacy API versions. The stable version of the API types live in `api/types/*.go`.
|
|
||||||
|
|
||||||
Consider moving a type here when you need to keep backwards compatibility in the API. This legacy types are organized by the latest API version they appear in. For instance, types in the `v1p19` package are valid for API versions below or equal `1.19`. Types in the `v1p20` package are valid for the API version `1.20`, since the versions below that will use the legacy types in `v1p19`.
|
|
||||||
|
|
||||||
## Package name conventions
|
|
||||||
|
|
||||||
The package name convention is to use `v` as a prefix for the version number and `p`(patch) as a separator. We use this nomenclature due to a few restrictions in the Go package name convention:
|
|
||||||
|
|
||||||
1. We cannot use `.` because it's interpreted by the language, think of `v1.20.CallFunction`.
|
|
||||||
2. We cannot use `_` because golint complains about it. The code is actually valid, but it looks probably more weird: `v1_20.CallFunction`.
|
|
||||||
|
|
||||||
For instance, if you want to modify a type that was available in the version `1.21` of the API but it will have different fields in the version `1.22`, you want to create a new package under `api/types/versions/v1p21`.
|
|
|
@ -1,35 +0,0 @@
|
||||||
// Package v1p19 provides specific API types for the API version 1, patch 19.
|
|
||||||
package v1p19 // import "github.com/docker/docker/api/types/versions/v1p19"
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/docker/docker/api/types"
|
|
||||||
"github.com/docker/docker/api/types/container"
|
|
||||||
"github.com/docker/docker/api/types/versions/v1p20"
|
|
||||||
"github.com/docker/go-connections/nat"
|
|
||||||
)
|
|
||||||
|
|
||||||
// ContainerJSON is a backcompatibility struct for APIs prior to 1.20.
|
|
||||||
// Note this is not used by the Windows daemon.
|
|
||||||
type ContainerJSON struct {
|
|
||||||
*types.ContainerJSONBase
|
|
||||||
Volumes map[string]string
|
|
||||||
VolumesRW map[string]bool
|
|
||||||
Config *ContainerConfig
|
|
||||||
NetworkSettings *v1p20.NetworkSettings
|
|
||||||
}
|
|
||||||
|
|
||||||
// ContainerConfig is a backcompatibility struct for APIs prior to 1.20.
|
|
||||||
type ContainerConfig struct {
|
|
||||||
*container.Config
|
|
||||||
|
|
||||||
MacAddress string
|
|
||||||
NetworkDisabled bool
|
|
||||||
ExposedPorts map[nat.Port]struct{}
|
|
||||||
|
|
||||||
// backward compatibility, they now live in HostConfig
|
|
||||||
VolumeDriver string
|
|
||||||
Memory int64
|
|
||||||
MemorySwap int64
|
|
||||||
CPUShares int64 `json:"CpuShares"`
|
|
||||||
CPUSet string `json:"Cpuset"`
|
|
||||||
}
|
|
|
@ -1,40 +0,0 @@
|
||||||
// Package v1p20 provides specific API types for the API version 1, patch 20.
|
|
||||||
package v1p20 // import "github.com/docker/docker/api/types/versions/v1p20"
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/docker/docker/api/types"
|
|
||||||
"github.com/docker/docker/api/types/container"
|
|
||||||
"github.com/docker/go-connections/nat"
|
|
||||||
)
|
|
||||||
|
|
||||||
// ContainerJSON is a backcompatibility struct for the API 1.20
|
|
||||||
type ContainerJSON struct {
|
|
||||||
*types.ContainerJSONBase
|
|
||||||
Mounts []types.MountPoint
|
|
||||||
Config *ContainerConfig
|
|
||||||
NetworkSettings *NetworkSettings
|
|
||||||
}
|
|
||||||
|
|
||||||
// ContainerConfig is a backcompatibility struct used in ContainerJSON for the API 1.20
|
|
||||||
type ContainerConfig struct {
|
|
||||||
*container.Config
|
|
||||||
|
|
||||||
MacAddress string
|
|
||||||
NetworkDisabled bool
|
|
||||||
ExposedPorts map[nat.Port]struct{}
|
|
||||||
|
|
||||||
// backward compatibility, they now live in HostConfig
|
|
||||||
VolumeDriver string
|
|
||||||
}
|
|
||||||
|
|
||||||
// StatsJSON is a backcompatibility struct used in Stats for APIs prior to 1.21
|
|
||||||
type StatsJSON struct {
|
|
||||||
types.Stats
|
|
||||||
Network types.NetworkStats `json:"network,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// NetworkSettings is a backward compatible struct for APIs prior to 1.21
|
|
||||||
type NetworkSettings struct {
|
|
||||||
types.NetworkSettingsBase
|
|
||||||
types.DefaultNetworkSettings
|
|
||||||
}
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -256,7 +256,10 @@ func (cli *DaemonCli) start(opts *daemonOptions) (err error) {
|
||||||
pluginStore := plugin.NewStore()
|
pluginStore := plugin.NewStore()
|
||||||
|
|
||||||
var apiServer apiserver.Server
|
var apiServer apiserver.Server
|
||||||
cli.authzMiddleware = initMiddlewares(&apiServer, cli.Config, pluginStore)
|
cli.authzMiddleware, err = initMiddlewares(&apiServer, cli.Config, pluginStore)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "failed to start API server")
|
||||||
|
}
|
||||||
|
|
||||||
d, err := daemon.NewDaemon(ctx, cli.Config, pluginStore, cli.authzMiddleware)
|
d, err := daemon.NewDaemon(ctx, cli.Config, pluginStore, cli.authzMiddleware)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -708,14 +711,15 @@ func (opts routerOptions) Build() []router.Router {
|
||||||
return routers
|
return routers
|
||||||
}
|
}
|
||||||
|
|
||||||
func initMiddlewares(s *apiserver.Server, cfg *config.Config, pluginStore plugingetter.PluginGetter) *authorization.Middleware {
|
func initMiddlewares(s *apiserver.Server, cfg *config.Config, pluginStore plugingetter.PluginGetter) (*authorization.Middleware, error) {
|
||||||
v := dockerversion.Version
|
|
||||||
|
|
||||||
exp := middleware.NewExperimentalMiddleware(cfg.Experimental)
|
exp := middleware.NewExperimentalMiddleware(cfg.Experimental)
|
||||||
s.UseMiddleware(exp)
|
s.UseMiddleware(exp)
|
||||||
|
|
||||||
vm := middleware.NewVersionMiddleware(v, api.DefaultVersion, cfg.MinAPIVersion)
|
vm, err := middleware.NewVersionMiddleware(dockerversion.Version, api.DefaultVersion, cfg.MinAPIVersion)
|
||||||
s.UseMiddleware(vm)
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
s.UseMiddleware(*vm)
|
||||||
|
|
||||||
if cfg.CorsHeaders != "" {
|
if cfg.CorsHeaders != "" {
|
||||||
c := middleware.NewCORSMiddleware(cfg.CorsHeaders)
|
c := middleware.NewCORSMiddleware(cfg.CorsHeaders)
|
||||||
|
@ -724,7 +728,7 @@ func initMiddlewares(s *apiserver.Server, cfg *config.Config, pluginStore plugin
|
||||||
|
|
||||||
authzMiddleware := authorization.NewMiddleware(cfg.AuthorizationPlugins, pluginStore)
|
authzMiddleware := authorization.NewMiddleware(cfg.AuthorizationPlugins, pluginStore)
|
||||||
s.UseMiddleware(authzMiddleware)
|
s.UseMiddleware(authzMiddleware)
|
||||||
return authzMiddleware
|
return authzMiddleware, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cli *DaemonCli) getContainerdDaemonOpts() ([]supervisor.DaemonOpt, error) {
|
func (cli *DaemonCli) getContainerdDaemonOpts() ([]supervisor.DaemonOpt, error) {
|
||||||
|
|
|
@ -8,25 +8,6 @@ import (
|
||||||
"github.com/docker/docker/errdefs"
|
"github.com/docker/docker/errdefs"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ContainerCopy performs a deprecated operation of archiving the resource at
|
|
||||||
// the specified path in the container identified by the given name.
|
|
||||||
func (daemon *Daemon) ContainerCopy(name string, res string) (io.ReadCloser, error) {
|
|
||||||
ctr, err := daemon.GetContainer(name)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
data, err := daemon.containerCopy(ctr, res)
|
|
||||||
if err == nil {
|
|
||||||
return data, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if os.IsNotExist(err) {
|
|
||||||
return nil, containerFileNotFound{res, name}
|
|
||||||
}
|
|
||||||
return nil, errdefs.System(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ContainerStatPath stats the filesystem resource at the specified path in the
|
// ContainerStatPath stats the filesystem resource at the specified path in the
|
||||||
// container identified by the given name.
|
// container identified by the given name.
|
||||||
func (daemon *Daemon) ContainerStatPath(name string, path string) (stat *types.ContainerPathStat, err error) {
|
func (daemon *Daemon) ContainerStatPath(name string, path string) (stat *types.ContainerPathStat, err error) {
|
||||||
|
|
|
@ -161,55 +161,6 @@ func (daemon *Daemon) containerExtractToDir(container *container.Container, path
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (daemon *Daemon) containerCopy(container *container.Container, resource string) (rc io.ReadCloser, err error) {
|
|
||||||
container.Lock()
|
|
||||||
|
|
||||||
defer func() {
|
|
||||||
if err != nil {
|
|
||||||
// Wait to unlock the container until the archive is fully read
|
|
||||||
// (see the ReadCloseWrapper func below) or if there is an error
|
|
||||||
// before that occurs.
|
|
||||||
container.Unlock()
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
cfs, err := daemon.openContainerFS(container)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
defer func() {
|
|
||||||
if err != nil {
|
|
||||||
cfs.Close()
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
err = cfs.RunInFS(context.TODO(), func() error {
|
|
||||||
_, err := os.Stat(resource)
|
|
||||||
return err
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
tb, err := archive.NewTarballer(resource, &archive.TarOptions{
|
|
||||||
Compression: archive.Uncompressed,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
cfs.GoInFS(context.TODO(), tb.Do)
|
|
||||||
archv := tb.Reader()
|
|
||||||
reader := ioutils.NewReadCloserWrapper(archv, func() error {
|
|
||||||
err := archv.Close()
|
|
||||||
_ = cfs.Close()
|
|
||||||
container.Unlock()
|
|
||||||
return err
|
|
||||||
})
|
|
||||||
daemon.LogContainerEvent(container, events.ActionCopy)
|
|
||||||
return reader, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// checkIfPathIsInAVolume checks if the path is in a volume. If it is, it
|
// checkIfPathIsInAVolume checks if the path is in a volume. If it is, it
|
||||||
// cannot be in a read-only volume. If it is not in a volume, the container
|
// cannot be in a read-only volume. If it is not in a volume, the container
|
||||||
// cannot be configured with a read-only rootfs.
|
// cannot be configured with a read-only rootfs.
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -56,9 +56,9 @@ const (
|
||||||
DefaultPluginNamespace = "plugins.moby"
|
DefaultPluginNamespace = "plugins.moby"
|
||||||
// defaultMinAPIVersion is the minimum API version supported by the API.
|
// defaultMinAPIVersion is the minimum API version supported by the API.
|
||||||
// This version can be overridden through the "DOCKER_MIN_API_VERSION"
|
// This version can be overridden through the "DOCKER_MIN_API_VERSION"
|
||||||
// environment variable. The minimum allowed version is determined
|
// environment variable. It currently defaults to the minimum API version
|
||||||
// by [minAPIVersion].
|
// supported by the API server.
|
||||||
defaultMinAPIVersion = "1.24"
|
defaultMinAPIVersion = api.MinSupportedAPIVersion
|
||||||
// SeccompProfileDefault is the built-in default seccomp profile.
|
// SeccompProfileDefault is the built-in default seccomp profile.
|
||||||
SeccompProfileDefault = "builtin"
|
SeccompProfileDefault = "builtin"
|
||||||
// SeccompProfileUnconfined is a special profile name for seccomp to use an
|
// SeccompProfileUnconfined is a special profile name for seccomp to use an
|
||||||
|
@ -610,8 +610,8 @@ func ValidateMinAPIVersion(ver string) error {
|
||||||
if strings.EqualFold(ver[0:1], "v") {
|
if strings.EqualFold(ver[0:1], "v") {
|
||||||
return errors.New(`API version must be provided without "v" prefix`)
|
return errors.New(`API version must be provided without "v" prefix`)
|
||||||
}
|
}
|
||||||
if versions.LessThan(ver, minAPIVersion) {
|
if versions.LessThan(ver, defaultMinAPIVersion) {
|
||||||
return errors.Errorf(`minimum supported API version is %s: %s`, minAPIVersion, ver)
|
return errors.Errorf(`minimum supported API version is %s: %s`, defaultMinAPIVersion, ver)
|
||||||
}
|
}
|
||||||
if versions.GreaterThan(ver, api.DefaultVersion) {
|
if versions.GreaterThan(ver, api.DefaultVersion) {
|
||||||
return errors.Errorf(`maximum supported API version is %s: %s`, api.DefaultVersion, ver)
|
return errors.Errorf(`maximum supported API version is %s: %s`, api.DefaultVersion, ver)
|
||||||
|
|
|
@ -33,9 +33,6 @@ const (
|
||||||
// OCI runtime being shipped with the docker daemon package.
|
// OCI runtime being shipped with the docker daemon package.
|
||||||
StockRuntimeName = "runc"
|
StockRuntimeName = "runc"
|
||||||
|
|
||||||
// minAPIVersion represents Minimum REST API version supported
|
|
||||||
minAPIVersion = "1.12"
|
|
||||||
|
|
||||||
// userlandProxyBinary is the name of the userland-proxy binary.
|
// userlandProxyBinary is the name of the userland-proxy binary.
|
||||||
// In rootless-mode, [rootless.RootlessKitDockerProxyBinary] is used instead.
|
// In rootless-mode, [rootless.RootlessKitDockerProxyBinary] is used instead.
|
||||||
userlandProxyBinary = "docker-proxy"
|
userlandProxyBinary = "docker-proxy"
|
||||||
|
|
|
@ -13,13 +13,6 @@ const (
|
||||||
// default value. On Windows keep this empty so the value is auto-detected
|
// default value. On Windows keep this empty so the value is auto-detected
|
||||||
// based on other options.
|
// based on other options.
|
||||||
StockRuntimeName = ""
|
StockRuntimeName = ""
|
||||||
|
|
||||||
// minAPIVersion represents Minimum REST API version supported
|
|
||||||
// Technically the first daemon API version released on Windows is v1.25 in
|
|
||||||
// engine version 1.13. However, some clients are explicitly using downlevel
|
|
||||||
// APIs (e.g. docker-compose v2.1 file format) and that is just too restrictive.
|
|
||||||
// Hence also allowing 1.24 on Windows.
|
|
||||||
minAPIVersion string = "1.24"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// BridgeConfig is meant to store all the parameters for both the bridge driver and the default bridge network. On
|
// BridgeConfig is meant to store all the parameters for both the bridge driver and the default bridge network. On
|
||||||
|
|
|
@ -108,7 +108,7 @@ func (daemon *Daemon) containerCreate(ctx context.Context, daemonCfg *configStor
|
||||||
if opts.params.HostConfig == nil {
|
if opts.params.HostConfig == nil {
|
||||||
opts.params.HostConfig = &containertypes.HostConfig{}
|
opts.params.HostConfig = &containertypes.HostConfig{}
|
||||||
}
|
}
|
||||||
err = daemon.adaptContainerSettings(&daemonCfg.Config, opts.params.HostConfig, opts.params.AdjustCPUShares)
|
err = daemon.adaptContainerSettings(&daemonCfg.Config, opts.params.HostConfig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return containertypes.CreateResponse{Warnings: warnings}, errdefs.InvalidParameter(err)
|
return containertypes.CreateResponse{Warnings: warnings}, errdefs.InvalidParameter(err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -54,9 +54,16 @@ import (
|
||||||
const (
|
const (
|
||||||
isWindows = false
|
isWindows = false
|
||||||
|
|
||||||
|
// These values were used to adjust the CPU-shares for older API versions,
|
||||||
|
// but were not used for validation.
|
||||||
|
//
|
||||||
|
// TODO(thaJeztah): validate min/max values for CPU-shares, similar to Windows: https://github.com/moby/moby/issues/47340
|
||||||
|
// https://github.com/moby/moby/blob/27e85c7b6885c2d21ae90791136d9aba78b83d01/daemon/daemon_windows.go#L97-L99
|
||||||
|
//
|
||||||
// See https://git.kernel.org/cgit/linux/kernel/git/tip/tip.git/tree/kernel/sched/sched.h?id=8cd9234c64c584432f6992fe944ca9e46ca8ea76#n269
|
// See https://git.kernel.org/cgit/linux/kernel/git/tip/tip.git/tree/kernel/sched/sched.h?id=8cd9234c64c584432f6992fe944ca9e46ca8ea76#n269
|
||||||
linuxMinCPUShares = 2
|
// linuxMinCPUShares = 2
|
||||||
linuxMaxCPUShares = 262144
|
// linuxMaxCPUShares = 262144
|
||||||
|
|
||||||
// It's not kernel limit, we want this 6M limit to account for overhead during startup, and to supply a reasonable functional container
|
// It's not kernel limit, we want this 6M limit to account for overhead during startup, and to supply a reasonable functional container
|
||||||
linuxMinMemory = 6291456
|
linuxMinMemory = 6291456
|
||||||
// constants for remapped root settings
|
// constants for remapped root settings
|
||||||
|
@ -306,17 +313,7 @@ func adjustParallelLimit(n int, limit int) int {
|
||||||
|
|
||||||
// adaptContainerSettings is called during container creation to modify any
|
// adaptContainerSettings is called during container creation to modify any
|
||||||
// settings necessary in the HostConfig structure.
|
// settings necessary in the HostConfig structure.
|
||||||
func (daemon *Daemon) adaptContainerSettings(daemonCfg *config.Config, hostConfig *containertypes.HostConfig, adjustCPUShares bool) error {
|
func (daemon *Daemon) adaptContainerSettings(daemonCfg *config.Config, hostConfig *containertypes.HostConfig) error {
|
||||||
if adjustCPUShares && hostConfig.CPUShares > 0 {
|
|
||||||
// Handle unsupported CPUShares
|
|
||||||
if hostConfig.CPUShares < linuxMinCPUShares {
|
|
||||||
log.G(context.TODO()).Warnf("Changing requested CPUShares of %d to minimum allowed of %d", hostConfig.CPUShares, linuxMinCPUShares)
|
|
||||||
hostConfig.CPUShares = linuxMinCPUShares
|
|
||||||
} else if hostConfig.CPUShares > linuxMaxCPUShares {
|
|
||||||
log.G(context.TODO()).Warnf("Changing requested CPUShares of %d to maximum allowed of %d", hostConfig.CPUShares, linuxMaxCPUShares)
|
|
||||||
hostConfig.CPUShares = linuxMaxCPUShares
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if hostConfig.Memory > 0 && hostConfig.MemorySwap == 0 {
|
if hostConfig.Memory > 0 && hostConfig.MemorySwap == 0 {
|
||||||
// By default, MemorySwap is set to twice the size of Memory.
|
// By default, MemorySwap is set to twice the size of Memory.
|
||||||
hostConfig.MemorySwap = hostConfig.Memory * 2
|
hostConfig.MemorySwap = hostConfig.Memory * 2
|
||||||
|
|
|
@ -57,87 +57,6 @@ func TestAdjustSharedNamespaceContainerName(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Unix test as uses settings which are not available on Windows
|
|
||||||
func TestAdjustCPUShares(t *testing.T) {
|
|
||||||
tmp, err := os.MkdirTemp("", "docker-daemon-unix-test-")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
defer os.RemoveAll(tmp)
|
|
||||||
daemon := &Daemon{
|
|
||||||
repository: tmp,
|
|
||||||
root: tmp,
|
|
||||||
}
|
|
||||||
cfg := &config.Config{}
|
|
||||||
muteLogs(t)
|
|
||||||
|
|
||||||
hostConfig := &containertypes.HostConfig{
|
|
||||||
Resources: containertypes.Resources{CPUShares: linuxMinCPUShares - 1},
|
|
||||||
}
|
|
||||||
daemon.adaptContainerSettings(cfg, hostConfig, true)
|
|
||||||
if hostConfig.CPUShares != linuxMinCPUShares {
|
|
||||||
t.Errorf("Expected CPUShares to be %d", linuxMinCPUShares)
|
|
||||||
}
|
|
||||||
|
|
||||||
hostConfig.CPUShares = linuxMaxCPUShares + 1
|
|
||||||
daemon.adaptContainerSettings(cfg, hostConfig, true)
|
|
||||||
if hostConfig.CPUShares != linuxMaxCPUShares {
|
|
||||||
t.Errorf("Expected CPUShares to be %d", linuxMaxCPUShares)
|
|
||||||
}
|
|
||||||
|
|
||||||
hostConfig.CPUShares = 0
|
|
||||||
daemon.adaptContainerSettings(cfg, hostConfig, true)
|
|
||||||
if hostConfig.CPUShares != 0 {
|
|
||||||
t.Error("Expected CPUShares to be unchanged")
|
|
||||||
}
|
|
||||||
|
|
||||||
hostConfig.CPUShares = 1024
|
|
||||||
daemon.adaptContainerSettings(cfg, hostConfig, true)
|
|
||||||
if hostConfig.CPUShares != 1024 {
|
|
||||||
t.Error("Expected CPUShares to be unchanged")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Unix test as uses settings which are not available on Windows
|
|
||||||
func TestAdjustCPUSharesNoAdjustment(t *testing.T) {
|
|
||||||
tmp, err := os.MkdirTemp("", "docker-daemon-unix-test-")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
defer os.RemoveAll(tmp)
|
|
||||||
daemon := &Daemon{
|
|
||||||
repository: tmp,
|
|
||||||
root: tmp,
|
|
||||||
}
|
|
||||||
cfg := &config.Config{}
|
|
||||||
|
|
||||||
hostConfig := &containertypes.HostConfig{
|
|
||||||
Resources: containertypes.Resources{CPUShares: linuxMinCPUShares - 1},
|
|
||||||
}
|
|
||||||
daemon.adaptContainerSettings(cfg, hostConfig, false)
|
|
||||||
if hostConfig.CPUShares != linuxMinCPUShares-1 {
|
|
||||||
t.Errorf("Expected CPUShares to be %d", linuxMinCPUShares-1)
|
|
||||||
}
|
|
||||||
|
|
||||||
hostConfig.CPUShares = linuxMaxCPUShares + 1
|
|
||||||
daemon.adaptContainerSettings(cfg, hostConfig, false)
|
|
||||||
if hostConfig.CPUShares != linuxMaxCPUShares+1 {
|
|
||||||
t.Errorf("Expected CPUShares to be %d", linuxMaxCPUShares+1)
|
|
||||||
}
|
|
||||||
|
|
||||||
hostConfig.CPUShares = 0
|
|
||||||
daemon.adaptContainerSettings(cfg, hostConfig, false)
|
|
||||||
if hostConfig.CPUShares != 0 {
|
|
||||||
t.Error("Expected CPUShares to be unchanged")
|
|
||||||
}
|
|
||||||
|
|
||||||
hostConfig.CPUShares = 1024
|
|
||||||
daemon.adaptContainerSettings(cfg, hostConfig, false)
|
|
||||||
if hostConfig.CPUShares != 1024 {
|
|
||||||
t.Error("Expected CPUShares to be unchanged")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Unix test as uses settings which are not available on Windows
|
// Unix test as uses settings which are not available on Windows
|
||||||
func TestParseSecurityOptWithDeprecatedColon(t *testing.T) {
|
func TestParseSecurityOptWithDeprecatedColon(t *testing.T) {
|
||||||
opts := &container.SecurityOptions{}
|
opts := &container.SecurityOptions{}
|
||||||
|
|
|
@ -66,7 +66,7 @@ func setupInitLayer(idMapping idtools.IdentityMapping) func(string) error {
|
||||||
|
|
||||||
// adaptContainerSettings is called during container creation to modify any
|
// adaptContainerSettings is called during container creation to modify any
|
||||||
// settings necessary in the HostConfig structure.
|
// settings necessary in the HostConfig structure.
|
||||||
func (daemon *Daemon) adaptContainerSettings(daemonCfg *config.Config, hostConfig *containertypes.HostConfig, adjustCPUShares bool) error {
|
func (daemon *Daemon) adaptContainerSettings(daemonCfg *config.Config, hostConfig *containertypes.HostConfig) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -14,7 +14,6 @@ import (
|
||||||
containertypes "github.com/docker/docker/api/types/container"
|
containertypes "github.com/docker/docker/api/types/container"
|
||||||
networktypes "github.com/docker/docker/api/types/network"
|
networktypes "github.com/docker/docker/api/types/network"
|
||||||
"github.com/docker/docker/api/types/versions"
|
"github.com/docker/docker/api/types/versions"
|
||||||
"github.com/docker/docker/api/types/versions/v1p20"
|
|
||||||
"github.com/docker/docker/container"
|
"github.com/docker/docker/container"
|
||||||
"github.com/docker/docker/daemon/config"
|
"github.com/docker/docker/daemon/config"
|
||||||
"github.com/docker/docker/daemon/network"
|
"github.com/docker/docker/daemon/network"
|
||||||
|
@ -29,10 +28,6 @@ import (
|
||||||
// there is an error getting the data.
|
// there is an error getting the data.
|
||||||
func (daemon *Daemon) ContainerInspect(ctx context.Context, name string, size bool, version string) (interface{}, error) {
|
func (daemon *Daemon) ContainerInspect(ctx context.Context, name string, size bool, version string) (interface{}, error) {
|
||||||
switch {
|
switch {
|
||||||
case versions.LessThan(version, "1.20"):
|
|
||||||
return daemon.containerInspectPre120(ctx, name)
|
|
||||||
case versions.Equal(version, "1.20"):
|
|
||||||
return daemon.containerInspect120(name)
|
|
||||||
case versions.LessThan(version, "1.45"):
|
case versions.LessThan(version, "1.45"):
|
||||||
ctr, err := daemon.ContainerInspectCurrent(ctx, name, size)
|
ctr, err := daemon.ContainerInspectCurrent(ctx, name, size)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -117,35 +112,6 @@ func (daemon *Daemon) ContainerInspectCurrent(ctx context.Context, name string,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// containerInspect120 serializes the master version of a container into a json type.
|
|
||||||
func (daemon *Daemon) containerInspect120(name string) (*v1p20.ContainerJSON, error) {
|
|
||||||
ctr, err := daemon.GetContainer(name)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
ctr.Lock()
|
|
||||||
defer ctr.Unlock()
|
|
||||||
|
|
||||||
base, err := daemon.getInspectData(&daemon.config().Config, ctr)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return &v1p20.ContainerJSON{
|
|
||||||
ContainerJSONBase: base,
|
|
||||||
Mounts: ctr.GetMountPoints(),
|
|
||||||
Config: &v1p20.ContainerConfig{
|
|
||||||
Config: ctr.Config,
|
|
||||||
MacAddress: ctr.Config.MacAddress, //nolint:staticcheck // ignore SA1019: field is deprecated, but still used on API < v1.44.
|
|
||||||
NetworkDisabled: ctr.Config.NetworkDisabled,
|
|
||||||
ExposedPorts: ctr.Config.ExposedPorts,
|
|
||||||
VolumeDriver: ctr.HostConfig.VolumeDriver,
|
|
||||||
},
|
|
||||||
NetworkSettings: daemon.getBackwardsCompatibleNetworkSettings(ctr.NetworkSettings),
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (daemon *Daemon) getInspectData(daemonCfg *config.Config, container *container.Container) (*types.ContainerJSONBase, error) {
|
func (daemon *Daemon) getInspectData(daemonCfg *config.Config, container *container.Container) (*types.ContainerJSONBase, error) {
|
||||||
// make a copy to play with
|
// make a copy to play with
|
||||||
hostConfig := *container.HostConfig
|
hostConfig := *container.HostConfig
|
||||||
|
@ -283,25 +249,6 @@ func (daemon *Daemon) ContainerExecInspect(id string) (*backend.ExecInspect, err
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (daemon *Daemon) getBackwardsCompatibleNetworkSettings(settings *network.Settings) *v1p20.NetworkSettings {
|
|
||||||
result := &v1p20.NetworkSettings{
|
|
||||||
NetworkSettingsBase: types.NetworkSettingsBase{
|
|
||||||
Bridge: settings.Bridge,
|
|
||||||
SandboxID: settings.SandboxID,
|
|
||||||
SandboxKey: settings.SandboxKey,
|
|
||||||
HairpinMode: settings.HairpinMode,
|
|
||||||
LinkLocalIPv6Address: settings.LinkLocalIPv6Address,
|
|
||||||
LinkLocalIPv6PrefixLen: settings.LinkLocalIPv6PrefixLen,
|
|
||||||
Ports: settings.Ports,
|
|
||||||
SecondaryIPAddresses: settings.SecondaryIPAddresses,
|
|
||||||
SecondaryIPv6Addresses: settings.SecondaryIPv6Addresses,
|
|
||||||
},
|
|
||||||
DefaultNetworkSettings: daemon.getDefaultNetworkSettings(settings.Networks),
|
|
||||||
}
|
|
||||||
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
// getDefaultNetworkSettings creates the deprecated structure that holds the information
|
// getDefaultNetworkSettings creates the deprecated structure that holds the information
|
||||||
// about the bridge network for a container.
|
// about the bridge network for a container.
|
||||||
func (daemon *Daemon) getDefaultNetworkSettings(networks map[string]*network.EndpointSettings) types.DefaultNetworkSettings {
|
func (daemon *Daemon) getDefaultNetworkSettings(networks map[string]*network.EndpointSettings) types.DefaultNetworkSettings {
|
||||||
|
|
|
@ -1,11 +1,8 @@
|
||||||
package daemon // import "github.com/docker/docker/daemon"
|
package daemon // import "github.com/docker/docker/daemon"
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
|
|
||||||
"github.com/docker/docker/api/types"
|
"github.com/docker/docker/api/types"
|
||||||
"github.com/docker/docker/api/types/backend"
|
"github.com/docker/docker/api/types/backend"
|
||||||
"github.com/docker/docker/api/types/versions/v1p19"
|
|
||||||
"github.com/docker/docker/container"
|
"github.com/docker/docker/container"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -19,47 +16,6 @@ func setPlatformSpecificContainerFields(container *container.Container, contJSON
|
||||||
return contJSONBase
|
return contJSONBase
|
||||||
}
|
}
|
||||||
|
|
||||||
// containerInspectPre120 gets containers for pre 1.20 APIs.
|
|
||||||
func (daemon *Daemon) containerInspectPre120(ctx context.Context, name string) (*v1p19.ContainerJSON, error) {
|
|
||||||
ctr, err := daemon.GetContainer(name)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
ctr.Lock()
|
|
||||||
defer ctr.Unlock()
|
|
||||||
|
|
||||||
base, err := daemon.getInspectData(&daemon.config().Config, ctr)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
volumes := make(map[string]string)
|
|
||||||
volumesRW := make(map[string]bool)
|
|
||||||
for _, m := range ctr.MountPoints {
|
|
||||||
volumes[m.Destination] = m.Path()
|
|
||||||
volumesRW[m.Destination] = m.RW
|
|
||||||
}
|
|
||||||
|
|
||||||
return &v1p19.ContainerJSON{
|
|
||||||
ContainerJSONBase: base,
|
|
||||||
Volumes: volumes,
|
|
||||||
VolumesRW: volumesRW,
|
|
||||||
Config: &v1p19.ContainerConfig{
|
|
||||||
Config: ctr.Config,
|
|
||||||
MacAddress: ctr.Config.MacAddress, //nolint:staticcheck // ignore SA1019: field is deprecated, but still used on API < v1.44.
|
|
||||||
NetworkDisabled: ctr.Config.NetworkDisabled,
|
|
||||||
ExposedPorts: ctr.Config.ExposedPorts,
|
|
||||||
VolumeDriver: ctr.HostConfig.VolumeDriver,
|
|
||||||
Memory: ctr.HostConfig.Memory,
|
|
||||||
MemorySwap: ctr.HostConfig.MemorySwap,
|
|
||||||
CPUShares: ctr.HostConfig.CPUShares,
|
|
||||||
CPUSet: ctr.HostConfig.CpusetCpus,
|
|
||||||
},
|
|
||||||
NetworkSettings: daemon.getBackwardsCompatibleNetworkSettings(ctr.NetworkSettings),
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func inspectExecProcessConfig(e *container.ExecConfig) *backend.ExecProcessConfig {
|
func inspectExecProcessConfig(e *container.ExecConfig) *backend.ExecProcessConfig {
|
||||||
return &backend.ExecProcessConfig{
|
return &backend.ExecProcessConfig{
|
||||||
Tty: e.Tty,
|
Tty: e.Tty,
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
package daemon // import "github.com/docker/docker/daemon"
|
package daemon // import "github.com/docker/docker/daemon"
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
|
|
||||||
"github.com/docker/docker/api/types"
|
"github.com/docker/docker/api/types"
|
||||||
"github.com/docker/docker/api/types/backend"
|
"github.com/docker/docker/api/types/backend"
|
||||||
"github.com/docker/docker/container"
|
"github.com/docker/docker/container"
|
||||||
|
@ -13,11 +11,6 @@ func setPlatformSpecificContainerFields(container *container.Container, contJSON
|
||||||
return contJSONBase
|
return contJSONBase
|
||||||
}
|
}
|
||||||
|
|
||||||
// containerInspectPre120 get containers for pre 1.20 APIs.
|
|
||||||
func (daemon *Daemon) containerInspectPre120(ctx context.Context, name string) (*types.ContainerJSON, error) {
|
|
||||||
return daemon.ContainerInspectCurrent(ctx, name, false)
|
|
||||||
}
|
|
||||||
|
|
||||||
func inspectExecProcessConfig(e *container.ExecConfig) *backend.ExecProcessConfig {
|
func inspectExecProcessConfig(e *container.ExecConfig) *backend.ExecProcessConfig {
|
||||||
return &backend.ExecProcessConfig{
|
return &backend.ExecProcessConfig{
|
||||||
Tty: e.Tty,
|
Tty: e.Tty,
|
||||||
|
|
|
@ -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, false); err != nil {
|
|
||||||
return errdefs.InvalidParameter(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return daemon.containerStart(ctx, daemonCfg, ctr, checkpoint, checkpointDir, true)
|
return daemon.containerStart(ctx, daemonCfg, ctr, checkpoint, checkpointDir, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -10,8 +10,6 @@ import (
|
||||||
"github.com/containerd/log"
|
"github.com/containerd/log"
|
||||||
"github.com/docker/docker/api/types"
|
"github.com/docker/docker/api/types"
|
||||||
"github.com/docker/docker/api/types/backend"
|
"github.com/docker/docker/api/types/backend"
|
||||||
"github.com/docker/docker/api/types/versions"
|
|
||||||
"github.com/docker/docker/api/types/versions/v1p20"
|
|
||||||
"github.com/docker/docker/container"
|
"github.com/docker/docker/container"
|
||||||
"github.com/docker/docker/errdefs"
|
"github.com/docker/docker/errdefs"
|
||||||
"github.com/docker/docker/pkg/ioutils"
|
"github.com/docker/docker/pkg/ioutils"
|
||||||
|
@ -20,13 +18,6 @@ import (
|
||||||
// ContainerStats writes information about the container to the stream
|
// ContainerStats writes information about the container to the stream
|
||||||
// given in the config object.
|
// given in the config object.
|
||||||
func (daemon *Daemon) ContainerStats(ctx context.Context, prefixOrName string, config *backend.ContainerStatsConfig) error {
|
func (daemon *Daemon) ContainerStats(ctx context.Context, prefixOrName string, config *backend.ContainerStatsConfig) error {
|
||||||
// Engine API version (used for backwards compatibility)
|
|
||||||
apiVersion := config.Version
|
|
||||||
|
|
||||||
if isWindows && versions.LessThan(apiVersion, "1.21") {
|
|
||||||
return errors.New("API versions pre v1.21 do not support stats on Windows")
|
|
||||||
}
|
|
||||||
|
|
||||||
ctr, err := daemon.GetContainer(prefixOrName)
|
ctr, err := daemon.GetContainer(prefixOrName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -87,46 +78,7 @@ func (daemon *Daemon) ContainerStats(ctx context.Context, prefixOrName string, c
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
var statsJSON interface{}
|
statsJSON := getStatJSON(v)
|
||||||
statsJSONPost120 := getStatJSON(v)
|
|
||||||
if versions.LessThan(apiVersion, "1.21") {
|
|
||||||
var (
|
|
||||||
rxBytes uint64
|
|
||||||
rxPackets uint64
|
|
||||||
rxErrors uint64
|
|
||||||
rxDropped uint64
|
|
||||||
txBytes uint64
|
|
||||||
txPackets uint64
|
|
||||||
txErrors uint64
|
|
||||||
txDropped uint64
|
|
||||||
)
|
|
||||||
for _, v := range statsJSONPost120.Networks {
|
|
||||||
rxBytes += v.RxBytes
|
|
||||||
rxPackets += v.RxPackets
|
|
||||||
rxErrors += v.RxErrors
|
|
||||||
rxDropped += v.RxDropped
|
|
||||||
txBytes += v.TxBytes
|
|
||||||
txPackets += v.TxPackets
|
|
||||||
txErrors += v.TxErrors
|
|
||||||
txDropped += v.TxDropped
|
|
||||||
}
|
|
||||||
statsJSON = &v1p20.StatsJSON{
|
|
||||||
Stats: statsJSONPost120.Stats,
|
|
||||||
Network: types.NetworkStats{
|
|
||||||
RxBytes: rxBytes,
|
|
||||||
RxPackets: rxPackets,
|
|
||||||
RxErrors: rxErrors,
|
|
||||||
RxDropped: rxDropped,
|
|
||||||
TxBytes: txBytes,
|
|
||||||
TxPackets: txPackets,
|
|
||||||
TxErrors: txErrors,
|
|
||||||
TxDropped: txDropped,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
statsJSON = statsJSONPost120
|
|
||||||
}
|
|
||||||
|
|
||||||
if !config.Stream && noStreamFirstFrame {
|
if !config.Stream && noStreamFirstFrame {
|
||||||
// prime the cpu stats so they aren't 0 in the final output
|
// prime the cpu stats so they aren't 0 in the final output
|
||||||
noStreamFirstFrame = false
|
noStreamFirstFrame = false
|
||||||
|
|
|
@ -46,9 +46,6 @@ export DOCKER_ALLOW_SCHEMA1_PUSH_DONOTUSE=1
|
||||||
export DOCKER_GRAPHDRIVER=${DOCKER_GRAPHDRIVER:-vfs}
|
export DOCKER_GRAPHDRIVER=${DOCKER_GRAPHDRIVER:-vfs}
|
||||||
export DOCKER_USERLANDPROXY=${DOCKER_USERLANDPROXY:-true}
|
export DOCKER_USERLANDPROXY=${DOCKER_USERLANDPROXY:-true}
|
||||||
|
|
||||||
# Allow testing old API versions
|
|
||||||
export DOCKER_MIN_API_VERSION=1.12
|
|
||||||
|
|
||||||
# example usage: DOCKER_STORAGE_OPTS="dm.basesize=20G,dm.loopdatasize=200G"
|
# example usage: DOCKER_STORAGE_OPTS="dm.basesize=20G,dm.loopdatasize=200G"
|
||||||
storage_params=""
|
storage_params=""
|
||||||
if [ -n "$DOCKER_STORAGE_OPTS" ]; then
|
if [ -n "$DOCKER_STORAGE_OPTS" ]; then
|
||||||
|
|
|
@ -958,81 +958,6 @@ func (s *DockerAPISuite) TestContainerAPICopyNotExistsAnyMore(c *testing.T) {
|
||||||
assert.Equal(c, res.StatusCode, http.StatusNotFound)
|
assert.Equal(c, res.StatusCode, http.StatusNotFound)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *DockerAPISuite) TestContainerAPICopyPre124(c *testing.T) {
|
|
||||||
testRequires(c, DaemonIsLinux) // Windows only supports 1.25 or later
|
|
||||||
const name = "test-container-api-copy"
|
|
||||||
cli.DockerCmd(c, "run", "--name", name, "busybox", "touch", "/test.txt")
|
|
||||||
|
|
||||||
postData := types.CopyConfig{
|
|
||||||
Resource: "/test.txt",
|
|
||||||
}
|
|
||||||
|
|
||||||
res, body, err := request.Post(testutil.GetContext(c), "/v1.23/containers/"+name+"/copy", request.JSONBody(postData))
|
|
||||||
assert.NilError(c, err)
|
|
||||||
assert.Equal(c, res.StatusCode, http.StatusOK)
|
|
||||||
|
|
||||||
found := false
|
|
||||||
for tarReader := tar.NewReader(body); ; {
|
|
||||||
h, err := tarReader.Next()
|
|
||||||
if err != nil {
|
|
||||||
if err == io.EOF {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
c.Fatal(err)
|
|
||||||
}
|
|
||||||
if h.Name == "test.txt" {
|
|
||||||
found = true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
assert.Assert(c, found)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *DockerAPISuite) TestContainerAPICopyResourcePathEmptyPre124(c *testing.T) {
|
|
||||||
testRequires(c, DaemonIsLinux) // Windows only supports 1.25 or later
|
|
||||||
const name = "test-container-api-copy-resource-empty"
|
|
||||||
cli.DockerCmd(c, "run", "--name", name, "busybox", "touch", "/test.txt")
|
|
||||||
|
|
||||||
postData := types.CopyConfig{
|
|
||||||
Resource: "",
|
|
||||||
}
|
|
||||||
|
|
||||||
res, body, err := request.Post(testutil.GetContext(c), "/v1.23/containers/"+name+"/copy", request.JSONBody(postData))
|
|
||||||
assert.NilError(c, err)
|
|
||||||
assert.Equal(c, res.StatusCode, http.StatusBadRequest)
|
|
||||||
b, err := request.ReadBody(body)
|
|
||||||
assert.NilError(c, err)
|
|
||||||
assert.Assert(c, is.Regexp("^Path cannot be empty\n$", string(b)))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *DockerAPISuite) TestContainerAPICopyResourcePathNotFoundPre124(c *testing.T) {
|
|
||||||
testRequires(c, DaemonIsLinux) // Windows only supports 1.25 or later
|
|
||||||
const name = "test-container-api-copy-resource-not-found"
|
|
||||||
cli.DockerCmd(c, "run", "--name", name, "busybox")
|
|
||||||
|
|
||||||
postData := types.CopyConfig{
|
|
||||||
Resource: "/notexist",
|
|
||||||
}
|
|
||||||
|
|
||||||
res, body, err := request.Post(testutil.GetContext(c), "/v1.23/containers/"+name+"/copy", request.JSONBody(postData))
|
|
||||||
assert.NilError(c, err)
|
|
||||||
assert.Equal(c, res.StatusCode, http.StatusNotFound)
|
|
||||||
b, err := request.ReadBody(body)
|
|
||||||
assert.NilError(c, err)
|
|
||||||
assert.Assert(c, is.Regexp("^Could not find the file /notexist in container "+name+"\n$", string(b)))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *DockerAPISuite) TestContainerAPICopyContainerNotFoundPr124(c *testing.T) {
|
|
||||||
testRequires(c, DaemonIsLinux) // Windows only supports 1.25 or later
|
|
||||||
postData := types.CopyConfig{
|
|
||||||
Resource: "/something",
|
|
||||||
}
|
|
||||||
|
|
||||||
res, _, err := request.Post(testutil.GetContext(c), "/v1.23/containers/notexists/copy", request.JSONBody(postData))
|
|
||||||
assert.NilError(c, err)
|
|
||||||
assert.Equal(c, res.StatusCode, http.StatusNotFound)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *DockerAPISuite) TestContainerAPIDelete(c *testing.T) {
|
func (s *DockerAPISuite) TestContainerAPIDelete(c *testing.T) {
|
||||||
id := runSleepingContainer(c)
|
id := runSleepingContainer(c)
|
||||||
cli.WaitRun(c, id)
|
cli.WaitRun(c, id)
|
||||||
|
@ -1251,20 +1176,6 @@ func (s *DockerAPISuite) TestPostContainersCreateWithStringOrSliceCapAddDrop(c *
|
||||||
assert.NilError(c, err)
|
assert.NilError(c, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// #14915
|
|
||||||
func (s *DockerAPISuite) TestContainerAPICreateNoHostConfig118(c *testing.T) {
|
|
||||||
testRequires(c, DaemonIsLinux) // Windows only support 1.25 or later
|
|
||||||
config := container.Config{
|
|
||||||
Image: "busybox",
|
|
||||||
}
|
|
||||||
|
|
||||||
apiClient, err := client.NewClientWithOpts(client.FromEnv, client.WithVersion("v1.18"))
|
|
||||||
assert.NilError(c, err)
|
|
||||||
|
|
||||||
_, err = apiClient.ContainerCreate(testutil.GetContext(c), &config, &container.HostConfig{}, &network.NetworkingConfig{}, nil, "")
|
|
||||||
assert.NilError(c, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ensure an error occurs when you have a container read-only rootfs but you
|
// Ensure an error occurs when you have a container read-only rootfs but you
|
||||||
// extract an archive to a symlink in a writable volume which points to a
|
// extract an archive to a symlink in a writable volume which points to a
|
||||||
// directory outside of the volume.
|
// directory outside of the volume.
|
||||||
|
|
|
@ -109,20 +109,6 @@ func (s *DockerAPISuite) TestExecAPIStartEnsureHeaders(c *testing.T) {
|
||||||
assert.Assert(c, resp.Header.Get("Server") != "")
|
assert.Assert(c, resp.Header.Get("Server") != "")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *DockerAPISuite) TestExecAPIStartBackwardsCompatible(c *testing.T) {
|
|
||||||
testRequires(c, DaemonIsLinux) // Windows only supports 1.25 or later
|
|
||||||
runSleepingContainer(c, "-d", "--name", "test")
|
|
||||||
id := createExec(c, "test")
|
|
||||||
|
|
||||||
resp, body, err := request.Post(testutil.GetContext(c), fmt.Sprintf("/v1.20/exec/%s/start", id), request.RawString(`{"Detach": true}`), request.ContentType("text/plain"))
|
|
||||||
assert.NilError(c, err)
|
|
||||||
|
|
||||||
b, err := request.ReadBody(body)
|
|
||||||
comment := fmt.Sprintf("response body: %s", b)
|
|
||||||
assert.NilError(c, err, comment)
|
|
||||||
assert.Equal(c, resp.StatusCode, http.StatusOK, comment)
|
|
||||||
}
|
|
||||||
|
|
||||||
// #19362
|
// #19362
|
||||||
func (s *DockerAPISuite) TestExecAPIStartMultipleTimesError(c *testing.T) {
|
func (s *DockerAPISuite) TestExecAPIStartMultipleTimesError(c *testing.T) {
|
||||||
runSleepingContainer(c, "-d", "--name", "test")
|
runSleepingContainer(c, "-d", "--name", "test")
|
||||||
|
|
|
@ -6,7 +6,6 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/docker/docker/api/types"
|
"github.com/docker/docker/api/types"
|
||||||
"github.com/docker/docker/api/types/versions/v1p20"
|
|
||||||
"github.com/docker/docker/client"
|
"github.com/docker/docker/client"
|
||||||
"github.com/docker/docker/integration-cli/cli"
|
"github.com/docker/docker/integration-cli/cli"
|
||||||
"github.com/docker/docker/testutil"
|
"github.com/docker/docker/testutil"
|
||||||
|
@ -21,26 +20,15 @@ func (s *DockerAPISuite) TestInspectAPIContainerResponse(c *testing.T) {
|
||||||
keysBase := []string{
|
keysBase := []string{
|
||||||
"Id", "State", "Created", "Path", "Args", "Config", "Image", "NetworkSettings",
|
"Id", "State", "Created", "Path", "Args", "Config", "Image", "NetworkSettings",
|
||||||
"ResolvConfPath", "HostnamePath", "HostsPath", "LogPath", "Name", "Driver", "MountLabel", "ProcessLabel", "GraphDriver",
|
"ResolvConfPath", "HostnamePath", "HostsPath", "LogPath", "Name", "Driver", "MountLabel", "ProcessLabel", "GraphDriver",
|
||||||
|
"Mounts",
|
||||||
}
|
}
|
||||||
|
|
||||||
type acase struct {
|
cases := []struct {
|
||||||
version string
|
version string
|
||||||
keys []string
|
keys []string
|
||||||
|
}{
|
||||||
|
{version: "v1.24", keys: keysBase},
|
||||||
}
|
}
|
||||||
|
|
||||||
var cases []acase
|
|
||||||
|
|
||||||
if testEnv.DaemonInfo.OSType == "windows" {
|
|
||||||
cases = []acase{
|
|
||||||
{"v1.25", append(keysBase, "Mounts")},
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
cases = []acase{
|
|
||||||
{"v1.20", append(keysBase, "Mounts")},
|
|
||||||
{"v1.19", append(keysBase, "Volumes", "VolumesRW")},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, cs := range cases {
|
for _, cs := range cases {
|
||||||
body := getInspectBody(c, cs.version, cleanedContainerID)
|
body := getInspectBody(c, cs.version, cleanedContainerID)
|
||||||
|
|
||||||
|
@ -59,28 +47,6 @@ func (s *DockerAPISuite) TestInspectAPIContainerResponse(c *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *DockerAPISuite) TestInspectAPIContainerVolumeDriverLegacy(c *testing.T) {
|
|
||||||
// No legacy implications for Windows
|
|
||||||
testRequires(c, DaemonIsLinux)
|
|
||||||
out := cli.DockerCmd(c, "run", "-d", "busybox", "true").Stdout()
|
|
||||||
cleanedContainerID := strings.TrimSpace(out)
|
|
||||||
|
|
||||||
cases := []string{"v1.19", "v1.20"}
|
|
||||||
for _, version := range cases {
|
|
||||||
body := getInspectBody(c, version, cleanedContainerID)
|
|
||||||
|
|
||||||
var inspectJSON map[string]interface{}
|
|
||||||
err := json.Unmarshal(body, &inspectJSON)
|
|
||||||
assert.NilError(c, err, "Unable to unmarshal body for version %s", version)
|
|
||||||
|
|
||||||
config, ok := inspectJSON["Config"]
|
|
||||||
assert.Assert(c, ok, "Unable to find 'Config'")
|
|
||||||
cfg := config.(map[string]interface{})
|
|
||||||
_, ok = cfg["VolumeDriver"]
|
|
||||||
assert.Assert(c, ok, "API version %s expected to include VolumeDriver in 'Config'", version)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *DockerAPISuite) TestInspectAPIContainerVolumeDriver(c *testing.T) {
|
func (s *DockerAPISuite) TestInspectAPIContainerVolumeDriver(c *testing.T) {
|
||||||
out := cli.DockerCmd(c, "run", "-d", "--volume-driver", "local", "busybox", "true").Stdout()
|
out := cli.DockerCmd(c, "run", "-d", "--volume-driver", "local", "busybox", "true").Stdout()
|
||||||
cleanedContainerID := strings.TrimSpace(out)
|
cleanedContainerID := strings.TrimSpace(out)
|
||||||
|
@ -118,47 +84,6 @@ func (s *DockerAPISuite) TestInspectAPIImageResponse(c *testing.T) {
|
||||||
assert.Check(c, is.Contains(imageJSON.RepoTags, "busybox:mytag"))
|
assert.Check(c, is.Contains(imageJSON.RepoTags, "busybox:mytag"))
|
||||||
}
|
}
|
||||||
|
|
||||||
// #17131, #17139, #17173
|
|
||||||
func (s *DockerAPISuite) TestInspectAPIEmptyFieldsInConfigPre121(c *testing.T) {
|
|
||||||
// Not relevant on Windows
|
|
||||||
testRequires(c, DaemonIsLinux)
|
|
||||||
out := cli.DockerCmd(c, "run", "-d", "busybox", "true").Stdout()
|
|
||||||
cleanedContainerID := strings.TrimSpace(out)
|
|
||||||
|
|
||||||
cases := []string{"v1.19", "v1.20"}
|
|
||||||
for _, version := range cases {
|
|
||||||
body := getInspectBody(c, version, cleanedContainerID)
|
|
||||||
|
|
||||||
var inspectJSON map[string]interface{}
|
|
||||||
err := json.Unmarshal(body, &inspectJSON)
|
|
||||||
assert.NilError(c, err, "Unable to unmarshal body for version %s", version)
|
|
||||||
config, ok := inspectJSON["Config"]
|
|
||||||
assert.Assert(c, ok, "Unable to find 'Config'")
|
|
||||||
cfg := config.(map[string]interface{})
|
|
||||||
for _, f := range []string{"MacAddress", "NetworkDisabled", "ExposedPorts"} {
|
|
||||||
_, ok := cfg[f]
|
|
||||||
assert.Check(c, ok, "API version %s expected to include %s in 'Config'", version, f)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *DockerAPISuite) TestInspectAPIBridgeNetworkSettings120(c *testing.T) {
|
|
||||||
// Not relevant on Windows, and besides it doesn't have any bridge network settings
|
|
||||||
testRequires(c, DaemonIsLinux)
|
|
||||||
out := cli.DockerCmd(c, "run", "-d", "busybox", "top").Stdout()
|
|
||||||
containerID := strings.TrimSpace(out)
|
|
||||||
cli.WaitRun(c, containerID)
|
|
||||||
|
|
||||||
body := getInspectBody(c, "v1.20", containerID)
|
|
||||||
|
|
||||||
var inspectJSON v1p20.ContainerJSON
|
|
||||||
err := json.Unmarshal(body, &inspectJSON)
|
|
||||||
assert.NilError(c, err)
|
|
||||||
|
|
||||||
settings := inspectJSON.NetworkSettings
|
|
||||||
assert.Assert(c, len(settings.IPAddress) != 0)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Inspect for API v1.21 and up; see
|
// Inspect for API v1.21 and up; see
|
||||||
//
|
//
|
||||||
// - https://github.com/moby/moby/issues/17131
|
// - https://github.com/moby/moby/issues/17131
|
||||||
|
|
|
@ -8,13 +8,11 @@ import (
|
||||||
"runtime"
|
"runtime"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/docker/docker/api/types"
|
"github.com/docker/docker/api/types"
|
||||||
"github.com/docker/docker/api/types/system"
|
"github.com/docker/docker/api/types/system"
|
||||||
"github.com/docker/docker/api/types/versions"
|
|
||||||
"github.com/docker/docker/client"
|
"github.com/docker/docker/client"
|
||||||
"github.com/docker/docker/integration-cli/cli"
|
"github.com/docker/docker/integration-cli/cli"
|
||||||
"github.com/docker/docker/testutil"
|
"github.com/docker/docker/testutil"
|
||||||
|
@ -166,27 +164,13 @@ func (s *DockerAPISuite) TestAPIStatsNetworkStats(c *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *DockerAPISuite) TestAPIStatsNetworkStatsVersioning(c *testing.T) {
|
func (s *DockerAPISuite) TestAPIStatsNetworkStatsVersioning(c *testing.T) {
|
||||||
// Windows doesn't support API versions less than 1.25, so no point testing 1.17 .. 1.21
|
|
||||||
testRequires(c, testEnv.IsLocalDaemon, DaemonIsLinux)
|
testRequires(c, testEnv.IsLocalDaemon, DaemonIsLinux)
|
||||||
|
|
||||||
id := runSleepingContainer(c)
|
id := runSleepingContainer(c)
|
||||||
cli.WaitRun(c, id)
|
cli.WaitRun(c, id)
|
||||||
wg := sync.WaitGroup{}
|
|
||||||
|
|
||||||
for i := 17; i <= 21; i++ {
|
statsJSONBlob := getStats(c, id)
|
||||||
wg.Add(1)
|
assert.Assert(c, jsonBlobHasGTE121NetworkStats(statsJSONBlob), "Stats JSON blob from API does not look like a >=v1.21 API stats structure", statsJSONBlob)
|
||||||
go func(i int) {
|
|
||||||
defer wg.Done()
|
|
||||||
apiVersion := fmt.Sprintf("v1.%d", i)
|
|
||||||
statsJSONBlob := getVersionedStats(c, id, apiVersion)
|
|
||||||
if versions.LessThan(apiVersion, "v1.21") {
|
|
||||||
assert.Assert(c, jsonBlobHasLTv121NetworkStats(statsJSONBlob), "Stats JSON blob from API %s %#v does not look like a <v1.21 API stats structure", apiVersion, statsJSONBlob)
|
|
||||||
} else {
|
|
||||||
assert.Assert(c, jsonBlobHasGTE121NetworkStats(statsJSONBlob), "Stats JSON blob from API %s %#v does not look like a >=v1.21 API stats structure", apiVersion, statsJSONBlob)
|
|
||||||
}
|
|
||||||
}(i)
|
|
||||||
}
|
|
||||||
wg.Wait()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func getNetworkStats(c *testing.T, id string) map[string]types.NetworkStats {
|
func getNetworkStats(c *testing.T, id string) map[string]types.NetworkStats {
|
||||||
|
@ -202,14 +186,15 @@ func getNetworkStats(c *testing.T, id string) map[string]types.NetworkStats {
|
||||||
return st.Networks
|
return st.Networks
|
||||||
}
|
}
|
||||||
|
|
||||||
// getVersionedStats returns stats result for the
|
// getStats returns stats result for the
|
||||||
// container with id using an API call with version apiVersion. Since the
|
// container with id using an API call with version apiVersion. Since the
|
||||||
// stats result type differs between API versions, we simply return
|
// stats result type differs between API versions, we simply return
|
||||||
// map[string]interface{}.
|
// map[string]interface{}.
|
||||||
func getVersionedStats(c *testing.T, id string, apiVersion string) map[string]interface{} {
|
func getStats(c *testing.T, id string) map[string]interface{} {
|
||||||
|
c.Helper()
|
||||||
stats := make(map[string]interface{})
|
stats := make(map[string]interface{})
|
||||||
|
|
||||||
_, body, err := request.Get(testutil.GetContext(c), "/"+apiVersion+"/containers/"+id+"/stats?stream=false")
|
_, body, err := request.Get(testutil.GetContext(c), "/containers/"+id+"/stats?stream=false")
|
||||||
assert.NilError(c, err)
|
assert.NilError(c, err)
|
||||||
defer body.Close()
|
defer body.Close()
|
||||||
|
|
||||||
|
@ -219,23 +204,6 @@ func getVersionedStats(c *testing.T, id string, apiVersion string) map[string]in
|
||||||
return stats
|
return stats
|
||||||
}
|
}
|
||||||
|
|
||||||
func jsonBlobHasLTv121NetworkStats(blob map[string]interface{}) bool {
|
|
||||||
networkStatsIntfc, ok := blob["network"]
|
|
||||||
if !ok {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
networkStats, ok := networkStatsIntfc.(map[string]interface{})
|
|
||||||
if !ok {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
for _, expectedKey := range expectedNetworkInterfaceStats {
|
|
||||||
if _, ok := networkStats[expectedKey]; !ok {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
func jsonBlobHasGTE121NetworkStats(blob map[string]interface{}) bool {
|
func jsonBlobHasGTE121NetworkStats(blob map[string]interface{}) bool {
|
||||||
networksStatsIntfc, ok := blob["networks"]
|
networksStatsIntfc, ok := blob["networks"]
|
||||||
if !ok {
|
if !ok {
|
||||||
|
|
|
@ -3,7 +3,6 @@ package main
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"runtime"
|
"runtime"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
@ -62,9 +61,9 @@ func (s *DockerAPISuite) TestAPIClientVersionOldNotSupported(c *testing.T) {
|
||||||
defer body.Close()
|
defer body.Close()
|
||||||
assert.Equal(c, resp.StatusCode, http.StatusBadRequest)
|
assert.Equal(c, resp.StatusCode, http.StatusBadRequest)
|
||||||
expected := fmt.Sprintf("client version %s is too old. Minimum supported API version is %s, please upgrade your client to a newer version", version, testEnv.DaemonVersion.MinAPIVersion)
|
expected := fmt.Sprintf("client version %s is too old. Minimum supported API version is %s, please upgrade your client to a newer version", version, testEnv.DaemonVersion.MinAPIVersion)
|
||||||
content, err := io.ReadAll(body)
|
b, err := request.ReadBody(body)
|
||||||
assert.NilError(c, err)
|
assert.NilError(c, err)
|
||||||
assert.Equal(c, strings.TrimSpace(string(content)), expected)
|
assert.Equal(c, getErrorMessage(c, b), expected)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *DockerAPISuite) TestAPIErrorJSON(c *testing.T) {
|
func (s *DockerAPISuite) TestAPIErrorJSON(c *testing.T) {
|
||||||
|
@ -77,19 +76,6 @@ func (s *DockerAPISuite) TestAPIErrorJSON(c *testing.T) {
|
||||||
assert.Equal(c, getErrorMessage(c, b), runconfig.ErrEmptyConfig.Error())
|
assert.Equal(c, getErrorMessage(c, b), runconfig.ErrEmptyConfig.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *DockerAPISuite) TestAPIErrorPlainText(c *testing.T) {
|
|
||||||
// Windows requires API 1.25 or later. This test is validating a behaviour which was present
|
|
||||||
// in v1.23, but changed in 1.24, hence not applicable on Windows. See apiVersionSupportsJSONErrors
|
|
||||||
testRequires(c, DaemonIsLinux)
|
|
||||||
httpResp, body, err := request.Post(testutil.GetContext(c), "/v1.23/containers/create", request.JSONBody(struct{}{}))
|
|
||||||
assert.NilError(c, err)
|
|
||||||
assert.Equal(c, httpResp.StatusCode, http.StatusBadRequest)
|
|
||||||
assert.Assert(c, strings.Contains(httpResp.Header.Get("Content-Type"), "text/plain"))
|
|
||||||
b, err := request.ReadBody(body)
|
|
||||||
assert.NilError(c, err)
|
|
||||||
assert.Equal(c, strings.TrimSpace(string(b)), runconfig.ErrEmptyConfig.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *DockerAPISuite) TestAPIErrorNotFoundJSON(c *testing.T) {
|
func (s *DockerAPISuite) TestAPIErrorNotFoundJSON(c *testing.T) {
|
||||||
// 404 is a different code path to normal errors, so test separately
|
// 404 is a different code path to normal errors, so test separately
|
||||||
httpResp, body, err := request.Get(testutil.GetContext(c), "/notfound", request.JSON)
|
httpResp, body, err := request.Get(testutil.GetContext(c), "/notfound", request.JSON)
|
||||||
|
@ -100,13 +86,3 @@ func (s *DockerAPISuite) TestAPIErrorNotFoundJSON(c *testing.T) {
|
||||||
assert.NilError(c, err)
|
assert.NilError(c, err)
|
||||||
assert.Equal(c, getErrorMessage(c, b), "page not found")
|
assert.Equal(c, getErrorMessage(c, b), "page not found")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *DockerAPISuite) TestAPIErrorNotFoundPlainText(c *testing.T) {
|
|
||||||
httpResp, body, err := request.Get(testutil.GetContext(c), "/v1.23/notfound", request.JSON)
|
|
||||||
assert.NilError(c, err)
|
|
||||||
assert.Equal(c, httpResp.StatusCode, http.StatusNotFound)
|
|
||||||
assert.Assert(c, strings.Contains(httpResp.Header.Get("Content-Type"), "text/plain"))
|
|
||||||
b, err := request.ReadBody(body)
|
|
||||||
assert.NilError(c, err)
|
|
||||||
assert.Equal(c, strings.TrimSpace(string(b)), "page not found")
|
|
||||||
}
|
|
||||||
|
|
|
@ -15,7 +15,6 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/docker/docker/api/types"
|
"github.com/docker/docker/api/types"
|
||||||
"github.com/docker/docker/api/types/versions/v1p20"
|
|
||||||
"github.com/docker/docker/integration-cli/cli"
|
"github.com/docker/docker/integration-cli/cli"
|
||||||
"github.com/docker/docker/integration-cli/daemon"
|
"github.com/docker/docker/integration-cli/daemon"
|
||||||
"github.com/docker/docker/libnetwork/driverapi"
|
"github.com/docker/docker/libnetwork/driverapi"
|
||||||
|
@ -1015,22 +1014,14 @@ func (s *DockerCLINetworkSuite) TestInspectAPIMultipleNetworks(c *testing.T) {
|
||||||
cli.DockerCmd(c, "network", "connect", "mybridge1", id)
|
cli.DockerCmd(c, "network", "connect", "mybridge1", id)
|
||||||
cli.DockerCmd(c, "network", "connect", "mybridge2", id)
|
cli.DockerCmd(c, "network", "connect", "mybridge2", id)
|
||||||
|
|
||||||
body := getInspectBody(c, "v1.20", id)
|
|
||||||
var inspect120 v1p20.ContainerJSON
|
|
||||||
err := json.Unmarshal(body, &inspect120)
|
|
||||||
assert.NilError(c, err)
|
|
||||||
|
|
||||||
versionedIP := inspect120.NetworkSettings.IPAddress
|
|
||||||
|
|
||||||
// Current API version (API v1.21 and up)
|
// Current API version (API v1.21 and up)
|
||||||
body = getInspectBody(c, "", id)
|
body := getInspectBody(c, "", id)
|
||||||
var inspectCurrent types.ContainerJSON
|
var inspectCurrent types.ContainerJSON
|
||||||
err = json.Unmarshal(body, &inspectCurrent)
|
err := json.Unmarshal(body, &inspectCurrent)
|
||||||
assert.NilError(c, err)
|
assert.NilError(c, err)
|
||||||
assert.Equal(c, len(inspectCurrent.NetworkSettings.Networks), 3)
|
assert.Equal(c, len(inspectCurrent.NetworkSettings.Networks), 3)
|
||||||
|
|
||||||
bridge := inspectCurrent.NetworkSettings.Networks["bridge"]
|
bridge := inspectCurrent.NetworkSettings.Networks["bridge"]
|
||||||
assert.Equal(c, bridge.IPAddress, versionedIP)
|
|
||||||
assert.Equal(c, bridge.IPAddress, inspectCurrent.NetworkSettings.IPAddress)
|
assert.Equal(c, bridge.IPAddress, inspectCurrent.NetworkSettings.IPAddress)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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,15 +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/copy", // deprecated since 1.8 (API v1.20), errors out since 1.12 (API v1.24)
|
|
||||||
"/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) {
|
||||||
|
|
|
@ -1,53 +1,17 @@
|
||||||
package container // import "github.com/docker/docker/integration/container"
|
package container // import "github.com/docker/docker/integration/container"
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
|
||||||
"runtime"
|
"runtime"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
|
||||||
|
|
||||||
containertypes "github.com/docker/docker/api/types/container"
|
containertypes "github.com/docker/docker/api/types/container"
|
||||||
"github.com/docker/docker/client"
|
|
||||||
"github.com/docker/docker/integration/internal/container"
|
"github.com/docker/docker/integration/internal/container"
|
||||||
"github.com/docker/docker/testutil/request"
|
"github.com/docker/docker/testutil/request"
|
||||||
"gotest.tools/v3/assert"
|
"gotest.tools/v3/assert"
|
||||||
is "gotest.tools/v3/assert/cmp"
|
is "gotest.tools/v3/assert/cmp"
|
||||||
"gotest.tools/v3/poll"
|
|
||||||
"gotest.tools/v3/skip"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestInspectCpusetInConfigPre120(t *testing.T) {
|
|
||||||
skip.If(t, testEnv.DaemonInfo.OSType == "windows" || !testEnv.DaemonInfo.CPUSet)
|
|
||||||
|
|
||||||
ctx := setupTest(t)
|
|
||||||
apiClient := request.NewAPIClient(t, client.WithVersion("1.19"))
|
|
||||||
|
|
||||||
name := strings.ToLower(t.Name())
|
|
||||||
// Create container with up to-date-API
|
|
||||||
container.Run(ctx, t, request.NewAPIClient(t), container.WithName(name),
|
|
||||||
container.WithCmd("true"),
|
|
||||||
func(c *container.TestContainerConfig) {
|
|
||||||
c.HostConfig.Resources.CpusetCpus = "0"
|
|
||||||
},
|
|
||||||
)
|
|
||||||
poll.WaitOn(t, container.IsInState(ctx, apiClient, name, "exited"), poll.WithDelay(100*time.Millisecond))
|
|
||||||
|
|
||||||
_, body, err := apiClient.ContainerInspectWithRaw(ctx, name, false)
|
|
||||||
assert.NilError(t, err)
|
|
||||||
|
|
||||||
var inspectJSON map[string]interface{}
|
|
||||||
err = json.Unmarshal(body, &inspectJSON)
|
|
||||||
assert.NilError(t, err, "unable to unmarshal body for version 1.19: %s", err)
|
|
||||||
|
|
||||||
config, ok := inspectJSON["Config"]
|
|
||||||
assert.Check(t, is.Equal(true, ok), "Unable to find 'Config'")
|
|
||||||
|
|
||||||
cfg := config.(map[string]interface{})
|
|
||||||
_, ok = cfg["Cpuset"]
|
|
||||||
assert.Check(t, is.Equal(true, ok), "API version 1.19 expected to include Cpuset in 'Config'")
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestInspectAnnotations(t *testing.T) {
|
func TestInspectAnnotations(t *testing.T) {
|
||||||
ctx := setupTest(t)
|
ctx := setupTest(t)
|
||||||
apiClient := request.NewAPIClient(t)
|
apiClient := request.NewAPIClient(t)
|
||||||
|
|
|
@ -6,7 +6,6 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
containertypes "github.com/docker/docker/api/types/container"
|
containertypes "github.com/docker/docker/api/types/container"
|
||||||
"github.com/docker/docker/client"
|
|
||||||
"github.com/docker/docker/integration/internal/container"
|
"github.com/docker/docker/integration/internal/container"
|
||||||
"github.com/docker/docker/testutil"
|
"github.com/docker/docker/testutil"
|
||||||
"github.com/docker/docker/testutil/request"
|
"github.com/docker/docker/testutil/request"
|
||||||
|
@ -133,15 +132,6 @@ func TestKillStoppedContainer(t *testing.T) {
|
||||||
assert.Assert(t, is.Contains(err.Error(), "is not running"))
|
assert.Assert(t, is.Contains(err.Error(), "is not running"))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestKillStoppedContainerAPIPre120(t *testing.T) {
|
|
||||||
skip.If(t, testEnv.DaemonInfo.OSType == "windows", "Windows only supports 1.25 or later")
|
|
||||||
ctx := setupTest(t)
|
|
||||||
apiClient := request.NewAPIClient(t, client.WithVersion("1.19"))
|
|
||||||
id := container.Create(ctx, t, apiClient)
|
|
||||||
err := apiClient.ContainerKill(ctx, id, "SIGKILL")
|
|
||||||
assert.NilError(t, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestKillDifferentUserContainer(t *testing.T) {
|
func TestKillDifferentUserContainer(t *testing.T) {
|
||||||
// TODO Windows: Windows does not yet support -u (Feb 2016).
|
// TODO Windows: Windows does not yet support -u (Feb 2016).
|
||||||
skip.If(t, testEnv.DaemonInfo.OSType == "windows", "User containers (container.Config.User) are not yet supported on %q platform", testEnv.DaemonInfo.OSType)
|
skip.If(t, testEnv.DaemonInfo.OSType == "windows", "User containers (container.Config.User) are not yet supported on %q platform", testEnv.DaemonInfo.OSType)
|
||||||
|
|
|
@ -31,7 +31,6 @@ func TestInfoBinaryCommits(t *testing.T) {
|
||||||
|
|
||||||
func TestInfoAPIVersioned(t *testing.T) {
|
func TestInfoAPIVersioned(t *testing.T) {
|
||||||
ctx := testutil.StartSpan(baseContext, t)
|
ctx := testutil.StartSpan(baseContext, t)
|
||||||
// Windows only supports 1.25 or later
|
|
||||||
|
|
||||||
res, body, err := req.Get(ctx, "/v1.24/info")
|
res, body, err := req.Get(ctx, "/v1.24/info")
|
||||||
assert.NilError(t, err)
|
assert.NilError(t, err)
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -26,11 +26,10 @@ func TestDecodeContainerConfig(t *testing.T) {
|
||||||
imgName string
|
imgName string
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// FIXME (thaJeztah): update fixtures for more current versions.
|
||||||
if runtime.GOOS != "windows" {
|
if runtime.GOOS != "windows" {
|
||||||
imgName = "ubuntu"
|
imgName = "ubuntu"
|
||||||
fixtures = []f{
|
fixtures = []f{
|
||||||
{"fixtures/unix/container_config_1_14.json", strslice.StrSlice{}},
|
|
||||||
{"fixtures/unix/container_config_1_17.json", strslice.StrSlice{"bash"}},
|
|
||||||
{"fixtures/unix/container_config_1_19.json", strslice.StrSlice{"bash"}},
|
{"fixtures/unix/container_config_1_19.json", strslice.StrSlice{"bash"}},
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -1,30 +0,0 @@
|
||||||
{
|
|
||||||
"Hostname":"",
|
|
||||||
"Domainname": "",
|
|
||||||
"User":"",
|
|
||||||
"Memory": 1000,
|
|
||||||
"MemorySwap":0,
|
|
||||||
"CpuShares": 512,
|
|
||||||
"Cpuset": "0,1",
|
|
||||||
"AttachStdin":false,
|
|
||||||
"AttachStdout":true,
|
|
||||||
"AttachStderr":true,
|
|
||||||
"PortSpecs":null,
|
|
||||||
"Tty":false,
|
|
||||||
"OpenStdin":false,
|
|
||||||
"StdinOnce":false,
|
|
||||||
"Env":null,
|
|
||||||
"Cmd":[
|
|
||||||
"bash"
|
|
||||||
],
|
|
||||||
"Image":"ubuntu",
|
|
||||||
"Volumes":{
|
|
||||||
"/tmp": {}
|
|
||||||
},
|
|
||||||
"WorkingDir":"",
|
|
||||||
"NetworkDisabled": false,
|
|
||||||
"ExposedPorts":{
|
|
||||||
"22/tcp": {}
|
|
||||||
},
|
|
||||||
"RestartPolicy": { "Name": "always" }
|
|
||||||
}
|
|
|
@ -1,50 +0,0 @@
|
||||||
{
|
|
||||||
"Hostname": "",
|
|
||||||
"Domainname": "",
|
|
||||||
"User": "",
|
|
||||||
"Memory": 1000,
|
|
||||||
"MemorySwap": 0,
|
|
||||||
"CpuShares": 512,
|
|
||||||
"Cpuset": "0,1",
|
|
||||||
"AttachStdin": false,
|
|
||||||
"AttachStdout": true,
|
|
||||||
"AttachStderr": true,
|
|
||||||
"Tty": false,
|
|
||||||
"OpenStdin": false,
|
|
||||||
"StdinOnce": false,
|
|
||||||
"Env": null,
|
|
||||||
"Cmd": [
|
|
||||||
"date"
|
|
||||||
],
|
|
||||||
"Entrypoint": "bash",
|
|
||||||
"Image": "ubuntu",
|
|
||||||
"Volumes": {
|
|
||||||
"/tmp": {}
|
|
||||||
},
|
|
||||||
"WorkingDir": "",
|
|
||||||
"NetworkDisabled": false,
|
|
||||||
"MacAddress": "12:34:56:78:9a:bc",
|
|
||||||
"ExposedPorts": {
|
|
||||||
"22/tcp": {}
|
|
||||||
},
|
|
||||||
"SecurityOpt": [""],
|
|
||||||
"HostConfig": {
|
|
||||||
"Binds": ["/tmp:/tmp"],
|
|
||||||
"Links": ["redis3:redis"],
|
|
||||||
"LxcConf": {"lxc.utsname":"docker"},
|
|
||||||
"PortBindings": { "22/tcp": [{ "HostPort": "11022" }] },
|
|
||||||
"PublishAllPorts": false,
|
|
||||||
"Privileged": false,
|
|
||||||
"ReadonlyRootfs": false,
|
|
||||||
"Dns": ["8.8.8.8"],
|
|
||||||
"DnsSearch": [""],
|
|
||||||
"DnsOptions": [""],
|
|
||||||
"ExtraHosts": null,
|
|
||||||
"VolumesFrom": ["parent", "other:ro"],
|
|
||||||
"CapAdd": ["NET_ADMIN"],
|
|
||||||
"CapDrop": ["MKNOD"],
|
|
||||||
"RestartPolicy": { "Name": "", "MaximumRetryCount": 0 },
|
|
||||||
"NetworkMode": "bridge",
|
|
||||||
"Devices": []
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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