container_routes.go 28 KB

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