Merge pull request #47155 from thaJeztah/remove_deprecated_api_versions

api: remove deprecated API versions (API < v1.24)
This commit is contained in:
Sebastiaan van Stijn 2024-02-07 01:43:04 +01:00 committed by GitHub
commit 9e075f3808
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
58 changed files with 200 additions and 1445 deletions

View file

@ -2,9 +2,18 @@ package api // import "github.com/docker/docker/api"
// Common constants for daemon and client.
const (
// DefaultVersion of Current REST API
// DefaultVersion of the current REST API.
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
// command to specify that no base image is to be used.
NoBaseImageSpecifier = "scratch"

View file

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

View file

@ -12,5 +12,4 @@ import (
// container configuration.
type ContainerDecoder interface {
DecodeConfig(src io.Reader) (*container.Config, *container.HostConfig, *network.NetworkingConfig, error)
DecodeHostConfig(src io.Reader) (*container.HostConfig, error)
}

View file

@ -6,6 +6,7 @@ import (
"net/http"
"runtime"
"github.com/docker/docker/api"
"github.com/docker/docker/api/server/httputils"
"github.com/docker/docker/api/types/versions"
)
@ -13,19 +14,40 @@ import (
// VersionMiddleware is a middleware that
// validates the client and server versions.
type VersionMiddleware struct {
serverVersion string
defaultVersion string
minVersion string
serverVersion 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
// with the default versions.
func NewVersionMiddleware(s, d, m string) VersionMiddleware {
return VersionMiddleware{
serverVersion: s,
defaultVersion: d,
minVersion: m,
// NewVersionMiddleware creates a VersionMiddleware with the given versions.
func NewVersionMiddleware(serverVersion, defaultAPIVersion, minAPIVersion string) (*VersionMiddleware, error) {
if versions.LessThan(defaultAPIVersion, api.MinSupportedAPIVersion) || versions.GreaterThan(defaultAPIVersion, api.DefaultVersion) {
return nil, fmt.Errorf("invalid default API version (%s): must be between %s and %s", defaultAPIVersion, api.MinSupportedAPIVersion, api.DefaultVersion)
}
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 {
@ -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 {
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("API-Version", v.defaultVersion)
w.Header().Set("API-Version", v.defaultAPIVersion)
w.Header().Set("OSType", runtime.GOOS)
apiVersion := vars["version"]
if apiVersion == "" {
apiVersion = v.defaultVersion
apiVersion = v.defaultAPIVersion
}
if versions.LessThan(apiVersion, v.minVersion) {
return versionUnsupportedError{version: apiVersion, minVersion: v.minVersion}
if versions.LessThan(apiVersion, v.minAPIVersion) {
return versionUnsupportedError{version: apiVersion, minVersion: v.minAPIVersion}
}
if versions.GreaterThan(apiVersion, v.defaultVersion) {
return versionUnsupportedError{version: apiVersion, maxVersion: v.defaultVersion}
if versions.GreaterThan(apiVersion, v.defaultAPIVersion) {
return versionUnsupportedError{version: apiVersion, maxVersion: v.defaultAPIVersion}
}
ctx = context.WithValue(ctx, httputils.APIVersionKey{}, apiVersion)
return handler(ctx, w, r, vars)

View file

@ -2,27 +2,82 @@ package middleware // import "github.com/docker/docker/api/server/middleware"
import (
"context"
"fmt"
"net/http"
"net/http/httptest"
"runtime"
"testing"
"github.com/docker/docker/api"
"github.com/docker/docker/api/server/httputils"
"gotest.tools/v3/assert"
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) {
defaultVersion := "1.10.0"
minVersion := "1.2.0"
expectedVersion := defaultVersion
expectedVersion := "<not set>"
handler := func(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
v := httputils.VersionFromContext(ctx)
assert.Check(t, is.Equal(expectedVersion, v))
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)
req, _ := http.NewRequest(http.MethodGet, "/containers/json", nil)
@ -35,19 +90,19 @@ func TestVersionMiddlewareVersion(t *testing.T) {
errString string
}{
{
expectedVersion: "1.10.0",
expectedVersion: api.DefaultVersion,
},
{
reqVersion: "1.9.0",
expectedVersion: "1.9.0",
reqVersion: api.MinSupportedAPIVersion,
expectedVersion: api.MinSupportedAPIVersion,
},
{
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",
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
}
defaultVersion := "1.10.0"
minVersion := "1.2.0"
m := NewVersionMiddleware(defaultVersion, defaultVersion, minVersion)
m, err := NewVersionMiddleware("1.2.3", api.DefaultVersion, api.MinSupportedAPIVersion)
assert.NilError(t, err)
h := m.WrapHandler(handler)
req, _ := http.NewRequest(http.MethodGet, "/containers/json", nil)
@ -81,12 +135,12 @@ func TestVersionMiddlewareWithErrorsReturnsHeaders(t *testing.T) {
ctx := context.Background()
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, ""))
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.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))
}

View file

@ -42,6 +42,7 @@ func newImageBuildOptions(ctx context.Context, r *http.Request) (*types.ImageBui
SuppressOutput: httputils.BoolValue(r, "q"),
NoCache: httputils.BoolValue(r, "nocache"),
ForceRemove: httputils.BoolValue(r, "forcerm"),
PullParent: httputils.BoolValue(r, "pull"),
MemorySwap: httputils.Int64ValueOrZero(r, "memswap"),
Memory: httputils.Int64ValueOrZero(r, "memory"),
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)}
}
version := httputils.VersionFromContext(ctx)
if httputils.BoolValue(r, "forcerm") && versions.GreaterThanOrEqualTo(version, "1.12") {
if httputils.BoolValue(r, "forcerm") {
options.Remove = true
} else if r.FormValue("rm") == "" && versions.GreaterThanOrEqualTo(version, "1.12") {
} else if r.FormValue("rm") == "" {
options.Remove = true
} else {
options.Remove = httputils.BoolValue(r, "rm")
}
if httputils.BoolValue(r, "pull") && versions.GreaterThanOrEqualTo(version, "1.16") {
options.PullParent = true
}
version := httputils.VersionFromContext(ctx)
if versions.GreaterThanOrEqualTo(version, "1.32") {
options.Platform = r.FormValue("platform")
}

View file

@ -24,7 +24,6 @@ type execBackend interface {
// copyBackend includes functions to implement to provide container copy functionality.
type copyBackend interface {
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
ContainerExtractToDir(name, path string, copyUIDGID, noOverwriteDirNonDir bool, content io.Reader) 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
ContainerRestart(ctx context.Context, name string, options container.StopOptions) 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
ContainerUnpause(name string) error
ContainerUpdate(name string, hostConfig *container.HostConfig) (container.ContainerUpdateOKBody, error)

View file

@ -56,7 +56,6 @@ func (r *containerRouter) initRoutes() {
router.NewPostRoute("/containers/{name:.*}/wait", r.postContainersWait),
router.NewPostRoute("/containers/{name:.*}/resize", r.postContainersResize),
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("/exec/{name:.*}/start", r.postContainerExecStart),
router.NewPostRoute("/exec/{name:.*}/resize", r.postContainerExecResize),

View file

@ -39,13 +39,6 @@ func (s *containerRouter) postCommit(ctx context.Context, w http.ResponseWriter,
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)
if err != nil && !errors.Is(err, io.EOF) { // Do not fail if body is empty.
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{
Pause: pause,
Pause: httputils.BoolValueOrDefault(r, "pause", true), // TODO(dnephin): remove pause arg, and always pause in backend
Tag: ref,
Author: r.Form.Get("author"),
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)
}
config := &backend.ContainerStatsConfig{
return s.backend.ContainerStats(ctx, vars["name"], &backend.ContainerStatsConfig{
Stream: stream,
OneShot: oneShot,
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 {
@ -178,14 +168,6 @@ func (s *containerRouter) getContainersExport(ctx context.Context, w http.Respon
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 {
// If contentLength is -1, we can assumed chunked encoding
// 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
// including r.TransferEncoding
// 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.
if r.ContentLength > 7 || r.ContentLength == -1 {
if versions.GreaterThanOrEqualTo(version, "1.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
return errdefs.InvalidParameter(errors.New("starting container with non-empty request body was deprecated since API v1.22 and removed in v1.24"))
}
if err := httputils.ParseForm(r); err != nil {
return err
}
checkpoint := r.Form.Get("checkpoint")
checkpointDir := r.Form.Get("checkpoint-dir")
if err := s.backend.ContainerStart(ctx, vars["name"], hostConfig, checkpoint, checkpointDir); err != nil {
if err := s.backend.ContainerStart(ctx, vars["name"], r.Form.Get("checkpoint"), r.Form.Get("checkpoint-dir")); err != nil {
return err
}
@ -255,25 +221,14 @@ func (s *containerRouter) postContainersStop(ctx context.Context, w http.Respons
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 {
return err
}
name := vars["name"]
if err := s.backend.ContainerKill(name, r.Form.Get("signal")); err != nil {
var isStopped bool
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)
}
return errors.Wrapf(err, "cannot kill container: %s", name)
}
w.WriteHeader(http.StatusNoContent)
@ -512,7 +467,6 @@ func (s *containerRouter) postContainersCreate(ctx context.Context, w http.Respo
}
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
if versions.LessThan(version, "1.25") {
@ -656,7 +610,6 @@ func (s *containerRouter) postContainersCreate(ctx context.Context, w http.Respo
Config: config,
HostConfig: hostConfig,
NetworkingConfig: networkingConfig,
AdjustCPUShares: adjustCPUShares,
Platform: platform,
})
if err != nil {

View file

@ -11,49 +11,10 @@ import (
"github.com/docker/docker/api/server/httputils"
"github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/versions"
gddohttputil "github.com/golang/gddo/httputil"
)
type pathError struct{}
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.
// setContainerPathStatHeader encodes the stat to JSON, base64 encode, and place in a header.
func setContainerPathStatHeader(stat *types.ContainerPathStat, header http.Header) error {
statJSON, err := json.Marshal(stat)
if err != nil {

View file

@ -71,15 +71,6 @@ func (s *containerRouter) postContainerExecStart(ctx context.Context, w http.Res
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 (
execName = vars["name"]
stdin, inStream io.ReadCloser
@ -96,6 +87,8 @@ func (s *containerRouter) postContainerExecStart(ctx context.Context, w http.Res
}
if execStartCheck.ConsoleSize != nil {
version := httputils.VersionFromContext(ctx)
// Not supported before 1.42
if versions.LessThan(version, "1.42") {
execStartCheck.ConsoleSize = nil

View file

@ -10,6 +10,7 @@ import (
"github.com/docker/docker/api/server/middleware"
"github.com/docker/docker/api/server/router"
"github.com/docker/docker/api/server/router/debug"
"github.com/docker/docker/api/types"
"github.com/docker/docker/dockerversion"
"github.com/gorilla/mux"
"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 {
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
}
type pageNotFoundError struct{}
func (pageNotFoundError) Error() string {
return "page not found"
}
func (pageNotFoundError) NotFound() {}
// CreateMux returns a new mux with all the routers registered.
func (s *Server) CreateMux(routers ...router.Router) *mux.Router {
m := mux.NewRouter()
@ -91,7 +86,12 @@ func (s *Server) CreateMux(routers ...router.Router) *mux.Router {
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.NotFoundHandler = notFoundHandler
m.MethodNotAllowedHandler = notFoundHandler

View file

@ -15,8 +15,11 @@ import (
func TestMiddlewares(t *testing.T) {
srv := &Server{}
const apiMinVersion = "1.12"
srv.UseMiddleware(middleware.NewVersionMiddleware("0.1omega2", api.DefaultVersion, apiMinVersion))
m, err := middleware.NewVersionMiddleware("0.1omega2", api.DefaultVersion, api.MinSupportedAPIVersion)
if err != nil {
t.Fatal(err)
}
srv.UseMiddleware(*m)
req, _ := http.NewRequest(http.MethodGet, "/containers/json", nil)
resp := httptest.NewRecorder()

View file

@ -18,7 +18,6 @@ type ContainerCreateConfig struct {
HostConfig *container.HostConfig
NetworkingConfig *network.NetworkingConfig
Platform *ocispec.Platform
AdjustCPUShares bool
}
// ContainerRmConfig holds arguments for the container remove
@ -90,7 +89,6 @@ type ContainerStatsConfig struct {
Stream bool
OneShot bool
OutStream io.Writer
Version string
}
// ExecInspect holds information about a running process started

View file

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

View file

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

View file

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

View file

@ -64,7 +64,7 @@ type ExecBackend interface {
// ContainerRm removes a container specified by `id`.
ContainerRm(name string, config *backend.ContainerRmConfig) error
// 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(ctx context.Context, name string, condition containerpkg.WaitCondition) (<-chan containerpkg.StateStatus, error)
}

View file

@ -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)
logCancellationError(cancelErrCh, "error from ContainerStart: "+err.Error())
return err

View file

@ -46,7 +46,7 @@ func (m *MockBackend) CommitBuildStep(ctx context.Context, c backend.CommitConfi
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
}

View file

@ -256,7 +256,10 @@ func (cli *DaemonCli) start(opts *daemonOptions) (err error) {
pluginStore := plugin.NewStore()
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)
if err != nil {
@ -708,14 +711,15 @@ func (opts routerOptions) Build() []router.Router {
return routers
}
func initMiddlewares(s *apiserver.Server, cfg *config.Config, pluginStore plugingetter.PluginGetter) *authorization.Middleware {
v := dockerversion.Version
func initMiddlewares(s *apiserver.Server, cfg *config.Config, pluginStore plugingetter.PluginGetter) (*authorization.Middleware, error) {
exp := middleware.NewExperimentalMiddleware(cfg.Experimental)
s.UseMiddleware(exp)
vm := middleware.NewVersionMiddleware(v, api.DefaultVersion, cfg.MinAPIVersion)
s.UseMiddleware(vm)
vm, err := middleware.NewVersionMiddleware(dockerversion.Version, api.DefaultVersion, cfg.MinAPIVersion)
if err != nil {
return nil, err
}
s.UseMiddleware(*vm)
if 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)
s.UseMiddleware(authzMiddleware)
return authzMiddleware
return authzMiddleware, nil
}
func (cli *DaemonCli) getContainerdDaemonOpts() ([]supervisor.DaemonOpt, error) {

View file

@ -8,25 +8,6 @@ import (
"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
// container identified by the given name.
func (daemon *Daemon) ContainerStatPath(name string, path string) (stat *types.ContainerPathStat, err error) {

View file

@ -161,55 +161,6 @@ func (daemon *Daemon) containerExtractToDir(container *container.Container, path
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
// cannot be in a read-only volume. If it is not in a volume, the container
// cannot be configured with a read-only rootfs.

View file

@ -38,7 +38,7 @@ type Backend interface {
SetupIngress(clustertypes.NetworkCreateRequest, string) (<-chan struct{}, error)
ReleaseIngress() (<-chan struct{}, 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
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

View file

@ -347,7 +347,7 @@ func (c *containerAdapter) start(ctx context.Context) error {
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) {

View file

@ -56,9 +56,9 @@ const (
DefaultPluginNamespace = "plugins.moby"
// defaultMinAPIVersion is the minimum API version supported by the API.
// This version can be overridden through the "DOCKER_MIN_API_VERSION"
// environment variable. The minimum allowed version is determined
// by [minAPIVersion].
defaultMinAPIVersion = "1.24"
// environment variable. It currently defaults to the minimum API version
// supported by the API server.
defaultMinAPIVersion = api.MinSupportedAPIVersion
// SeccompProfileDefault is the built-in default seccomp profile.
SeccompProfileDefault = "builtin"
// 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") {
return errors.New(`API version must be provided without "v" prefix`)
}
if versions.LessThan(ver, minAPIVersion) {
return errors.Errorf(`minimum supported API version is %s: %s`, minAPIVersion, ver)
if versions.LessThan(ver, defaultMinAPIVersion) {
return errors.Errorf(`minimum supported API version is %s: %s`, defaultMinAPIVersion, ver)
}
if versions.GreaterThan(ver, api.DefaultVersion) {
return errors.Errorf(`maximum supported API version is %s: %s`, api.DefaultVersion, ver)

View file

@ -33,9 +33,6 @@ const (
// OCI runtime being shipped with the docker daemon package.
StockRuntimeName = "runc"
// minAPIVersion represents Minimum REST API version supported
minAPIVersion = "1.12"
// userlandProxyBinary is the name of the userland-proxy binary.
// In rootless-mode, [rootless.RootlessKitDockerProxyBinary] is used instead.
userlandProxyBinary = "docker-proxy"

View file

@ -13,13 +13,6 @@ const (
// default value. On Windows keep this empty so the value is auto-detected
// based on other options.
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

View file

@ -108,7 +108,7 @@ func (daemon *Daemon) containerCreate(ctx context.Context, daemonCfg *configStor
if opts.params.HostConfig == nil {
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 {
return containertypes.CreateResponse{Warnings: warnings}, errdefs.InvalidParameter(err)
}

View file

@ -54,9 +54,16 @@ import (
const (
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
linuxMinCPUShares = 2
linuxMaxCPUShares = 262144
// linuxMinCPUShares = 2
// 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
linuxMinMemory = 6291456
// 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
// settings necessary in the HostConfig structure.
func (daemon *Daemon) adaptContainerSettings(daemonCfg *config.Config, hostConfig *containertypes.HostConfig, adjustCPUShares bool) 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
}
}
func (daemon *Daemon) adaptContainerSettings(daemonCfg *config.Config, hostConfig *containertypes.HostConfig) error {
if hostConfig.Memory > 0 && hostConfig.MemorySwap == 0 {
// By default, MemorySwap is set to twice the size of Memory.
hostConfig.MemorySwap = hostConfig.Memory * 2

View file

@ -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
func TestParseSecurityOptWithDeprecatedColon(t *testing.T) {
opts := &container.SecurityOptions{}

View file

@ -66,7 +66,7 @@ func setupInitLayer(idMapping idtools.IdentityMapping) func(string) error {
// adaptContainerSettings is called during container creation to modify any
// 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
}

View file

@ -14,7 +14,6 @@ import (
containertypes "github.com/docker/docker/api/types/container"
networktypes "github.com/docker/docker/api/types/network"
"github.com/docker/docker/api/types/versions"
"github.com/docker/docker/api/types/versions/v1p20"
"github.com/docker/docker/container"
"github.com/docker/docker/daemon/config"
"github.com/docker/docker/daemon/network"
@ -29,10 +28,6 @@ import (
// there is an error getting the data.
func (daemon *Daemon) ContainerInspect(ctx context.Context, name string, size bool, version string) (interface{}, error) {
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"):
ctr, err := daemon.ContainerInspectCurrent(ctx, name, size)
if err != nil {
@ -117,35 +112,6 @@ func (daemon *Daemon) ContainerInspectCurrent(ctx context.Context, name string,
}, 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) {
// make a copy to play with
hostConfig := *container.HostConfig
@ -283,25 +249,6 @@ func (daemon *Daemon) ContainerExecInspect(id string) (*backend.ExecInspect, err
}, 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
// about the bridge network for a container.
func (daemon *Daemon) getDefaultNetworkSettings(networks map[string]*network.EndpointSettings) types.DefaultNetworkSettings {

View file

@ -1,11 +1,8 @@
package daemon // import "github.com/docker/docker/daemon"
import (
"context"
"github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/backend"
"github.com/docker/docker/api/types/versions/v1p19"
"github.com/docker/docker/container"
)
@ -19,47 +16,6 @@ func setPlatformSpecificContainerFields(container *container.Container, contJSON
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 {
return &backend.ExecProcessConfig{
Tty: e.Tty,

View file

@ -1,8 +1,6 @@
package daemon // import "github.com/docker/docker/daemon"
import (
"context"
"github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/backend"
"github.com/docker/docker/container"
@ -13,11 +11,6 @@ func setPlatformSpecificContainerFields(container *container.Container, contJSON
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 {
return &backend.ExecProcessConfig{
Tty: e.Tty,

View file

@ -2,12 +2,10 @@ package daemon // import "github.com/docker/docker/daemon"
import (
"context"
"runtime"
"time"
"github.com/containerd/log"
"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/container"
"github.com/docker/docker/errdefs"
@ -41,7 +39,7 @@ func validateState(ctr *container.Container) error {
}
// 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()
if checkpoint != "" && !daemonCfg.Experimental {
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
}
// 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.
// 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 {
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)
}

View file

@ -10,8 +10,6 @@ import (
"github.com/containerd/log"
"github.com/docker/docker/api/types"
"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/errdefs"
"github.com/docker/docker/pkg/ioutils"
@ -20,13 +18,6 @@ import (
// ContainerStats writes information about the container to the stream
// given in the config object.
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)
if err != nil {
return err
@ -87,46 +78,7 @@ func (daemon *Daemon) ContainerStats(ctx context.Context, prefixOrName string, c
return nil
}
var statsJSON interface{}
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
}
statsJSON := getStatJSON(v)
if !config.Stream && noStreamFirstFrame {
// prime the cpu stats so they aren't 0 in the final output
noStreamFirstFrame = false

View file

@ -46,9 +46,6 @@ export DOCKER_ALLOW_SCHEMA1_PUSH_DONOTUSE=1
export DOCKER_GRAPHDRIVER=${DOCKER_GRAPHDRIVER:-vfs}
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"
storage_params=""
if [ -n "$DOCKER_STORAGE_OPTS" ]; then

View file

@ -958,81 +958,6 @@ func (s *DockerAPISuite) TestContainerAPICopyNotExistsAnyMore(c *testing.T) {
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) {
id := runSleepingContainer(c)
cli.WaitRun(c, id)
@ -1251,20 +1176,6 @@ func (s *DockerAPISuite) TestPostContainersCreateWithStringOrSliceCapAddDrop(c *
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
// extract an archive to a symlink in a writable volume which points to a
// directory outside of the volume.

View file

@ -109,20 +109,6 @@ func (s *DockerAPISuite) TestExecAPIStartEnsureHeaders(c *testing.T) {
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
func (s *DockerAPISuite) TestExecAPIStartMultipleTimesError(c *testing.T) {
runSleepingContainer(c, "-d", "--name", "test")

View file

@ -6,7 +6,6 @@ import (
"testing"
"github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/versions/v1p20"
"github.com/docker/docker/client"
"github.com/docker/docker/integration-cli/cli"
"github.com/docker/docker/testutil"
@ -21,26 +20,15 @@ func (s *DockerAPISuite) TestInspectAPIContainerResponse(c *testing.T) {
keysBase := []string{
"Id", "State", "Created", "Path", "Args", "Config", "Image", "NetworkSettings",
"ResolvConfPath", "HostnamePath", "HostsPath", "LogPath", "Name", "Driver", "MountLabel", "ProcessLabel", "GraphDriver",
"Mounts",
}
type acase struct {
cases := []struct {
version 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 {
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) {
out := cli.DockerCmd(c, "run", "-d", "--volume-driver", "local", "busybox", "true").Stdout()
cleanedContainerID := strings.TrimSpace(out)
@ -118,47 +84,6 @@ func (s *DockerAPISuite) TestInspectAPIImageResponse(c *testing.T) {
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
//
// - https://github.com/moby/moby/issues/17131

View file

@ -8,13 +8,11 @@ import (
"runtime"
"strconv"
"strings"
"sync"
"testing"
"time"
"github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/system"
"github.com/docker/docker/api/types/versions"
"github.com/docker/docker/client"
"github.com/docker/docker/integration-cli/cli"
"github.com/docker/docker/testutil"
@ -166,27 +164,13 @@ func (s *DockerAPISuite) TestAPIStatsNetworkStats(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)
id := runSleepingContainer(c)
cli.WaitRun(c, id)
wg := sync.WaitGroup{}
for i := 17; i <= 21; i++ {
wg.Add(1)
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()
statsJSONBlob := getStats(c, id)
assert.Assert(c, jsonBlobHasGTE121NetworkStats(statsJSONBlob), "Stats JSON blob from API does not look like a >=v1.21 API stats structure", statsJSONBlob)
}
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
}
// getVersionedStats returns stats result for the
// getStats returns stats result for the
// container with id using an API call with version apiVersion. Since the
// stats result type differs between API versions, we simply return
// 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{})
_, 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)
defer body.Close()
@ -219,23 +204,6 @@ func getVersionedStats(c *testing.T, id string, apiVersion string) map[string]in
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 {
networksStatsIntfc, ok := blob["networks"]
if !ok {

View file

@ -3,7 +3,6 @@ package main
import (
"context"
"fmt"
"io"
"net/http"
"runtime"
"strconv"
@ -62,9 +61,9 @@ func (s *DockerAPISuite) TestAPIClientVersionOldNotSupported(c *testing.T) {
defer body.Close()
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)
content, err := io.ReadAll(body)
b, err := request.ReadBody(body)
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) {
@ -77,19 +76,6 @@ func (s *DockerAPISuite) TestAPIErrorJSON(c *testing.T) {
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) {
// 404 is a different code path to normal errors, so test separately
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.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")
}

View file

@ -15,7 +15,6 @@ import (
"time"
"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/daemon"
"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", "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)
body = getInspectBody(c, "", id)
body := getInspectBody(c, "", id)
var inspectCurrent types.ContainerJSON
err = json.Unmarshal(body, &inspectCurrent)
err := json.Unmarshal(body, &inspectCurrent)
assert.NilError(c, err)
assert.Equal(c, len(inspectCurrent.NetworkSettings.Networks), 3)
bridge := inspectCurrent.NetworkSettings.Networks["bridge"]
assert.Equal(c, bridge.IPAddress, versionedIP)
assert.Equal(c, bridge.IPAddress, inspectCurrent.NetworkSettings.IPAddress)
}

View file

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

View file

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

View file

@ -2,7 +2,6 @@ package container // import "github.com/docker/docker/integration/container"
import (
"net/http"
"runtime"
"testing"
"github.com/docker/docker/testutil"
@ -25,15 +24,6 @@ func TestContainerInvalidJSON(t *testing.T) {
"/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 {
ep := ep
t.Run(ep[1:], func(t *testing.T) {

View file

@ -1,53 +1,17 @@
package container // import "github.com/docker/docker/integration/container"
import (
"encoding/json"
"runtime"
"strings"
"testing"
"time"
containertypes "github.com/docker/docker/api/types/container"
"github.com/docker/docker/client"
"github.com/docker/docker/integration/internal/container"
"github.com/docker/docker/testutil/request"
"gotest.tools/v3/assert"
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) {
ctx := setupTest(t)
apiClient := request.NewAPIClient(t)

View file

@ -6,7 +6,6 @@ import (
"time"
containertypes "github.com/docker/docker/api/types/container"
"github.com/docker/docker/client"
"github.com/docker/docker/integration/internal/container"
"github.com/docker/docker/testutil"
"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"))
}
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) {
// 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)

View file

@ -31,7 +31,6 @@ func TestInfoBinaryCommits(t *testing.T) {
func TestInfoAPIVersioned(t *testing.T) {
ctx := testutil.StartSpan(baseContext, t)
// Windows only supports 1.25 or later
res, body, err := req.Get(ctx, "/v1.24/info")
assert.NilError(t, err)

View file

@ -27,11 +27,6 @@ func (r ContainerDecoder) DecodeConfig(src io.Reader) (*container.Config, *conta
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
// struct and returns both a Config and a HostConfig struct, and performs some
// validation. Certain parameters need daemon-side validation that cannot be done

View file

@ -26,11 +26,10 @@ func TestDecodeContainerConfig(t *testing.T) {
imgName string
)
// FIXME (thaJeztah): update fixtures for more current versions.
if runtime.GOOS != "windows" {
imgName = "ubuntu"
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"}},
}
} else {

View file

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

View file

@ -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": []
}
}

View file

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

View file

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

View file

@ -1,23 +1,12 @@
package runconfig // import "github.com/docker/docker/runconfig"
import (
"io"
"strings"
"github.com/docker/docker/api/types/container"
"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
// 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

View file

@ -3,51 +3,12 @@
package runconfig // import "github.com/docker/docker/runconfig"
import (
"bytes"
"fmt"
"os"
"testing"
"github.com/docker/docker/api/types/container"
"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) {
type resourceTest struct {
ConfigCPURealtimePeriod int64