github.com/khulnasoft-lab/khulnasoft@v26.0.1-0.20240328202558-330a6f959fe0+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 hostConfig.NetworkMode == "" {
   460  		hostConfig.NetworkMode = "default"
   461  	}
   462  	if networkingConfig == nil {
   463  		networkingConfig = &network.NetworkingConfig{}
   464  	}
   465  	if networkingConfig.EndpointsConfig == nil {
   466  		networkingConfig.EndpointsConfig = make(map[string]*network.EndpointSettings)
   467  	}
   468  
   469  	version := httputils.VersionFromContext(ctx)
   470  
   471  	// When using API 1.24 and under, the client is responsible for removing the container
   472  	if versions.LessThan(version, "1.25") {
   473  		hostConfig.AutoRemove = false
   474  	}
   475  
   476  	if versions.LessThan(version, "1.40") {
   477  		// Ignore BindOptions.NonRecursive because it was added in API 1.40.
   478  		for _, m := range hostConfig.Mounts {
   479  			if bo := m.BindOptions; bo != nil {
   480  				bo.NonRecursive = false
   481  			}
   482  		}
   483  
   484  		// Ignore KernelMemoryTCP because it was added in API 1.40.
   485  		hostConfig.KernelMemoryTCP = 0
   486  
   487  		// Older clients (API < 1.40) expects the default to be shareable, make them happy
   488  		if hostConfig.IpcMode.IsEmpty() {
   489  			hostConfig.IpcMode = container.IPCModeShareable
   490  		}
   491  	}
   492  
   493  	if versions.LessThan(version, "1.41") {
   494  		// Older clients expect the default to be "host" on cgroup v1 hosts
   495  		if !s.cgroup2 && hostConfig.CgroupnsMode.IsEmpty() {
   496  			hostConfig.CgroupnsMode = container.CgroupnsModeHost
   497  		}
   498  	}
   499  
   500  	var platform *ocispec.Platform
   501  	if versions.GreaterThanOrEqualTo(version, "1.41") {
   502  		if v := r.Form.Get("platform"); v != "" {
   503  			p, err := platforms.Parse(v)
   504  			if err != nil {
   505  				return errdefs.InvalidParameter(err)
   506  			}
   507  			platform = &p
   508  		}
   509  	}
   510  
   511  	if versions.LessThan(version, "1.42") {
   512  		for _, m := range hostConfig.Mounts {
   513  			// Ignore BindOptions.CreateMountpoint because it was added in API 1.42.
   514  			if bo := m.BindOptions; bo != nil {
   515  				bo.CreateMountpoint = false
   516  			}
   517  
   518  			// These combinations are invalid, but weren't validated in API < 1.42.
   519  			// We reset them here, so that validation doesn't produce an error.
   520  			if o := m.VolumeOptions; o != nil && m.Type != mount.TypeVolume {
   521  				m.VolumeOptions = nil
   522  			}
   523  			if o := m.TmpfsOptions; o != nil && m.Type != mount.TypeTmpfs {
   524  				m.TmpfsOptions = nil
   525  			}
   526  			if bo := m.BindOptions; bo != nil {
   527  				// Ignore BindOptions.CreateMountpoint because it was added in API 1.42.
   528  				bo.CreateMountpoint = false
   529  			}
   530  		}
   531  
   532  		if runtime.GOOS == "linux" {
   533  			// ConsoleSize is not respected by Linux daemon before API 1.42
   534  			hostConfig.ConsoleSize = [2]uint{0, 0}
   535  		}
   536  	}
   537  
   538  	if versions.GreaterThanOrEqualTo(version, "1.42") {
   539  		// Ignore KernelMemory removed in API 1.42.
   540  		hostConfig.KernelMemory = 0
   541  		for _, m := range hostConfig.Mounts {
   542  			if o := m.VolumeOptions; o != nil && m.Type != mount.TypeVolume {
   543  				return errdefs.InvalidParameter(fmt.Errorf("VolumeOptions must not be specified on mount type %q", m.Type))
   544  			}
   545  			if o := m.BindOptions; o != nil && m.Type != mount.TypeBind {
   546  				return errdefs.InvalidParameter(fmt.Errorf("BindOptions must not be specified on mount type %q", m.Type))
   547  			}
   548  			if o := m.TmpfsOptions; o != nil && m.Type != mount.TypeTmpfs {
   549  				return errdefs.InvalidParameter(fmt.Errorf("TmpfsOptions must not be specified on mount type %q", m.Type))
   550  			}
   551  		}
   552  	}
   553  
   554  	if versions.LessThan(version, "1.43") {
   555  		// Ignore Annotations because it was added in API v1.43.
   556  		hostConfig.Annotations = nil
   557  	}
   558  
   559  	defaultReadOnlyNonRecursive := false
   560  	if versions.LessThan(version, "1.44") {
   561  		if config.Healthcheck != nil {
   562  			// StartInterval was added in API 1.44
   563  			config.Healthcheck.StartInterval = 0
   564  		}
   565  
   566  		// Set ReadOnlyNonRecursive to true because it was added in API 1.44
   567  		// Before that all read-only mounts were non-recursive.
   568  		// Keep that behavior for clients on older APIs.
   569  		defaultReadOnlyNonRecursive = true
   570  
   571  		for _, m := range hostConfig.Mounts {
   572  			if m.Type == mount.TypeBind {
   573  				if m.BindOptions != nil && m.BindOptions.ReadOnlyForceRecursive {
   574  					// NOTE: that technically this is a breaking change for older
   575  					// API versions, and we should ignore the new field.
   576  					// However, this option may be incorrectly set by a client with
   577  					// the expectation that the failing to apply recursive read-only
   578  					// is enforced, so we decided to produce an error instead,
   579  					// instead of silently ignoring.
   580  					return errdefs.InvalidParameter(errors.New("BindOptions.ReadOnlyForceRecursive needs API v1.44 or newer"))
   581  				}
   582  			}
   583  		}
   584  
   585  		// Creating a container connected to several networks is not supported until v1.44.
   586  		if len(networkingConfig.EndpointsConfig) > 1 {
   587  			l := make([]string, 0, len(networkingConfig.EndpointsConfig))
   588  			for k := range networkingConfig.EndpointsConfig {
   589  				l = append(l, k)
   590  			}
   591  			return errdefs.InvalidParameter(errors.Errorf("Container cannot be created with multiple network endpoints: %s", strings.Join(l, ", ")))
   592  		}
   593  	}
   594  
   595  	if versions.LessThan(version, "1.45") {
   596  		for _, m := range hostConfig.Mounts {
   597  			if m.VolumeOptions != nil && m.VolumeOptions.Subpath != "" {
   598  				return errdefs.InvalidParameter(errors.New("VolumeOptions.Subpath needs API v1.45 or newer"))
   599  			}
   600  		}
   601  	}
   602  
   603  	var warnings []string
   604  	if warn, err := handleMACAddressBC(config, hostConfig, networkingConfig, version); err != nil {
   605  		return err
   606  	} else if warn != "" {
   607  		warnings = append(warnings, warn)
   608  	}
   609  
   610  	if hostConfig.PidsLimit != nil && *hostConfig.PidsLimit <= 0 {
   611  		// Don't set a limit if either no limit was specified, or "unlimited" was
   612  		// explicitly set.
   613  		// Both `0` and `-1` are accepted as "unlimited", and historically any
   614  		// negative value was accepted, so treat those as "unlimited" as well.
   615  		hostConfig.PidsLimit = nil
   616  	}
   617  
   618  	ccr, err := s.backend.ContainerCreate(ctx, backend.ContainerCreateConfig{
   619  		Name:                        name,
   620  		Config:                      config,
   621  		HostConfig:                  hostConfig,
   622  		NetworkingConfig:            networkingConfig,
   623  		Platform:                    platform,
   624  		DefaultReadOnlyNonRecursive: defaultReadOnlyNonRecursive,
   625  	})
   626  	if err != nil {
   627  		return err
   628  	}
   629  	ccr.Warnings = append(ccr.Warnings, warnings...)
   630  	return httputils.WriteJSON(w, http.StatusCreated, ccr)
   631  }
   632  
   633  // handleMACAddressBC takes care of backward-compatibility for the container-wide MAC address by mutating the
   634  // networkingConfig to set the endpoint-specific MACAddress field introduced in API v1.44. It returns a warning message
   635  // or an error if the container-wide field was specified for API >= v1.44.
   636  func handleMACAddressBC(config *container.Config, hostConfig *container.HostConfig, networkingConfig *network.NetworkingConfig, version string) (string, error) {
   637  	deprecatedMacAddress := config.MacAddress //nolint:staticcheck // ignore SA1019: field is deprecated, but still used on API < v1.44.
   638  
   639  	// For older versions of the API, migrate the container-wide MAC address to EndpointsConfig.
   640  	if versions.LessThan(version, "1.44") {
   641  		if deprecatedMacAddress == "" {
   642  			// If a MAC address is supplied in EndpointsConfig, discard it because the old API
   643  			// would have ignored it.
   644  			for _, ep := range networkingConfig.EndpointsConfig {
   645  				ep.MacAddress = ""
   646  			}
   647  			return "", nil
   648  		}
   649  		if !hostConfig.NetworkMode.IsDefault() && !hostConfig.NetworkMode.IsBridge() && !hostConfig.NetworkMode.IsUserDefined() {
   650  			return "", runconfig.ErrConflictContainerNetworkAndMac
   651  		}
   652  
   653  		// There cannot be more than one entry in EndpointsConfig with API < 1.44.
   654  
   655  		// If there's no EndpointsConfig, create a place to store the configured address. It is
   656  		// safe to use NetworkMode as the network name, whether it's a name or id/short-id, as
   657  		// it will be normalised later and there is no other EndpointSettings object that might
   658  		// refer to this network/endpoint.
   659  		if len(networkingConfig.EndpointsConfig) == 0 {
   660  			nwName := hostConfig.NetworkMode.NetworkName()
   661  			networkingConfig.EndpointsConfig[nwName] = &network.EndpointSettings{}
   662  		}
   663  		// There's exactly one network in EndpointsConfig, either from the API or just-created.
   664  		// Migrate the container-wide setting to it.
   665  		// No need to check for a match between NetworkMode and the names/ids in EndpointsConfig,
   666  		// the old version of the API would have applied the address to this network anyway.
   667  		for _, ep := range networkingConfig.EndpointsConfig {
   668  			ep.MacAddress = deprecatedMacAddress
   669  		}
   670  		return "", nil
   671  	}
   672  
   673  	// The container-wide MacAddress parameter is deprecated and should now be specified in EndpointsConfig.
   674  	if deprecatedMacAddress == "" {
   675  		return "", nil
   676  	}
   677  	var warning string
   678  	if hostConfig.NetworkMode.IsDefault() || hostConfig.NetworkMode.IsBridge() || hostConfig.NetworkMode.IsUserDefined() {
   679  		nwName := hostConfig.NetworkMode.NetworkName()
   680  		// If there's no endpoint config, create a place to store the configured address.
   681  		if len(networkingConfig.EndpointsConfig) == 0 {
   682  			networkingConfig.EndpointsConfig[nwName] = &network.EndpointSettings{
   683  				MacAddress: deprecatedMacAddress,
   684  			}
   685  		} else {
   686  			// There is existing endpoint config - if it's not indexed by NetworkMode.Name(), we
   687  			// can't tell which network the container-wide settings was intended for. NetworkMode,
   688  			// the keys in EndpointsConfig and the NetworkID in EndpointsConfig may mix network
   689  			// name/id/short-id. It's not safe to create EndpointsConfig under the NetworkMode
   690  			// name to store the container-wide MAC address, because that may result in two sets
   691  			// of EndpointsConfig for the same network and one set will be discarded later. So,
   692  			// reject the request ...
   693  			ep, ok := networkingConfig.EndpointsConfig[nwName]
   694  			if !ok {
   695  				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"))
   696  			}
   697  			// ep is the endpoint that needs the container-wide MAC address; migrate the address
   698  			// to it, or bail out if there's a mismatch.
   699  			if ep.MacAddress == "" {
   700  				ep.MacAddress = deprecatedMacAddress
   701  			} else if ep.MacAddress != deprecatedMacAddress {
   702  				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"))
   703  			}
   704  		}
   705  	}
   706  	warning = "The container-wide MacAddress field is now deprecated. It should be specified in EndpointsConfig instead."
   707  	config.MacAddress = "" //nolint:staticcheck // ignore SA1019: field is deprecated, but still used on API < v1.44.
   708  
   709  	return warning, nil
   710  }
   711  
   712  func (s *containerRouter) deleteContainers(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   713  	if err := httputils.ParseForm(r); err != nil {
   714  		return err
   715  	}
   716  
   717  	name := vars["name"]
   718  	config := &backend.ContainerRmConfig{
   719  		ForceRemove:  httputils.BoolValue(r, "force"),
   720  		RemoveVolume: httputils.BoolValue(r, "v"),
   721  		RemoveLink:   httputils.BoolValue(r, "link"),
   722  	}
   723  
   724  	if err := s.backend.ContainerRm(name, config); err != nil {
   725  		return err
   726  	}
   727  
   728  	w.WriteHeader(http.StatusNoContent)
   729  
   730  	return nil
   731  }
   732  
   733  func (s *containerRouter) postContainersResize(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   734  	if err := httputils.ParseForm(r); err != nil {
   735  		return err
   736  	}
   737  
   738  	height, err := strconv.Atoi(r.Form.Get("h"))
   739  	if err != nil {
   740  		return errdefs.InvalidParameter(err)
   741  	}
   742  	width, err := strconv.Atoi(r.Form.Get("w"))
   743  	if err != nil {
   744  		return errdefs.InvalidParameter(err)
   745  	}
   746  
   747  	return s.backend.ContainerResize(vars["name"], height, width)
   748  }
   749  
   750  func (s *containerRouter) postContainersAttach(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   751  	err := httputils.ParseForm(r)
   752  	if err != nil {
   753  		return err
   754  	}
   755  	containerName := vars["name"]
   756  
   757  	_, upgrade := r.Header["Upgrade"]
   758  	detachKeys := r.FormValue("detachKeys")
   759  
   760  	hijacker, ok := w.(http.Hijacker)
   761  	if !ok {
   762  		return errdefs.InvalidParameter(errors.Errorf("error attaching to container %s, hijack connection missing", containerName))
   763  	}
   764  
   765  	contentType := types.MediaTypeRawStream
   766  	setupStreams := func(multiplexed bool) (io.ReadCloser, io.Writer, io.Writer, error) {
   767  		conn, _, err := hijacker.Hijack()
   768  		if err != nil {
   769  			return nil, nil, nil, err
   770  		}
   771  
   772  		// set raw mode
   773  		conn.Write([]byte{})
   774  
   775  		if upgrade {
   776  			if multiplexed && versions.GreaterThanOrEqualTo(httputils.VersionFromContext(ctx), "1.42") {
   777  				contentType = types.MediaTypeMultiplexedStream
   778  			}
   779  			fmt.Fprintf(conn, "HTTP/1.1 101 UPGRADED\r\nContent-Type: "+contentType+"\r\nConnection: Upgrade\r\nUpgrade: tcp\r\n\r\n")
   780  		} else {
   781  			fmt.Fprintf(conn, "HTTP/1.1 200 OK\r\nContent-Type: application/vnd.docker.raw-stream\r\n\r\n")
   782  		}
   783  
   784  		closer := func() error {
   785  			httputils.CloseStreams(conn)
   786  			return nil
   787  		}
   788  		return ioutils.NewReadCloserWrapper(conn, closer), conn, conn, nil
   789  	}
   790  
   791  	attachConfig := &backend.ContainerAttachConfig{
   792  		GetStreams: setupStreams,
   793  		UseStdin:   httputils.BoolValue(r, "stdin"),
   794  		UseStdout:  httputils.BoolValue(r, "stdout"),
   795  		UseStderr:  httputils.BoolValue(r, "stderr"),
   796  		Logs:       httputils.BoolValue(r, "logs"),
   797  		Stream:     httputils.BoolValue(r, "stream"),
   798  		DetachKeys: detachKeys,
   799  		MuxStreams: true,
   800  	}
   801  
   802  	if err = s.backend.ContainerAttach(containerName, attachConfig); err != nil {
   803  		log.G(ctx).WithError(err).Errorf("Handler for %s %s returned error", r.Method, r.URL.Path)
   804  		// Remember to close stream if error happens
   805  		conn, _, errHijack := hijacker.Hijack()
   806  		if errHijack != nil {
   807  			log.G(ctx).WithError(err).Errorf("Handler for %s %s: unable to close stream; error when hijacking connection", r.Method, r.URL.Path)
   808  		} else {
   809  			statusCode := httpstatus.FromError(err)
   810  			statusText := http.StatusText(statusCode)
   811  			fmt.Fprintf(conn, "HTTP/1.1 %d %s\r\nContent-Type: %s\r\n\r\n%s\r\n", statusCode, statusText, contentType, err.Error())
   812  			httputils.CloseStreams(conn)
   813  		}
   814  	}
   815  	return nil
   816  }
   817  
   818  func (s *containerRouter) wsContainersAttach(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   819  	if err := httputils.ParseForm(r); err != nil {
   820  		return err
   821  	}
   822  	containerName := vars["name"]
   823  
   824  	var err error
   825  	detachKeys := r.FormValue("detachKeys")
   826  
   827  	done := make(chan struct{})
   828  	started := make(chan struct{})
   829  
   830  	version := httputils.VersionFromContext(ctx)
   831  
   832  	setupStreams := func(multiplexed bool) (io.ReadCloser, io.Writer, io.Writer, error) {
   833  		wsChan := make(chan *websocket.Conn)
   834  		h := func(conn *websocket.Conn) {
   835  			wsChan <- conn
   836  			<-done
   837  		}
   838  
   839  		srv := websocket.Server{Handler: h, Handshake: nil}
   840  		go func() {
   841  			close(started)
   842  			srv.ServeHTTP(w, r)
   843  		}()
   844  
   845  		conn := <-wsChan
   846  		// In case version 1.28 and above, a binary frame will be sent.
   847  		// See 28176 for details.
   848  		if versions.GreaterThanOrEqualTo(version, "1.28") {
   849  			conn.PayloadType = websocket.BinaryFrame
   850  		}
   851  		return conn, conn, conn, nil
   852  	}
   853  
   854  	useStdin, useStdout, useStderr := true, true, true
   855  	if versions.GreaterThanOrEqualTo(version, "1.42") {
   856  		useStdin = httputils.BoolValue(r, "stdin")
   857  		useStdout = httputils.BoolValue(r, "stdout")
   858  		useStderr = httputils.BoolValue(r, "stderr")
   859  	}
   860  
   861  	attachConfig := &backend.ContainerAttachConfig{
   862  		GetStreams: setupStreams,
   863  		UseStdin:   useStdin,
   864  		UseStdout:  useStdout,
   865  		UseStderr:  useStderr,
   866  		Logs:       httputils.BoolValue(r, "logs"),
   867  		Stream:     httputils.BoolValue(r, "stream"),
   868  		DetachKeys: detachKeys,
   869  		MuxStreams: false, // never multiplex, as we rely on websocket to manage distinct streams
   870  	}
   871  
   872  	err = s.backend.ContainerAttach(containerName, attachConfig)
   873  	close(done)
   874  	select {
   875  	case <-started:
   876  		if err != nil {
   877  			log.G(ctx).Errorf("Error attaching websocket: %s", err)
   878  		} else {
   879  			log.G(ctx).Debug("websocket connection was closed by client")
   880  		}
   881  		return nil
   882  	default:
   883  	}
   884  	return err
   885  }
   886  
   887  func (s *containerRouter) postContainersPrune(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   888  	if err := httputils.ParseForm(r); err != nil {
   889  		return err
   890  	}
   891  
   892  	pruneFilters, err := filters.FromJSON(r.Form.Get("filters"))
   893  	if err != nil {
   894  		return err
   895  	}
   896  
   897  	pruneReport, err := s.backend.ContainersPrune(ctx, pruneFilters)
   898  	if err != nil {
   899  		return err
   900  	}
   901  	return httputils.WriteJSON(w, http.StatusOK, pruneReport)
   902  }