github.com/rish1988/moby@v25.0.2+incompatible/api/server/router/container/container_routes.go (about)

     1  package container // import "github.com/docker/docker/api/server/router/container"
     2  
     3  import (
     4  	"context"
     5  	"encoding/json"
     6  	"fmt"
     7  	"io"
     8  	"net/http"
     9  	"runtime"
    10  	"strconv"
    11  	"strings"
    12  
    13  	"github.com/containerd/containerd/platforms"
    14  	"github.com/containerd/log"
    15  	"github.com/docker/docker/api/server/httpstatus"
    16  	"github.com/docker/docker/api/server/httputils"
    17  	"github.com/docker/docker/api/types"
    18  	"github.com/docker/docker/api/types/backend"
    19  	"github.com/docker/docker/api/types/container"
    20  	"github.com/docker/docker/api/types/filters"
    21  	"github.com/docker/docker/api/types/mount"
    22  	"github.com/docker/docker/api/types/network"
    23  	"github.com/docker/docker/api/types/versions"
    24  	containerpkg "github.com/docker/docker/container"
    25  	"github.com/docker/docker/errdefs"
    26  	"github.com/docker/docker/pkg/ioutils"
    27  	"github.com/docker/docker/runconfig"
    28  	ocispec "github.com/opencontainers/image-spec/specs-go/v1"
    29  	"github.com/pkg/errors"
    30  	"golang.org/x/net/websocket"
    31  )
    32  
    33  func (s *containerRouter) postCommit(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
    34  	if err := httputils.ParseForm(r); err != nil {
    35  		return err
    36  	}
    37  
    38  	if err := httputils.CheckForJSON(r); err != nil {
    39  		return err
    40  	}
    41  
    42  	// TODO: remove pause arg, and always pause in backend
    43  	pause := httputils.BoolValue(r, "pause")
    44  	version := httputils.VersionFromContext(ctx)
    45  	if r.FormValue("pause") == "" && versions.GreaterThanOrEqualTo(version, "1.13") {
    46  		pause = true
    47  	}
    48  
    49  	config, _, _, err := s.decoder.DecodeConfig(r.Body)
    50  	if err != nil && !errors.Is(err, io.EOF) { // Do not fail if body is empty.
    51  		return err
    52  	}
    53  
    54  	ref, err := httputils.RepoTagReference(r.Form.Get("repo"), r.Form.Get("tag"))
    55  	if err != nil {
    56  		return errdefs.InvalidParameter(err)
    57  	}
    58  
    59  	imgID, err := s.backend.CreateImageFromContainer(ctx, r.Form.Get("container"), &backend.CreateImageConfig{
    60  		Pause:   pause,
    61  		Tag:     ref,
    62  		Author:  r.Form.Get("author"),
    63  		Comment: r.Form.Get("comment"),
    64  		Config:  config,
    65  		Changes: r.Form["changes"],
    66  	})
    67  	if err != nil {
    68  		return err
    69  	}
    70  
    71  	return httputils.WriteJSON(w, http.StatusCreated, &types.IDResponse{ID: imgID})
    72  }
    73  
    74  func (s *containerRouter) getContainersJSON(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
    75  	if err := httputils.ParseForm(r); err != nil {
    76  		return err
    77  	}
    78  	filter, err := filters.FromJSON(r.Form.Get("filters"))
    79  	if err != nil {
    80  		return err
    81  	}
    82  
    83  	config := &container.ListOptions{
    84  		All:     httputils.BoolValue(r, "all"),
    85  		Size:    httputils.BoolValue(r, "size"),
    86  		Since:   r.Form.Get("since"),
    87  		Before:  r.Form.Get("before"),
    88  		Filters: filter,
    89  	}
    90  
    91  	if tmpLimit := r.Form.Get("limit"); tmpLimit != "" {
    92  		limit, err := strconv.Atoi(tmpLimit)
    93  		if err != nil {
    94  			return err
    95  		}
    96  		config.Limit = limit
    97  	}
    98  
    99  	containers, err := s.backend.Containers(ctx, config)
   100  	if err != nil {
   101  		return err
   102  	}
   103  
   104  	return httputils.WriteJSON(w, http.StatusOK, containers)
   105  }
   106  
   107  func (s *containerRouter) getContainersStats(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   108  	if err := httputils.ParseForm(r); err != nil {
   109  		return err
   110  	}
   111  
   112  	stream := httputils.BoolValueOrDefault(r, "stream", true)
   113  	if !stream {
   114  		w.Header().Set("Content-Type", "application/json")
   115  	}
   116  	var oneShot bool
   117  	if versions.GreaterThanOrEqualTo(httputils.VersionFromContext(ctx), "1.41") {
   118  		oneShot = httputils.BoolValueOrDefault(r, "one-shot", false)
   119  	}
   120  
   121  	config := &backend.ContainerStatsConfig{
   122  		Stream:    stream,
   123  		OneShot:   oneShot,
   124  		OutStream: w,
   125  		Version:   httputils.VersionFromContext(ctx),
   126  	}
   127  
   128  	return s.backend.ContainerStats(ctx, vars["name"], config)
   129  }
   130  
   131  func (s *containerRouter) getContainersLogs(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   132  	if err := httputils.ParseForm(r); err != nil {
   133  		return err
   134  	}
   135  
   136  	// Args are validated before the stream starts because when it starts we're
   137  	// sending HTTP 200 by writing an empty chunk of data to tell the client that
   138  	// daemon is going to stream. By sending this initial HTTP 200 we can't report
   139  	// any error after the stream starts (i.e. container not found, wrong parameters)
   140  	// with the appropriate status code.
   141  	stdout, stderr := httputils.BoolValue(r, "stdout"), httputils.BoolValue(r, "stderr")
   142  	if !(stdout || stderr) {
   143  		return errdefs.InvalidParameter(errors.New("Bad parameters: you must choose at least one stream"))
   144  	}
   145  
   146  	containerName := vars["name"]
   147  	logsConfig := &container.LogsOptions{
   148  		Follow:     httputils.BoolValue(r, "follow"),
   149  		Timestamps: httputils.BoolValue(r, "timestamps"),
   150  		Since:      r.Form.Get("since"),
   151  		Until:      r.Form.Get("until"),
   152  		Tail:       r.Form.Get("tail"),
   153  		ShowStdout: stdout,
   154  		ShowStderr: stderr,
   155  		Details:    httputils.BoolValue(r, "details"),
   156  	}
   157  
   158  	msgs, tty, err := s.backend.ContainerLogs(ctx, containerName, logsConfig)
   159  	if err != nil {
   160  		return err
   161  	}
   162  
   163  	contentType := types.MediaTypeRawStream
   164  	if !tty && versions.GreaterThanOrEqualTo(httputils.VersionFromContext(ctx), "1.42") {
   165  		contentType = types.MediaTypeMultiplexedStream
   166  	}
   167  	w.Header().Set("Content-Type", contentType)
   168  
   169  	// if has a tty, we're not muxing streams. if it doesn't, we are. simple.
   170  	// this is the point of no return for writing a response. once we call
   171  	// WriteLogStream, the response has been started and errors will be
   172  	// returned in band by WriteLogStream
   173  	httputils.WriteLogStream(ctx, w, msgs, logsConfig, !tty)
   174  	return nil
   175  }
   176  
   177  func (s *containerRouter) getContainersExport(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   178  	return s.backend.ContainerExport(ctx, vars["name"], w)
   179  }
   180  
   181  type bodyOnStartError struct{}
   182  
   183  func (bodyOnStartError) Error() string {
   184  	return "starting container with non-empty request body was deprecated since API v1.22 and removed in v1.24"
   185  }
   186  
   187  func (bodyOnStartError) InvalidParameter() {}
   188  
   189  func (s *containerRouter) postContainersStart(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   190  	// If contentLength is -1, we can assumed chunked encoding
   191  	// or more technically that the length is unknown
   192  	// https://golang.org/src/pkg/net/http/request.go#L139
   193  	// net/http otherwise seems to swallow any headers related to chunked encoding
   194  	// including r.TransferEncoding
   195  	// allow a nil body for backwards compatibility
   196  
   197  	version := httputils.VersionFromContext(ctx)
   198  	var hostConfig *container.HostConfig
   199  	// A non-nil json object is at least 7 characters.
   200  	if r.ContentLength > 7 || r.ContentLength == -1 {
   201  		if versions.GreaterThanOrEqualTo(version, "1.24") {
   202  			return bodyOnStartError{}
   203  		}
   204  
   205  		if err := httputils.CheckForJSON(r); err != nil {
   206  			return err
   207  		}
   208  
   209  		c, err := s.decoder.DecodeHostConfig(r.Body)
   210  		if err != nil {
   211  			return err
   212  		}
   213  		hostConfig = c
   214  	}
   215  
   216  	if err := httputils.ParseForm(r); err != nil {
   217  		return err
   218  	}
   219  
   220  	checkpoint := r.Form.Get("checkpoint")
   221  	checkpointDir := r.Form.Get("checkpoint-dir")
   222  	if err := s.backend.ContainerStart(ctx, vars["name"], hostConfig, checkpoint, checkpointDir); err != nil {
   223  		return err
   224  	}
   225  
   226  	w.WriteHeader(http.StatusNoContent)
   227  	return nil
   228  }
   229  
   230  func (s *containerRouter) postContainersStop(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   231  	if err := httputils.ParseForm(r); err != nil {
   232  		return err
   233  	}
   234  
   235  	var (
   236  		options container.StopOptions
   237  		version = httputils.VersionFromContext(ctx)
   238  	)
   239  	if versions.GreaterThanOrEqualTo(version, "1.42") {
   240  		options.Signal = r.Form.Get("signal")
   241  	}
   242  	if tmpSeconds := r.Form.Get("t"); tmpSeconds != "" {
   243  		valSeconds, err := strconv.Atoi(tmpSeconds)
   244  		if err != nil {
   245  			return err
   246  		}
   247  		options.Timeout = &valSeconds
   248  	}
   249  
   250  	if err := s.backend.ContainerStop(ctx, vars["name"], options); err != nil {
   251  		return err
   252  	}
   253  
   254  	w.WriteHeader(http.StatusNoContent)
   255  	return nil
   256  }
   257  
   258  func (s *containerRouter) postContainersKill(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   259  	if err := httputils.ParseForm(r); err != nil {
   260  		return err
   261  	}
   262  
   263  	name := vars["name"]
   264  	if err := s.backend.ContainerKill(name, r.Form.Get("signal")); err != nil {
   265  		var isStopped bool
   266  		if errdefs.IsConflict(err) {
   267  			isStopped = true
   268  		}
   269  
   270  		// Return error that's not caused because the container is stopped.
   271  		// Return error if the container is not running and the api is >= 1.20
   272  		// to keep backwards compatibility.
   273  		version := httputils.VersionFromContext(ctx)
   274  		if versions.GreaterThanOrEqualTo(version, "1.20") || !isStopped {
   275  			return errors.Wrapf(err, "Cannot kill container: %s", name)
   276  		}
   277  	}
   278  
   279  	w.WriteHeader(http.StatusNoContent)
   280  	return nil
   281  }
   282  
   283  func (s *containerRouter) postContainersRestart(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   284  	if err := httputils.ParseForm(r); err != nil {
   285  		return err
   286  	}
   287  
   288  	var (
   289  		options container.StopOptions
   290  		version = httputils.VersionFromContext(ctx)
   291  	)
   292  	if versions.GreaterThanOrEqualTo(version, "1.42") {
   293  		options.Signal = r.Form.Get("signal")
   294  	}
   295  	if tmpSeconds := r.Form.Get("t"); tmpSeconds != "" {
   296  		valSeconds, err := strconv.Atoi(tmpSeconds)
   297  		if err != nil {
   298  			return err
   299  		}
   300  		options.Timeout = &valSeconds
   301  	}
   302  
   303  	if err := s.backend.ContainerRestart(ctx, vars["name"], options); err != nil {
   304  		return err
   305  	}
   306  
   307  	w.WriteHeader(http.StatusNoContent)
   308  	return nil
   309  }
   310  
   311  func (s *containerRouter) postContainersPause(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   312  	if err := httputils.ParseForm(r); err != nil {
   313  		return err
   314  	}
   315  
   316  	if err := s.backend.ContainerPause(vars["name"]); err != nil {
   317  		return err
   318  	}
   319  
   320  	w.WriteHeader(http.StatusNoContent)
   321  
   322  	return nil
   323  }
   324  
   325  func (s *containerRouter) postContainersUnpause(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   326  	if err := httputils.ParseForm(r); err != nil {
   327  		return err
   328  	}
   329  
   330  	if err := s.backend.ContainerUnpause(vars["name"]); err != nil {
   331  		return err
   332  	}
   333  
   334  	w.WriteHeader(http.StatusNoContent)
   335  
   336  	return nil
   337  }
   338  
   339  func (s *containerRouter) postContainersWait(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   340  	// Behavior changed in version 1.30 to handle wait condition and to
   341  	// return headers immediately.
   342  	version := httputils.VersionFromContext(ctx)
   343  	legacyBehaviorPre130 := versions.LessThan(version, "1.30")
   344  	legacyRemovalWaitPre134 := false
   345  
   346  	// The wait condition defaults to "not-running".
   347  	waitCondition := containerpkg.WaitConditionNotRunning
   348  	if !legacyBehaviorPre130 {
   349  		if err := httputils.ParseForm(r); err != nil {
   350  			return err
   351  		}
   352  		if v := r.Form.Get("condition"); v != "" {
   353  			switch container.WaitCondition(v) {
   354  			case container.WaitConditionNotRunning:
   355  				waitCondition = containerpkg.WaitConditionNotRunning
   356  			case container.WaitConditionNextExit:
   357  				waitCondition = containerpkg.WaitConditionNextExit
   358  			case container.WaitConditionRemoved:
   359  				waitCondition = containerpkg.WaitConditionRemoved
   360  				legacyRemovalWaitPre134 = versions.LessThan(version, "1.34")
   361  			default:
   362  				return errdefs.InvalidParameter(errors.Errorf("invalid condition: %q", v))
   363  			}
   364  		}
   365  	}
   366  
   367  	waitC, err := s.backend.ContainerWait(ctx, vars["name"], waitCondition)
   368  	if err != nil {
   369  		return err
   370  	}
   371  
   372  	w.Header().Set("Content-Type", "application/json")
   373  
   374  	if !legacyBehaviorPre130 {
   375  		// Write response header immediately.
   376  		w.WriteHeader(http.StatusOK)
   377  		if flusher, ok := w.(http.Flusher); ok {
   378  			flusher.Flush()
   379  		}
   380  	}
   381  
   382  	// Block on the result of the wait operation.
   383  	status := <-waitC
   384  
   385  	// With API < 1.34, wait on WaitConditionRemoved did not return
   386  	// in case container removal failed. The only way to report an
   387  	// error back to the client is to not write anything (i.e. send
   388  	// an empty response which will be treated as an error).
   389  	if legacyRemovalWaitPre134 && status.Err() != nil {
   390  		return nil
   391  	}
   392  
   393  	var waitError *container.WaitExitError
   394  	if status.Err() != nil {
   395  		waitError = &container.WaitExitError{Message: status.Err().Error()}
   396  	}
   397  
   398  	return json.NewEncoder(w).Encode(&container.WaitResponse{
   399  		StatusCode: int64(status.ExitCode()),
   400  		Error:      waitError,
   401  	})
   402  }
   403  
   404  func (s *containerRouter) getContainersChanges(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   405  	changes, err := s.backend.ContainerChanges(ctx, vars["name"])
   406  	if err != nil {
   407  		return err
   408  	}
   409  
   410  	return httputils.WriteJSON(w, http.StatusOK, changes)
   411  }
   412  
   413  func (s *containerRouter) getContainersTop(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   414  	if err := httputils.ParseForm(r); err != nil {
   415  		return err
   416  	}
   417  
   418  	procList, err := s.backend.ContainerTop(vars["name"], r.Form.Get("ps_args"))
   419  	if err != nil {
   420  		return err
   421  	}
   422  
   423  	return httputils.WriteJSON(w, http.StatusOK, procList)
   424  }
   425  
   426  func (s *containerRouter) postContainerRename(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   427  	if err := httputils.ParseForm(r); err != nil {
   428  		return err
   429  	}
   430  
   431  	name := vars["name"]
   432  	newName := r.Form.Get("name")
   433  	if err := s.backend.ContainerRename(name, newName); err != nil {
   434  		return err
   435  	}
   436  	w.WriteHeader(http.StatusNoContent)
   437  	return nil
   438  }
   439  
   440  func (s *containerRouter) postContainerUpdate(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   441  	if err := httputils.ParseForm(r); err != nil {
   442  		return err
   443  	}
   444  
   445  	var updateConfig container.UpdateConfig
   446  	if err := httputils.ReadJSON(r, &updateConfig); err != nil {
   447  		return err
   448  	}
   449  	if versions.LessThan(httputils.VersionFromContext(ctx), "1.40") {
   450  		updateConfig.PidsLimit = nil
   451  	}
   452  
   453  	if versions.GreaterThanOrEqualTo(httputils.VersionFromContext(ctx), "1.42") {
   454  		// Ignore KernelMemory removed in API 1.42.
   455  		updateConfig.KernelMemory = 0
   456  	}
   457  
   458  	if updateConfig.PidsLimit != nil && *updateConfig.PidsLimit <= 0 {
   459  		// Both `0` and `-1` are accepted to set "unlimited" when updating.
   460  		// Historically, any negative value was accepted, so treat them as
   461  		// "unlimited" as well.
   462  		var unlimited int64
   463  		updateConfig.PidsLimit = &unlimited
   464  	}
   465  
   466  	hostConfig := &container.HostConfig{
   467  		Resources:     updateConfig.Resources,
   468  		RestartPolicy: updateConfig.RestartPolicy,
   469  	}
   470  
   471  	name := vars["name"]
   472  	resp, err := s.backend.ContainerUpdate(name, hostConfig)
   473  	if err != nil {
   474  		return err
   475  	}
   476  
   477  	return httputils.WriteJSON(w, http.StatusOK, resp)
   478  }
   479  
   480  func (s *containerRouter) postContainersCreate(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   481  	if err := httputils.ParseForm(r); err != nil {
   482  		return err
   483  	}
   484  	if err := httputils.CheckForJSON(r); err != nil {
   485  		return err
   486  	}
   487  
   488  	name := r.Form.Get("name")
   489  
   490  	config, hostConfig, networkingConfig, err := s.decoder.DecodeConfig(r.Body)
   491  	if err != nil {
   492  		if errors.Is(err, io.EOF) {
   493  			return errdefs.InvalidParameter(errors.New("invalid JSON: got EOF while reading request body"))
   494  		}
   495  		return err
   496  	}
   497  
   498  	if config == nil {
   499  		return errdefs.InvalidParameter(runconfig.ErrEmptyConfig)
   500  	}
   501  	if hostConfig == nil {
   502  		hostConfig = &container.HostConfig{}
   503  	}
   504  	if hostConfig.NetworkMode == "" {
   505  		hostConfig.NetworkMode = "default"
   506  	}
   507  	if networkingConfig == nil {
   508  		networkingConfig = &network.NetworkingConfig{}
   509  	}
   510  	if networkingConfig.EndpointsConfig == nil {
   511  		networkingConfig.EndpointsConfig = make(map[string]*network.EndpointSettings)
   512  	}
   513  
   514  	version := httputils.VersionFromContext(ctx)
   515  	adjustCPUShares := versions.LessThan(version, "1.19")
   516  
   517  	// When using API 1.24 and under, the client is responsible for removing the container
   518  	if versions.LessThan(version, "1.25") {
   519  		hostConfig.AutoRemove = false
   520  	}
   521  
   522  	if versions.LessThan(version, "1.40") {
   523  		// Ignore BindOptions.NonRecursive because it was added in API 1.40.
   524  		for _, m := range hostConfig.Mounts {
   525  			if bo := m.BindOptions; bo != nil {
   526  				bo.NonRecursive = false
   527  			}
   528  		}
   529  
   530  		// Ignore KernelMemoryTCP because it was added in API 1.40.
   531  		hostConfig.KernelMemoryTCP = 0
   532  
   533  		// Older clients (API < 1.40) expects the default to be shareable, make them happy
   534  		if hostConfig.IpcMode.IsEmpty() {
   535  			hostConfig.IpcMode = container.IPCModeShareable
   536  		}
   537  	}
   538  
   539  	if versions.LessThan(version, "1.41") {
   540  		// Older clients expect the default to be "host" on cgroup v1 hosts
   541  		if !s.cgroup2 && hostConfig.CgroupnsMode.IsEmpty() {
   542  			hostConfig.CgroupnsMode = container.CgroupnsModeHost
   543  		}
   544  	}
   545  
   546  	var platform *ocispec.Platform
   547  	if versions.GreaterThanOrEqualTo(version, "1.41") {
   548  		if v := r.Form.Get("platform"); v != "" {
   549  			p, err := platforms.Parse(v)
   550  			if err != nil {
   551  				return errdefs.InvalidParameter(err)
   552  			}
   553  			platform = &p
   554  		}
   555  	}
   556  
   557  	if versions.LessThan(version, "1.42") {
   558  		for _, m := range hostConfig.Mounts {
   559  			// Ignore BindOptions.CreateMountpoint because it was added in API 1.42.
   560  			if bo := m.BindOptions; bo != nil {
   561  				bo.CreateMountpoint = false
   562  			}
   563  
   564  			// These combinations are invalid, but weren't validated in API < 1.42.
   565  			// We reset them here, so that validation doesn't produce an error.
   566  			if o := m.VolumeOptions; o != nil && m.Type != mount.TypeVolume {
   567  				m.VolumeOptions = nil
   568  			}
   569  			if o := m.TmpfsOptions; o != nil && m.Type != mount.TypeTmpfs {
   570  				m.TmpfsOptions = nil
   571  			}
   572  			if bo := m.BindOptions; bo != nil {
   573  				// Ignore BindOptions.CreateMountpoint because it was added in API 1.42.
   574  				bo.CreateMountpoint = false
   575  			}
   576  		}
   577  
   578  		if runtime.GOOS == "linux" {
   579  			// ConsoleSize is not respected by Linux daemon before API 1.42
   580  			hostConfig.ConsoleSize = [2]uint{0, 0}
   581  		}
   582  	}
   583  
   584  	if versions.GreaterThanOrEqualTo(version, "1.42") {
   585  		// Ignore KernelMemory removed in API 1.42.
   586  		hostConfig.KernelMemory = 0
   587  		for _, m := range hostConfig.Mounts {
   588  			if o := m.VolumeOptions; o != nil && m.Type != mount.TypeVolume {
   589  				return errdefs.InvalidParameter(fmt.Errorf("VolumeOptions must not be specified on mount type %q", m.Type))
   590  			}
   591  			if o := m.BindOptions; o != nil && m.Type != mount.TypeBind {
   592  				return errdefs.InvalidParameter(fmt.Errorf("BindOptions must not be specified on mount type %q", m.Type))
   593  			}
   594  			if o := m.TmpfsOptions; o != nil && m.Type != mount.TypeTmpfs {
   595  				return errdefs.InvalidParameter(fmt.Errorf("TmpfsOptions must not be specified on mount type %q", m.Type))
   596  			}
   597  		}
   598  	}
   599  
   600  	if versions.LessThan(version, "1.43") {
   601  		// Ignore Annotations because it was added in API v1.43.
   602  		hostConfig.Annotations = nil
   603  	}
   604  
   605  	if versions.LessThan(version, "1.44") {
   606  		if config.Healthcheck != nil {
   607  			// StartInterval was added in API 1.44
   608  			config.Healthcheck.StartInterval = 0
   609  		}
   610  
   611  		for _, m := range hostConfig.Mounts {
   612  			if m.BindOptions != nil {
   613  				// Ignore ReadOnlyNonRecursive because it was added in API 1.44.
   614  				m.BindOptions.ReadOnlyNonRecursive = false
   615  				if m.BindOptions.ReadOnlyForceRecursive {
   616  					return errdefs.InvalidParameter(errors.New("BindOptions.ReadOnlyForceRecursive needs API v1.44 or newer"))
   617  				}
   618  			}
   619  		}
   620  
   621  		// Creating a container connected to several networks is not supported until v1.44.
   622  		if len(networkingConfig.EndpointsConfig) > 1 {
   623  			l := make([]string, 0, len(networkingConfig.EndpointsConfig))
   624  			for k := range networkingConfig.EndpointsConfig {
   625  				l = append(l, k)
   626  			}
   627  			return errdefs.InvalidParameter(errors.Errorf("Container cannot be created with multiple network endpoints: %s", strings.Join(l, ", ")))
   628  		}
   629  	}
   630  
   631  	if versions.LessThan(version, "1.45") {
   632  		for _, m := range hostConfig.Mounts {
   633  			if m.VolumeOptions != nil && m.VolumeOptions.Subpath != "" {
   634  				return errdefs.InvalidParameter(errors.New("VolumeOptions.Subpath needs API v1.45 or newer"))
   635  			}
   636  		}
   637  	}
   638  
   639  	var warnings []string
   640  	if warn, err := handleMACAddressBC(config, hostConfig, networkingConfig, version); err != nil {
   641  		return err
   642  	} else if warn != "" {
   643  		warnings = append(warnings, warn)
   644  	}
   645  
   646  	if hostConfig.PidsLimit != nil && *hostConfig.PidsLimit <= 0 {
   647  		// Don't set a limit if either no limit was specified, or "unlimited" was
   648  		// explicitly set.
   649  		// Both `0` and `-1` are accepted as "unlimited", and historically any
   650  		// negative value was accepted, so treat those as "unlimited" as well.
   651  		hostConfig.PidsLimit = nil
   652  	}
   653  
   654  	ccr, err := s.backend.ContainerCreate(ctx, backend.ContainerCreateConfig{
   655  		Name:             name,
   656  		Config:           config,
   657  		HostConfig:       hostConfig,
   658  		NetworkingConfig: networkingConfig,
   659  		AdjustCPUShares:  adjustCPUShares,
   660  		Platform:         platform,
   661  	})
   662  	if err != nil {
   663  		return err
   664  	}
   665  	ccr.Warnings = append(ccr.Warnings, warnings...)
   666  	return httputils.WriteJSON(w, http.StatusCreated, ccr)
   667  }
   668  
   669  // handleMACAddressBC takes care of backward-compatibility for the container-wide MAC address by mutating the
   670  // networkingConfig to set the endpoint-specific MACAddress field introduced in API v1.44. It returns a warning message
   671  // or an error if the container-wide field was specified for API >= v1.44.
   672  func handleMACAddressBC(config *container.Config, hostConfig *container.HostConfig, networkingConfig *network.NetworkingConfig, version string) (string, error) {
   673  	if config.MacAddress == "" { //nolint:staticcheck // ignore SA1019: field is deprecated, but still used on API < v1.44.
   674  		return "", nil
   675  	}
   676  
   677  	deprecatedMacAddress := config.MacAddress //nolint:staticcheck // ignore SA1019: field is deprecated, but still used on API < v1.44.
   678  
   679  	if versions.LessThan(version, "1.44") {
   680  		// The container-wide MacAddress parameter is deprecated and should now be specified in EndpointsConfig.
   681  		if hostConfig.NetworkMode.IsDefault() || hostConfig.NetworkMode.IsBridge() || hostConfig.NetworkMode.IsUserDefined() {
   682  			nwName := hostConfig.NetworkMode.NetworkName()
   683  			if _, ok := networkingConfig.EndpointsConfig[nwName]; !ok {
   684  				networkingConfig.EndpointsConfig[nwName] = &network.EndpointSettings{}
   685  			}
   686  			// Overwrite the config: either the endpoint's MacAddress was set by the user on API < v1.44, which
   687  			// must be ignored, or migrate the top-level MacAddress to the endpoint's config.
   688  			networkingConfig.EndpointsConfig[nwName].MacAddress = deprecatedMacAddress
   689  		}
   690  		if !hostConfig.NetworkMode.IsDefault() && !hostConfig.NetworkMode.IsBridge() && !hostConfig.NetworkMode.IsUserDefined() {
   691  			return "", runconfig.ErrConflictContainerNetworkAndMac
   692  		}
   693  
   694  		return "", nil
   695  	}
   696  
   697  	var warning string
   698  	if hostConfig.NetworkMode.IsDefault() || hostConfig.NetworkMode.IsBridge() || hostConfig.NetworkMode.IsUserDefined() {
   699  		nwName := hostConfig.NetworkMode.NetworkName()
   700  		if _, ok := networkingConfig.EndpointsConfig[nwName]; !ok {
   701  			networkingConfig.EndpointsConfig[nwName] = &network.EndpointSettings{}
   702  		}
   703  
   704  		ep := networkingConfig.EndpointsConfig[nwName]
   705  		if ep.MacAddress == "" {
   706  			ep.MacAddress = deprecatedMacAddress
   707  		} else if ep.MacAddress != deprecatedMacAddress {
   708  			return "", errdefs.InvalidParameter(errors.New("the container-wide MAC address should match the endpoint-specific MAC address for the main network or should be left empty"))
   709  		}
   710  	}
   711  	warning = "The container-wide MacAddress field is now deprecated. It should be specified in EndpointsConfig instead."
   712  	config.MacAddress = "" //nolint:staticcheck // ignore SA1019: field is deprecated, but still used on API < v1.44.
   713  
   714  	return warning, nil
   715  }
   716  
   717  func (s *containerRouter) deleteContainers(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   718  	if err := httputils.ParseForm(r); err != nil {
   719  		return err
   720  	}
   721  
   722  	name := vars["name"]
   723  	config := &backend.ContainerRmConfig{
   724  		ForceRemove:  httputils.BoolValue(r, "force"),
   725  		RemoveVolume: httputils.BoolValue(r, "v"),
   726  		RemoveLink:   httputils.BoolValue(r, "link"),
   727  	}
   728  
   729  	if err := s.backend.ContainerRm(name, config); err != nil {
   730  		return err
   731  	}
   732  
   733  	w.WriteHeader(http.StatusNoContent)
   734  
   735  	return nil
   736  }
   737  
   738  func (s *containerRouter) postContainersResize(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   739  	if err := httputils.ParseForm(r); err != nil {
   740  		return err
   741  	}
   742  
   743  	height, err := strconv.Atoi(r.Form.Get("h"))
   744  	if err != nil {
   745  		return errdefs.InvalidParameter(err)
   746  	}
   747  	width, err := strconv.Atoi(r.Form.Get("w"))
   748  	if err != nil {
   749  		return errdefs.InvalidParameter(err)
   750  	}
   751  
   752  	return s.backend.ContainerResize(vars["name"], height, width)
   753  }
   754  
   755  func (s *containerRouter) postContainersAttach(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   756  	err := httputils.ParseForm(r)
   757  	if err != nil {
   758  		return err
   759  	}
   760  	containerName := vars["name"]
   761  
   762  	_, upgrade := r.Header["Upgrade"]
   763  	detachKeys := r.FormValue("detachKeys")
   764  
   765  	hijacker, ok := w.(http.Hijacker)
   766  	if !ok {
   767  		return errdefs.InvalidParameter(errors.Errorf("error attaching to container %s, hijack connection missing", containerName))
   768  	}
   769  
   770  	contentType := types.MediaTypeRawStream
   771  	setupStreams := func(multiplexed bool) (io.ReadCloser, io.Writer, io.Writer, error) {
   772  		conn, _, err := hijacker.Hijack()
   773  		if err != nil {
   774  			return nil, nil, nil, err
   775  		}
   776  
   777  		// set raw mode
   778  		conn.Write([]byte{})
   779  
   780  		if upgrade {
   781  			if multiplexed && versions.GreaterThanOrEqualTo(httputils.VersionFromContext(ctx), "1.42") {
   782  				contentType = types.MediaTypeMultiplexedStream
   783  			}
   784  			fmt.Fprintf(conn, "HTTP/1.1 101 UPGRADED\r\nContent-Type: "+contentType+"\r\nConnection: Upgrade\r\nUpgrade: tcp\r\n\r\n")
   785  		} else {
   786  			fmt.Fprintf(conn, "HTTP/1.1 200 OK\r\nContent-Type: application/vnd.docker.raw-stream\r\n\r\n")
   787  		}
   788  
   789  		closer := func() error {
   790  			httputils.CloseStreams(conn)
   791  			return nil
   792  		}
   793  		return ioutils.NewReadCloserWrapper(conn, closer), conn, conn, nil
   794  	}
   795  
   796  	attachConfig := &backend.ContainerAttachConfig{
   797  		GetStreams: setupStreams,
   798  		UseStdin:   httputils.BoolValue(r, "stdin"),
   799  		UseStdout:  httputils.BoolValue(r, "stdout"),
   800  		UseStderr:  httputils.BoolValue(r, "stderr"),
   801  		Logs:       httputils.BoolValue(r, "logs"),
   802  		Stream:     httputils.BoolValue(r, "stream"),
   803  		DetachKeys: detachKeys,
   804  		MuxStreams: true,
   805  	}
   806  
   807  	if err = s.backend.ContainerAttach(containerName, attachConfig); err != nil {
   808  		log.G(ctx).WithError(err).Errorf("Handler for %s %s returned error", r.Method, r.URL.Path)
   809  		// Remember to close stream if error happens
   810  		conn, _, errHijack := hijacker.Hijack()
   811  		if errHijack != nil {
   812  			log.G(ctx).WithError(err).Errorf("Handler for %s %s: unable to close stream; error when hijacking connection", r.Method, r.URL.Path)
   813  		} else {
   814  			statusCode := httpstatus.FromError(err)
   815  			statusText := http.StatusText(statusCode)
   816  			fmt.Fprintf(conn, "HTTP/1.1 %d %s\r\nContent-Type: %s\r\n\r\n%s\r\n", statusCode, statusText, contentType, err.Error())
   817  			httputils.CloseStreams(conn)
   818  		}
   819  	}
   820  	return nil
   821  }
   822  
   823  func (s *containerRouter) wsContainersAttach(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   824  	if err := httputils.ParseForm(r); err != nil {
   825  		return err
   826  	}
   827  	containerName := vars["name"]
   828  
   829  	var err error
   830  	detachKeys := r.FormValue("detachKeys")
   831  
   832  	done := make(chan struct{})
   833  	started := make(chan struct{})
   834  
   835  	version := httputils.VersionFromContext(ctx)
   836  
   837  	setupStreams := func(multiplexed bool) (io.ReadCloser, io.Writer, io.Writer, error) {
   838  		wsChan := make(chan *websocket.Conn)
   839  		h := func(conn *websocket.Conn) {
   840  			wsChan <- conn
   841  			<-done
   842  		}
   843  
   844  		srv := websocket.Server{Handler: h, Handshake: nil}
   845  		go func() {
   846  			close(started)
   847  			srv.ServeHTTP(w, r)
   848  		}()
   849  
   850  		conn := <-wsChan
   851  		// In case version 1.28 and above, a binary frame will be sent.
   852  		// See 28176 for details.
   853  		if versions.GreaterThanOrEqualTo(version, "1.28") {
   854  			conn.PayloadType = websocket.BinaryFrame
   855  		}
   856  		return conn, conn, conn, nil
   857  	}
   858  
   859  	useStdin, useStdout, useStderr := true, true, true
   860  	if versions.GreaterThanOrEqualTo(version, "1.42") {
   861  		useStdin = httputils.BoolValue(r, "stdin")
   862  		useStdout = httputils.BoolValue(r, "stdout")
   863  		useStderr = httputils.BoolValue(r, "stderr")
   864  	}
   865  
   866  	attachConfig := &backend.ContainerAttachConfig{
   867  		GetStreams: setupStreams,
   868  		UseStdin:   useStdin,
   869  		UseStdout:  useStdout,
   870  		UseStderr:  useStderr,
   871  		Logs:       httputils.BoolValue(r, "logs"),
   872  		Stream:     httputils.BoolValue(r, "stream"),
   873  		DetachKeys: detachKeys,
   874  		MuxStreams: false, // never multiplex, as we rely on websocket to manage distinct streams
   875  	}
   876  
   877  	err = s.backend.ContainerAttach(containerName, attachConfig)
   878  	close(done)
   879  	select {
   880  	case <-started:
   881  		if err != nil {
   882  			log.G(ctx).Errorf("Error attaching websocket: %s", err)
   883  		} else {
   884  			log.G(ctx).Debug("websocket connection was closed by client")
   885  		}
   886  		return nil
   887  	default:
   888  	}
   889  	return err
   890  }
   891  
   892  func (s *containerRouter) postContainersPrune(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   893  	if err := httputils.ParseForm(r); err != nil {
   894  		return err
   895  	}
   896  
   897  	pruneFilters, err := filters.FromJSON(r.Form.Get("filters"))
   898  	if err != nil {
   899  		return err
   900  	}
   901  
   902  	pruneReport, err := s.backend.ContainersPrune(ctx, pruneFilters)
   903  	if err != nil {
   904  		return err
   905  	}
   906  	return httputils.WriteJSON(w, http.StatusOK, pruneReport)
   907  }