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 }