api.go 16 KB

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