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