api.go 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640
  1. package docker
  2. import (
  3. _ "bytes"
  4. "encoding/json"
  5. "fmt"
  6. "github.com/gorilla/mux"
  7. "io"
  8. "io/ioutil"
  9. "log"
  10. "net"
  11. "net/http"
  12. "os"
  13. "runtime"
  14. "strconv"
  15. "strings"
  16. "time"
  17. )
  18. func ListenAndServe(addr string, rtime *Runtime) error {
  19. r := mux.NewRouter()
  20. log.Printf("Listening for HTTP on %s\n", addr)
  21. r.Path("/version").Methods("GET").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  22. log.Println(r.Method, r.RequestURI)
  23. m := ApiVersion{VERSION, GIT_COMMIT, NO_MEMORY_LIMIT}
  24. b, err := json.Marshal(m)
  25. if err != nil {
  26. http.Error(w, err.Error(), http.StatusInternalServerError)
  27. } else {
  28. w.Write(b)
  29. }
  30. })
  31. r.Path("/containers/{name:.*}/kill").Methods("POST").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  32. log.Println(r.Method, r.RequestURI)
  33. vars := mux.Vars(r)
  34. name := vars["name"]
  35. if container := rtime.Get(name); container != nil {
  36. if err := container.Kill(); err != nil {
  37. http.Error(w, "Error restarting container "+name+": "+err.Error(), http.StatusInternalServerError)
  38. return
  39. }
  40. } else {
  41. http.Error(w, "No such container: "+name, http.StatusNotFound)
  42. return
  43. }
  44. w.WriteHeader(200)
  45. })
  46. r.Path("/images/{name:.*}").Methods("GET").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  47. log.Println(r.Method, r.RequestURI)
  48. vars := mux.Vars(r)
  49. name := vars["name"]
  50. if image, err := rtime.repositories.LookupImage(name); err == nil && image != nil {
  51. b, err := json.Marshal(image)
  52. if err != nil {
  53. http.Error(w, err.Error(), http.StatusInternalServerError)
  54. } else {
  55. w.Write(b)
  56. }
  57. return
  58. }
  59. http.Error(w, "No such image: "+name, http.StatusNotFound)
  60. })
  61. r.Path("/containers/{name:.*}/export").Methods("GET").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  62. log.Println(r.Method, r.RequestURI)
  63. vars := mux.Vars(r)
  64. name := vars["name"]
  65. if container := rtime.Get(name); container != nil {
  66. data, err := container.Export()
  67. if err != nil {
  68. http.Error(w, err.Error(), http.StatusInternalServerError)
  69. return
  70. }
  71. conn, _, err := w.(http.Hijacker).Hijack()
  72. if err != nil {
  73. http.Error(w, err.Error(), http.StatusInternalServerError)
  74. return
  75. }
  76. defer conn.Close()
  77. file, err := conn.(*net.TCPConn).File()
  78. if err != nil {
  79. http.Error(w, err.Error(), http.StatusInternalServerError)
  80. return
  81. }
  82. defer file.Close()
  83. fmt.Fprintln(file, "HTTP/1.1 200 OK\r\nContent-Type: application/octet-stream\r\n\r\n")
  84. // Stream the entire contents of the container (basically a volatile snapshot)
  85. if _, err := io.Copy(file, data); err != nil {
  86. fmt.Fprintln(file, "Error: "+err.Error())
  87. return
  88. }
  89. } else {
  90. http.Error(w, "No such container: "+name, http.StatusNotFound)
  91. }
  92. })
  93. r.Path("/containers/{name:.*}").Methods("GET").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  94. log.Println(r.Method, r.RequestURI)
  95. vars := mux.Vars(r)
  96. name := vars["name"]
  97. if container := rtime.Get(name); container != nil {
  98. b, err := json.Marshal(container)
  99. if err != nil {
  100. http.Error(w, err.Error(), http.StatusInternalServerError)
  101. } else {
  102. w.Write(b)
  103. }
  104. return
  105. }
  106. http.Error(w, "No such container: "+name, http.StatusNotFound)
  107. })
  108. r.Path("/images").Methods("GET").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  109. log.Println(r.Method, r.RequestURI)
  110. if err := r.ParseForm(); err != nil {
  111. http.Error(w, err.Error(), http.StatusInternalServerError)
  112. }
  113. All := r.Form.Get("all")
  114. NameFilter := r.Form.Get("filter")
  115. Quiet := r.Form.Get("quiet")
  116. var allImages map[string]*Image
  117. var err error
  118. if All == "true" {
  119. allImages, err = rtime.graph.Map()
  120. } else {
  121. allImages, err = rtime.graph.Heads()
  122. }
  123. if err != nil {
  124. w.WriteHeader(500)
  125. return
  126. }
  127. var outs []ApiImages
  128. for name, repository := range rtime.repositories.Repositories {
  129. if NameFilter != "" && name != NameFilter {
  130. continue
  131. }
  132. for tag, id := range repository {
  133. var out ApiImages
  134. image, err := rtime.graph.Get(id)
  135. if err != nil {
  136. log.Printf("Warning: couldn't load %s from %s/%s: %s", id, name, tag, err)
  137. continue
  138. }
  139. delete(allImages, id)
  140. if Quiet != "true" {
  141. out.Repository = name
  142. out.Tag = tag
  143. out.Id = TruncateId(id)
  144. out.Created = HumanDuration(time.Now().Sub(image.Created)) + " ago"
  145. } else {
  146. out.Id = image.ShortId()
  147. }
  148. outs = append(outs, out)
  149. }
  150. }
  151. // Display images which aren't part of a
  152. if NameFilter == "" {
  153. for id, image := range allImages {
  154. var out ApiImages
  155. if Quiet != "true" {
  156. out.Repository = "<none>"
  157. out.Tag = "<none>"
  158. out.Id = TruncateId(id)
  159. out.Created = HumanDuration(time.Now().Sub(image.Created)) + " ago"
  160. } else {
  161. out.Id = image.ShortId()
  162. }
  163. outs = append(outs, out)
  164. }
  165. }
  166. b, err := json.Marshal(outs)
  167. if err != nil {
  168. http.Error(w, err.Error(), http.StatusInternalServerError)
  169. } else {
  170. w.Write(b)
  171. }
  172. })
  173. r.Path("/info").Methods("GET").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  174. log.Println(r.Method, r.RequestURI)
  175. images, _ := rtime.graph.All()
  176. var imgcount int
  177. if images == nil {
  178. imgcount = 0
  179. } else {
  180. imgcount = len(images)
  181. }
  182. var out ApiInfo
  183. out.Containers = len(rtime.List())
  184. out.Version = VERSION
  185. out.Images = imgcount
  186. if os.Getenv("DEBUG") == "1" {
  187. out.Debug = true
  188. out.NFd = getTotalUsedFds()
  189. out.NGoroutines = runtime.NumGoroutine()
  190. }
  191. b, err := json.Marshal(out)
  192. if err != nil {
  193. http.Error(w, err.Error(), http.StatusInternalServerError)
  194. } else {
  195. w.Write(b)
  196. }
  197. })
  198. r.Path("/images/{name:.*}/history").Methods("GET").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  199. log.Println(r.Method, r.RequestURI)
  200. vars := mux.Vars(r)
  201. name := vars["name"]
  202. image, err := rtime.repositories.LookupImage(name)
  203. if err != nil {
  204. http.Error(w, err.Error(), http.StatusInternalServerError)
  205. return
  206. }
  207. var outs []ApiHistory
  208. err = image.WalkHistory(func(img *Image) error {
  209. var out ApiHistory
  210. out.Id = rtime.repositories.ImageName(img.ShortId())
  211. out.Created = HumanDuration(time.Now().Sub(img.Created)) + " ago"
  212. out.CreatedBy = strings.Join(img.ContainerConfig.Cmd, " ")
  213. return nil
  214. })
  215. b, err := json.Marshal(outs)
  216. if err != nil {
  217. http.Error(w, err.Error(), http.StatusInternalServerError)
  218. } else {
  219. w.Write(b)
  220. }
  221. })
  222. r.Path("/containers/{name:.*}/logs").Methods("GET").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  223. log.Println(r.Method, r.RequestURI)
  224. vars := mux.Vars(r)
  225. name := vars["name"]
  226. if container := rtime.Get(name); container != nil {
  227. var out ApiLogs
  228. logStdout, err := container.ReadLog("stdout")
  229. if err != nil {
  230. http.Error(w, err.Error(), http.StatusInternalServerError)
  231. return
  232. }
  233. logStderr, err := container.ReadLog("stderr")
  234. if err != nil {
  235. http.Error(w, err.Error(), http.StatusInternalServerError)
  236. return
  237. }
  238. stdout, errStdout := ioutil.ReadAll(logStdout)
  239. if errStdout != nil {
  240. http.Error(w, errStdout.Error(), http.StatusInternalServerError)
  241. return
  242. } else {
  243. out.Stdout = fmt.Sprintf("%s", stdout)
  244. }
  245. stderr, errStderr := ioutil.ReadAll(logStderr)
  246. if errStderr != nil {
  247. http.Error(w, errStderr.Error(), http.StatusInternalServerError)
  248. return
  249. } else {
  250. out.Stderr = fmt.Sprintf("%s", stderr)
  251. }
  252. b, err := json.Marshal(out)
  253. if err != nil {
  254. http.Error(w, err.Error(), http.StatusInternalServerError)
  255. } else {
  256. w.Write(b)
  257. }
  258. } else {
  259. http.Error(w, "No such container: "+name, http.StatusNotFound)
  260. }
  261. })
  262. r.Path("/containers/{name:.*}/changes").Methods("GET").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  263. log.Println(r.Method, r.RequestURI)
  264. vars := mux.Vars(r)
  265. name := vars["name"]
  266. if container := rtime.Get(name); container != nil {
  267. changes, err := container.Changes()
  268. if err != nil {
  269. http.Error(w, err.Error(), http.StatusInternalServerError)
  270. return
  271. }
  272. var changesStr []string
  273. for _, name := range changes {
  274. changesStr = append(changesStr, name.String())
  275. }
  276. b, err := json.Marshal(changesStr)
  277. if err != nil {
  278. http.Error(w, err.Error(), http.StatusInternalServerError)
  279. } else {
  280. w.Write(b)
  281. }
  282. } else {
  283. http.Error(w, "No such container: "+name, http.StatusNotFound)
  284. }
  285. })
  286. r.Path("/containers/{name:.*}/port").Methods("GET").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  287. log.Println(r.Method, r.RequestURI)
  288. if err := r.ParseForm(); err != nil {
  289. http.Error(w, err.Error(), http.StatusInternalServerError)
  290. }
  291. privatePort := r.Form.Get("port")
  292. vars := mux.Vars(r)
  293. name := vars["name"]
  294. if container := rtime.Get(name); container == nil {
  295. http.Error(w, "No such container: "+name, http.StatusNotFound)
  296. return
  297. } else {
  298. if frontend, exists := container.NetworkSettings.PortMapping[privatePort]; !exists {
  299. http.Error(w, "No private port '"+privatePort+"' allocated on "+name, http.StatusInternalServerError)
  300. return
  301. } else {
  302. b, err := json.Marshal(ApiPort{frontend})
  303. if err != nil {
  304. http.Error(w, err.Error(), http.StatusInternalServerError)
  305. } else {
  306. w.Write(b)
  307. }
  308. }
  309. }
  310. })
  311. r.Path("/containers").Methods("GET").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  312. log.Println(r.Method, r.RequestURI)
  313. if err := r.ParseForm(); err != nil {
  314. http.Error(w, err.Error(), http.StatusInternalServerError)
  315. }
  316. All := r.Form.Get("all")
  317. NoTrunc := r.Form.Get("notrunc")
  318. Quiet := r.Form.Get("quiet")
  319. Last := r.Form.Get("n")
  320. n, err := strconv.Atoi(Last)
  321. if err != nil {
  322. n = -1
  323. }
  324. var outs []ApiContainers
  325. for i, container := range rtime.List() {
  326. if !container.State.Running && All != "true" && n == -1 {
  327. continue
  328. }
  329. if i == n {
  330. break
  331. }
  332. var out ApiContainers
  333. out.Id = container.ShortId()
  334. if Quiet != "true" {
  335. command := fmt.Sprintf("%s %s", container.Path, strings.Join(container.Args, " "))
  336. if NoTrunc != "true" {
  337. command = Trunc(command, 20)
  338. }
  339. out.Image = rtime.repositories.ImageName(container.Image)
  340. out.Command = command
  341. out.Created = HumanDuration(time.Now().Sub(container.Created)) + " ago"
  342. out.Status = container.State.String()
  343. }
  344. outs = append(outs, out)
  345. }
  346. b, err := json.Marshal(outs)
  347. if err != nil {
  348. http.Error(w, err.Error(), http.StatusInternalServerError)
  349. } else {
  350. w.Write(b)
  351. }
  352. })
  353. r.Path("/containers/{name:.*}/commit").Methods("POST").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  354. log.Println(r.Method, r.RequestURI)
  355. if err := r.ParseForm(); err != nil {
  356. http.Error(w, err.Error(), http.StatusInternalServerError)
  357. return
  358. }
  359. vars := mux.Vars(r)
  360. name := vars["name"]
  361. repo := r.Form.Get("repo")
  362. tag := r.Form.Get("tag")
  363. comment := r.Form.Get("comment")
  364. img, err := rtime.Commit(name, repo, tag, comment)
  365. if err != nil {
  366. http.Error(w, err.Error(), http.StatusInternalServerError)
  367. return
  368. }
  369. b, err := json.Marshal(ApiCommit{img.ShortId()})
  370. if err != nil {
  371. http.Error(w, err.Error(), http.StatusInternalServerError)
  372. } else {
  373. w.Write(b)
  374. }
  375. })
  376. r.Path("/images/{name:.*}/tag").Methods("POST").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  377. log.Println(r.Method, r.RequestURI)
  378. if err := r.ParseForm(); err != nil {
  379. http.Error(w, err.Error(), http.StatusInternalServerError)
  380. }
  381. vars := mux.Vars(r)
  382. name := vars["name"]
  383. repo := r.Form.Get("repo")
  384. tag := r.Form.Get("tag")
  385. var force bool
  386. if r.Form.Get("force") == "true" {
  387. force = true
  388. }
  389. if err := rtime.repositories.Set(repo, tag, name, force); err != nil {
  390. http.Error(w, err.Error(), http.StatusInternalServerError)
  391. return
  392. }
  393. w.WriteHeader(200)
  394. })
  395. r.Path("/images/{name:.*}/pull").Methods("POST").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  396. log.Println(r.Method, r.RequestURI)
  397. vars := mux.Vars(r)
  398. name := vars["name"]
  399. conn, _, err := w.(http.Hijacker).Hijack()
  400. if err != nil {
  401. http.Error(w, err.Error(), http.StatusInternalServerError)
  402. return
  403. }
  404. defer conn.Close()
  405. file, err := conn.(*net.TCPConn).File()
  406. if err != nil {
  407. http.Error(w, err.Error(), http.StatusInternalServerError)
  408. return
  409. }
  410. defer file.Close()
  411. fmt.Fprintln(file, "HTTP/1.1 200 OK\r\nContent-Type: application/json\r\n\r\n")
  412. if rtime.graph.LookupRemoteImage(name, rtime.authConfig) {
  413. if err := rtime.graph.PullImage(file, name, rtime.authConfig); err != nil {
  414. fmt.Fprintln(file, "Error: "+err.Error())
  415. }
  416. return
  417. }
  418. if err := rtime.graph.PullRepository(file, name, "", rtime.repositories, rtime.authConfig); err != nil {
  419. fmt.Fprintln(file, "Error: "+err.Error())
  420. }
  421. })
  422. r.Path("/containers").Methods("POST").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  423. log.Println(r.Method, r.RequestURI)
  424. var config Config
  425. if err := json.NewDecoder(r.Body).Decode(&config); err != nil {
  426. http.Error(w, err.Error(), http.StatusInternalServerError)
  427. return
  428. }
  429. conn, _, err := w.(http.Hijacker).Hijack()
  430. if err != nil {
  431. http.Error(w, err.Error(), http.StatusInternalServerError)
  432. return
  433. }
  434. defer conn.Close()
  435. file, err := conn.(*net.TCPConn).File()
  436. if err != nil {
  437. http.Error(w, err.Error(), http.StatusInternalServerError)
  438. return
  439. }
  440. defer file.Close()
  441. fmt.Fprintln(file, "HTTP/1.1 200 OK\r\nContent-Type: application/json\r\n\r\n")
  442. container, err := rtime.Create(&config)
  443. if err != nil {
  444. // If container not found, try to pull it
  445. if rtime.graph.IsNotExist(err) {
  446. fmt.Fprintf(file, "Image %s not found, trying to pull it from registry.\r\n", config.Image)
  447. //if err = srv.CmdPull(stdin, stdout, config.Image); err != nil {
  448. // fmt.Fprintln(file, "Error: "+err.Error())
  449. // return
  450. //}
  451. //if container, err = srv.runtime.Create(config); err != nil {
  452. // fmt.Fprintln(file, "Error: "+err.Error())
  453. // return
  454. //}
  455. } else {
  456. fmt.Fprintln(file, "Error: "+err.Error())
  457. return
  458. }
  459. }
  460. var (
  461. cStdin io.ReadCloser
  462. cStdout, cStderr io.Writer
  463. )
  464. if config.AttachStdin {
  465. r, w := io.Pipe()
  466. go func() {
  467. defer w.Close()
  468. defer Debugf("Closing buffered stdin pipe")
  469. io.Copy(w, file)
  470. }()
  471. cStdin = r
  472. }
  473. if config.AttachStdout {
  474. cStdout = file
  475. }
  476. if config.AttachStderr {
  477. cStderr = file // FIXME: rcli can't differentiate stdout from stderr
  478. }
  479. attachErr := container.Attach(cStdin, file, cStdout, cStderr)
  480. Debugf("Starting\n")
  481. if err := container.Start(); err != nil {
  482. fmt.Fprintln(file, "Error: "+err.Error())
  483. return
  484. }
  485. if cStdout == nil && cStderr == nil {
  486. fmt.Fprintln(file, container.ShortId())
  487. }
  488. Debugf("Waiting for attach to return\n")
  489. <-attachErr
  490. // Expecting I/O pipe error, discarding
  491. })
  492. r.Path("/containers/{name:.*}/restart").Methods("POST").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  493. log.Println(r.Method, r.RequestURI)
  494. vars := mux.Vars(r)
  495. name := vars["name"]
  496. if container := rtime.Get(name); container != nil {
  497. if err := container.Restart(); err != nil {
  498. http.Error(w, "Error restarting container "+name+": "+err.Error(), http.StatusInternalServerError)
  499. return
  500. }
  501. } else {
  502. http.Error(w, "No such container: "+name, http.StatusNotFound)
  503. return
  504. }
  505. w.WriteHeader(200)
  506. })
  507. r.Path("/containers/{name:.*}").Methods("DELETE").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  508. log.Println(r.Method, r.RequestURI)
  509. vars := mux.Vars(r)
  510. name := vars["name"]
  511. if container := rtime.Get(name); container != nil {
  512. if err := rtime.Destroy(container); err != nil {
  513. http.Error(w, "Error destroying container "+name+": "+err.Error(), http.StatusInternalServerError)
  514. return
  515. }
  516. } else {
  517. http.Error(w, "No such container: "+name, http.StatusNotFound)
  518. return
  519. }
  520. w.WriteHeader(200)
  521. })
  522. r.Path("/images/{name:.*}").Methods("DELETE").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  523. log.Println(r.Method, r.RequestURI)
  524. vars := mux.Vars(r)
  525. name := vars["name"]
  526. img, err := rtime.repositories.LookupImage(name)
  527. if err != nil {
  528. http.Error(w, "No such image: "+name, http.StatusNotFound)
  529. return
  530. } else {
  531. if err := rtime.graph.Delete(img.Id); err != nil {
  532. http.Error(w, "Error deleting image "+name+": "+err.Error(), http.StatusInternalServerError)
  533. return
  534. }
  535. }
  536. w.WriteHeader(200)
  537. })
  538. r.Path("/containers/{name:.*}/start").Methods("POST").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  539. log.Println(r.Method, r.RequestURI)
  540. vars := mux.Vars(r)
  541. name := vars["name"]
  542. if container := rtime.Get(name); container != nil {
  543. if err := container.Start(); err != nil {
  544. http.Error(w, "Error starting container "+name+": "+err.Error(), http.StatusInternalServerError)
  545. return
  546. }
  547. } else {
  548. http.Error(w, "No such container: "+name, http.StatusNotFound)
  549. return
  550. }
  551. w.WriteHeader(200)
  552. })
  553. r.Path("/containers/{name:.*}/stop").Methods("POST").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  554. log.Println(r.Method, r.RequestURI)
  555. vars := mux.Vars(r)
  556. name := vars["name"]
  557. if container := rtime.Get(name); container != nil {
  558. if err := container.Stop(); err != nil {
  559. http.Error(w, "Error stopping container "+name+": "+err.Error(), http.StatusInternalServerError)
  560. return
  561. }
  562. } else {
  563. http.Error(w, "No such container: "+name, http.StatusNotFound)
  564. return
  565. }
  566. w.WriteHeader(200)
  567. })
  568. r.Path("/containers/{name:.*}/wait").Methods("POST").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  569. log.Println(r.Method, r.RequestURI)
  570. vars := mux.Vars(r)
  571. name := vars["name"]
  572. if container := rtime.Get(name); container != nil {
  573. b, err := json.Marshal(ApiWait{container.Wait()})
  574. if err != nil {
  575. http.Error(w, err.Error(), http.StatusInternalServerError)
  576. } else {
  577. w.Write(b)
  578. }
  579. return
  580. } else {
  581. http.Error(w, "No such container: "+name, http.StatusNotFound)
  582. return
  583. }
  584. })
  585. return http.ListenAndServe(addr, r)
  586. }