container_routes.go 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528
  1. package container
  2. import (
  3. "encoding/json"
  4. "fmt"
  5. "io"
  6. "net/http"
  7. "strconv"
  8. "strings"
  9. "syscall"
  10. "time"
  11. "github.com/Sirupsen/logrus"
  12. "github.com/docker/distribution/registry/api/errcode"
  13. "github.com/docker/docker/api/server/httputils"
  14. "github.com/docker/docker/api/types/backend"
  15. derr "github.com/docker/docker/errors"
  16. "github.com/docker/docker/pkg/ioutils"
  17. "github.com/docker/docker/pkg/signal"
  18. "github.com/docker/docker/pkg/term"
  19. "github.com/docker/docker/runconfig"
  20. "github.com/docker/docker/utils"
  21. "github.com/docker/engine-api/types"
  22. "github.com/docker/engine-api/types/container"
  23. "github.com/docker/engine-api/types/filters"
  24. "golang.org/x/net/context"
  25. "golang.org/x/net/websocket"
  26. )
  27. func (s *containerRouter) getContainersJSON(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
  28. if err := httputils.ParseForm(r); err != nil {
  29. return err
  30. }
  31. filter, err := filters.FromParam(r.Form.Get("filters"))
  32. if err != nil {
  33. return err
  34. }
  35. config := &types.ContainerListOptions{
  36. All: httputils.BoolValue(r, "all"),
  37. Size: httputils.BoolValue(r, "size"),
  38. Since: r.Form.Get("since"),
  39. Before: r.Form.Get("before"),
  40. Filter: filter,
  41. }
  42. if tmpLimit := r.Form.Get("limit"); tmpLimit != "" {
  43. limit, err := strconv.Atoi(tmpLimit)
  44. if err != nil {
  45. return err
  46. }
  47. config.Limit = limit
  48. }
  49. containers, err := s.backend.Containers(config)
  50. if err != nil {
  51. return err
  52. }
  53. return httputils.WriteJSON(w, http.StatusOK, containers)
  54. }
  55. func (s *containerRouter) getContainersStats(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
  56. if err := httputils.ParseForm(r); err != nil {
  57. return err
  58. }
  59. stream := httputils.BoolValueOrDefault(r, "stream", true)
  60. if !stream {
  61. w.Header().Set("Content-Type", "application/json")
  62. }
  63. var closeNotifier <-chan bool
  64. if notifier, ok := w.(http.CloseNotifier); ok {
  65. closeNotifier = notifier.CloseNotify()
  66. }
  67. config := &backend.ContainerStatsConfig{
  68. Stream: stream,
  69. OutStream: w,
  70. Stop: closeNotifier,
  71. Version: string(httputils.VersionFromContext(ctx)),
  72. }
  73. return s.backend.ContainerStats(vars["name"], config)
  74. }
  75. func (s *containerRouter) getContainersLogs(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
  76. if err := httputils.ParseForm(r); err != nil {
  77. return err
  78. }
  79. // Args are validated before the stream starts because when it starts we're
  80. // sending HTTP 200 by writing an empty chunk of data to tell the client that
  81. // daemon is going to stream. By sending this initial HTTP 200 we can't report
  82. // any error after the stream starts (i.e. container not found, wrong parameters)
  83. // with the appropriate status code.
  84. stdout, stderr := httputils.BoolValue(r, "stdout"), httputils.BoolValue(r, "stderr")
  85. if !(stdout || stderr) {
  86. return fmt.Errorf("Bad parameters: you must choose at least one stream")
  87. }
  88. var closeNotifier <-chan bool
  89. if notifier, ok := w.(http.CloseNotifier); ok {
  90. closeNotifier = notifier.CloseNotify()
  91. }
  92. containerName := vars["name"]
  93. logsConfig := &backend.ContainerLogsConfig{
  94. ContainerLogsOptions: types.ContainerLogsOptions{
  95. Follow: httputils.BoolValue(r, "follow"),
  96. Timestamps: httputils.BoolValue(r, "timestamps"),
  97. Since: r.Form.Get("since"),
  98. Tail: r.Form.Get("tail"),
  99. ShowStdout: stdout,
  100. ShowStderr: stderr,
  101. },
  102. OutStream: w,
  103. Stop: closeNotifier,
  104. }
  105. chStarted := make(chan struct{})
  106. if err := s.backend.ContainerLogs(containerName, logsConfig, chStarted); err != nil {
  107. select {
  108. case <-chStarted:
  109. // The client may be expecting all of the data we're sending to
  110. // be multiplexed, so send it through OutStream, which will
  111. // have been set up to handle that if needed.
  112. fmt.Fprintf(logsConfig.OutStream, "Error running logs job: %s\n", utils.GetErrorMessage(err))
  113. default:
  114. return err
  115. }
  116. }
  117. return nil
  118. }
  119. func (s *containerRouter) getContainersExport(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
  120. return s.backend.ContainerExport(vars["name"], w)
  121. }
  122. func (s *containerRouter) postContainersStart(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
  123. // If contentLength is -1, we can assumed chunked encoding
  124. // or more technically that the length is unknown
  125. // https://golang.org/src/pkg/net/http/request.go#L139
  126. // net/http otherwise seems to swallow any headers related to chunked encoding
  127. // including r.TransferEncoding
  128. // allow a nil body for backwards compatibility
  129. var hostConfig *container.HostConfig
  130. if r.Body != nil && (r.ContentLength > 0 || r.ContentLength == -1) {
  131. if err := httputils.CheckForJSON(r); err != nil {
  132. return err
  133. }
  134. c, err := runconfig.DecodeHostConfig(r.Body)
  135. if err != nil {
  136. return err
  137. }
  138. hostConfig = c
  139. }
  140. if err := s.backend.ContainerStart(vars["name"], hostConfig); err != nil {
  141. return err
  142. }
  143. w.WriteHeader(http.StatusNoContent)
  144. return nil
  145. }
  146. func (s *containerRouter) postContainersStop(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
  147. if err := httputils.ParseForm(r); err != nil {
  148. return err
  149. }
  150. seconds, _ := strconv.Atoi(r.Form.Get("t"))
  151. if err := s.backend.ContainerStop(vars["name"], seconds); err != nil {
  152. return err
  153. }
  154. w.WriteHeader(http.StatusNoContent)
  155. return nil
  156. }
  157. func (s *containerRouter) postContainersKill(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
  158. if err := httputils.ParseForm(r); err != nil {
  159. return err
  160. }
  161. var sig syscall.Signal
  162. name := vars["name"]
  163. // If we have a signal, look at it. Otherwise, do nothing
  164. if sigStr := r.Form.Get("signal"); sigStr != "" {
  165. var err error
  166. if sig, err = signal.ParseSignal(sigStr); err != nil {
  167. return err
  168. }
  169. }
  170. if err := s.backend.ContainerKill(name, uint64(sig)); err != nil {
  171. theErr, isDerr := err.(errcode.ErrorCoder)
  172. isStopped := isDerr && theErr.ErrorCode() == derr.ErrorCodeNotRunning
  173. // Return error that's not caused because the container is stopped.
  174. // Return error if the container is not running and the api is >= 1.20
  175. // to keep backwards compatibility.
  176. version := httputils.VersionFromContext(ctx)
  177. if version.GreaterThanOrEqualTo("1.20") || !isStopped {
  178. return fmt.Errorf("Cannot kill container %s: %v", name, utils.GetErrorMessage(err))
  179. }
  180. }
  181. w.WriteHeader(http.StatusNoContent)
  182. return nil
  183. }
  184. func (s *containerRouter) postContainersRestart(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
  185. if err := httputils.ParseForm(r); err != nil {
  186. return err
  187. }
  188. timeout, _ := strconv.Atoi(r.Form.Get("t"))
  189. if err := s.backend.ContainerRestart(vars["name"], timeout); err != nil {
  190. return err
  191. }
  192. w.WriteHeader(http.StatusNoContent)
  193. return nil
  194. }
  195. func (s *containerRouter) postContainersPause(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
  196. if err := httputils.ParseForm(r); err != nil {
  197. return err
  198. }
  199. if err := s.backend.ContainerPause(vars["name"]); err != nil {
  200. return err
  201. }
  202. w.WriteHeader(http.StatusNoContent)
  203. return nil
  204. }
  205. func (s *containerRouter) postContainersUnpause(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
  206. if err := httputils.ParseForm(r); err != nil {
  207. return err
  208. }
  209. if err := s.backend.ContainerUnpause(vars["name"]); err != nil {
  210. return err
  211. }
  212. w.WriteHeader(http.StatusNoContent)
  213. return nil
  214. }
  215. func (s *containerRouter) postContainersWait(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
  216. status, err := s.backend.ContainerWait(vars["name"], -1*time.Second)
  217. if err != nil {
  218. return err
  219. }
  220. return httputils.WriteJSON(w, http.StatusOK, &types.ContainerWaitResponse{
  221. StatusCode: status,
  222. })
  223. }
  224. func (s *containerRouter) getContainersChanges(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
  225. changes, err := s.backend.ContainerChanges(vars["name"])
  226. if err != nil {
  227. return err
  228. }
  229. return httputils.WriteJSON(w, http.StatusOK, changes)
  230. }
  231. func (s *containerRouter) getContainersTop(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
  232. if err := httputils.ParseForm(r); err != nil {
  233. return err
  234. }
  235. procList, err := s.backend.ContainerTop(vars["name"], r.Form.Get("ps_args"))
  236. if err != nil {
  237. return err
  238. }
  239. return httputils.WriteJSON(w, http.StatusOK, procList)
  240. }
  241. func (s *containerRouter) postContainerRename(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
  242. if err := httputils.ParseForm(r); err != nil {
  243. return err
  244. }
  245. name := vars["name"]
  246. newName := r.Form.Get("name")
  247. if err := s.backend.ContainerRename(name, newName); err != nil {
  248. return err
  249. }
  250. w.WriteHeader(http.StatusNoContent)
  251. return nil
  252. }
  253. func (s *containerRouter) postContainerUpdate(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
  254. if err := httputils.ParseForm(r); err != nil {
  255. return err
  256. }
  257. if err := httputils.CheckForJSON(r); err != nil {
  258. return err
  259. }
  260. var updateConfig container.UpdateConfig
  261. decoder := json.NewDecoder(r.Body)
  262. if err := decoder.Decode(&updateConfig); err != nil {
  263. return err
  264. }
  265. hostConfig := &container.HostConfig{
  266. Resources: updateConfig.Resources,
  267. RestartPolicy: updateConfig.RestartPolicy,
  268. }
  269. name := vars["name"]
  270. warnings, err := s.backend.ContainerUpdate(name, hostConfig)
  271. if err != nil {
  272. return err
  273. }
  274. return httputils.WriteJSON(w, http.StatusOK, &types.ContainerUpdateResponse{
  275. Warnings: warnings,
  276. })
  277. }
  278. func (s *containerRouter) postContainersCreate(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
  279. if err := httputils.ParseForm(r); err != nil {
  280. return err
  281. }
  282. if err := httputils.CheckForJSON(r); err != nil {
  283. return err
  284. }
  285. name := r.Form.Get("name")
  286. config, hostConfig, networkingConfig, err := runconfig.DecodeContainerConfig(r.Body)
  287. if err != nil {
  288. return err
  289. }
  290. version := httputils.VersionFromContext(ctx)
  291. adjustCPUShares := version.LessThan("1.19")
  292. ccr, err := s.backend.ContainerCreate(types.ContainerCreateConfig{
  293. Name: name,
  294. Config: config,
  295. HostConfig: hostConfig,
  296. NetworkingConfig: networkingConfig,
  297. AdjustCPUShares: adjustCPUShares,
  298. })
  299. if err != nil {
  300. return err
  301. }
  302. return httputils.WriteJSON(w, http.StatusCreated, ccr)
  303. }
  304. func (s *containerRouter) deleteContainers(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
  305. if err := httputils.ParseForm(r); err != nil {
  306. return err
  307. }
  308. name := vars["name"]
  309. config := &types.ContainerRmConfig{
  310. ForceRemove: httputils.BoolValue(r, "force"),
  311. RemoveVolume: httputils.BoolValue(r, "v"),
  312. RemoveLink: httputils.BoolValue(r, "link"),
  313. }
  314. if err := s.backend.ContainerRm(name, config); err != nil {
  315. // Force a 404 for the empty string
  316. if strings.Contains(strings.ToLower(err.Error()), "prefix can't be empty") {
  317. return fmt.Errorf("no such container: \"\"")
  318. }
  319. return err
  320. }
  321. w.WriteHeader(http.StatusNoContent)
  322. return nil
  323. }
  324. func (s *containerRouter) postContainersResize(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
  325. if err := httputils.ParseForm(r); err != nil {
  326. return err
  327. }
  328. height, err := strconv.Atoi(r.Form.Get("h"))
  329. if err != nil {
  330. return err
  331. }
  332. width, err := strconv.Atoi(r.Form.Get("w"))
  333. if err != nil {
  334. return err
  335. }
  336. return s.backend.ContainerResize(vars["name"], height, width)
  337. }
  338. func (s *containerRouter) postContainersAttach(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
  339. err := httputils.ParseForm(r)
  340. if err != nil {
  341. return err
  342. }
  343. containerName := vars["name"]
  344. _, upgrade := r.Header["Upgrade"]
  345. keys := []byte{}
  346. detachKeys := r.FormValue("detachKeys")
  347. if detachKeys != "" {
  348. keys, err = term.ToBytes(detachKeys)
  349. if err != nil {
  350. logrus.Warnf("Invalid escape keys provided (%s) using default : ctrl-p ctrl-q", detachKeys)
  351. }
  352. }
  353. hijacker, ok := w.(http.Hijacker)
  354. if !ok {
  355. return derr.ErrorCodeNoHijackConnection.WithArgs(containerName)
  356. }
  357. setupStreams := func() (io.ReadCloser, io.Writer, io.Writer, error) {
  358. conn, _, err := hijacker.Hijack()
  359. if err != nil {
  360. return nil, nil, nil, err
  361. }
  362. // set raw mode
  363. conn.Write([]byte{})
  364. if upgrade {
  365. fmt.Fprintf(conn, "HTTP/1.1 101 UPGRADED\r\nContent-Type: application/vnd.docker.raw-stream\r\nConnection: Upgrade\r\nUpgrade: tcp\r\n\r\n")
  366. } else {
  367. fmt.Fprintf(conn, "HTTP/1.1 200 OK\r\nContent-Type: application/vnd.docker.raw-stream\r\n\r\n")
  368. }
  369. closer := func() error {
  370. httputils.CloseStreams(conn)
  371. return nil
  372. }
  373. return ioutils.NewReadCloserWrapper(conn, closer), conn, conn, nil
  374. }
  375. attachConfig := &backend.ContainerAttachConfig{
  376. GetStreams: setupStreams,
  377. UseStdin: httputils.BoolValue(r, "stdin"),
  378. UseStdout: httputils.BoolValue(r, "stdout"),
  379. UseStderr: httputils.BoolValue(r, "stderr"),
  380. Logs: httputils.BoolValue(r, "logs"),
  381. Stream: httputils.BoolValue(r, "stream"),
  382. DetachKeys: keys,
  383. MuxStreams: true,
  384. }
  385. return s.backend.ContainerAttach(containerName, attachConfig)
  386. }
  387. func (s *containerRouter) wsContainersAttach(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
  388. if err := httputils.ParseForm(r); err != nil {
  389. return err
  390. }
  391. containerName := vars["name"]
  392. var keys []byte
  393. var err error
  394. detachKeys := r.FormValue("detachKeys")
  395. if detachKeys != "" {
  396. keys, err = term.ToBytes(detachKeys)
  397. if err != nil {
  398. logrus.Warnf("Invalid escape keys provided (%s) using default : ctrl-p ctrl-q", detachKeys)
  399. }
  400. }
  401. done := make(chan struct{})
  402. started := make(chan struct{})
  403. setupStreams := func() (io.ReadCloser, io.Writer, io.Writer, error) {
  404. wsChan := make(chan *websocket.Conn)
  405. h := func(conn *websocket.Conn) {
  406. wsChan <- conn
  407. <-done
  408. }
  409. srv := websocket.Server{Handler: h, Handshake: nil}
  410. go func() {
  411. close(started)
  412. srv.ServeHTTP(w, r)
  413. }()
  414. conn := <-wsChan
  415. return conn, conn, conn, nil
  416. }
  417. attachConfig := &backend.ContainerAttachConfig{
  418. GetStreams: setupStreams,
  419. Logs: httputils.BoolValue(r, "logs"),
  420. Stream: httputils.BoolValue(r, "stream"),
  421. DetachKeys: keys,
  422. UseStdin: true,
  423. UseStdout: true,
  424. UseStderr: true,
  425. MuxStreams: false, // TODO: this should be true since it's a single stream for both stdout and stderr
  426. }
  427. err = s.backend.ContainerAttach(containerName, attachConfig)
  428. close(done)
  429. select {
  430. case <-started:
  431. logrus.Errorf("Error attaching websocket: %s", err)
  432. return nil
  433. default:
  434. }
  435. return err
  436. }