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.
|
||||
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"
|
||||
|
|
|
@ -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.
|
||||
type ContainerDecoder interface {
|
||||
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"
|
||||
"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)
|
||||
|
|
|
@ -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))
|
||||
}
|
||||
|
|
|
@ -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")
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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(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)
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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{}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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")
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
||||
|
|
|
@ -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 (
|
||||
"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) {
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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"
|
||||
|
||||
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
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue