api.go 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371
  1. package docker
  2. import (
  3. _"bytes"
  4. "encoding/json"
  5. "fmt"
  6. "github.com/gorilla/mux"
  7. "io/ioutil"
  8. "log"
  9. "net/http"
  10. "os"
  11. "runtime"
  12. "strconv"
  13. "strings"
  14. "time"
  15. )
  16. func ListenAndServe(addr string, rtime *Runtime) error {
  17. r := mux.NewRouter()
  18. log.Printf("Listening for HTTP on %s\n", addr)
  19. r.Path("/version").Methods("GET").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  20. log.Println(r.RequestURI)
  21. m := VersionOut{VERSION, GIT_COMMIT, NO_MEMORY_LIMIT}
  22. b, err := json.Marshal(m)
  23. if err != nil {
  24. http.Error(w, err.Error(), http.StatusInternalServerError)
  25. } else {
  26. w.Write(b)
  27. }
  28. })
  29. r.Path("/containers/{name:.*}/kill").Methods("POST").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  30. log.Println(r.RequestURI)
  31. vars := mux.Vars(r)
  32. name := vars["name"]
  33. if container := rtime.Get(name); container != nil {
  34. if err := container.Kill(); err != nil {
  35. http.Error(w, "Error restarting container "+name+": "+err.Error(), http.StatusInternalServerError)
  36. return
  37. }
  38. } else {
  39. http.Error(w, "No such container: "+name, http.StatusInternalServerError)
  40. return
  41. }
  42. w.WriteHeader(200)
  43. })
  44. r.Path("/images").Methods("GET").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  45. log.Println(r.RequestURI)
  46. All := r.Form.Get("all")
  47. NameFilter := r.Form.Get("filter")
  48. Quiet := r.Form.Get("quiet")
  49. var allImages map[string]*Image
  50. var err error
  51. if All == "true" {
  52. allImages, err = rtime.graph.Map()
  53. } else {
  54. allImages, err = rtime.graph.Heads()
  55. }
  56. if err != nil {
  57. w.WriteHeader(500)
  58. return
  59. }
  60. var outs []ImagesOut
  61. for name, repository := range rtime.repositories.Repositories {
  62. if NameFilter != "" && name != NameFilter {
  63. continue
  64. }
  65. for tag, id := range repository {
  66. var out ImagesOut
  67. image, err := rtime.graph.Get(id)
  68. if err != nil {
  69. log.Printf("Warning: couldn't load %s from %s/%s: %s", id, name, tag, err)
  70. continue
  71. }
  72. delete(allImages, id)
  73. if Quiet != "true" {
  74. out.Repository = name
  75. out.Tag = tag
  76. out.Id = TruncateId(id)
  77. out.Created = HumanDuration(time.Now().Sub(image.Created)) + " ago"
  78. } else {
  79. out.Id = image.ShortId()
  80. }
  81. outs = append(outs, out)
  82. }
  83. }
  84. // Display images which aren't part of a
  85. if NameFilter == "" {
  86. for id, image := range allImages {
  87. var out ImagesOut
  88. if Quiet != "true" {
  89. out.Repository = "<none>"
  90. out.Tag = "<none>"
  91. out.Id = TruncateId(id)
  92. out.Created = HumanDuration(time.Now().Sub(image.Created)) + " ago"
  93. } else {
  94. out.Id = image.ShortId()
  95. }
  96. outs = append(outs, out)
  97. }
  98. }
  99. b, err := json.Marshal(outs)
  100. if err != nil {
  101. http.Error(w, err.Error(), http.StatusInternalServerError)
  102. } else {
  103. w.Write(b)
  104. }
  105. })
  106. r.Path("/info").Methods("GET").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  107. log.Println(r.RequestURI)
  108. images, _ := rtime.graph.All()
  109. var imgcount int
  110. if images == nil {
  111. imgcount = 0
  112. } else {
  113. imgcount = len(images)
  114. }
  115. var out InfoOut
  116. out.Containers = len(rtime.List())
  117. out.Version = VERSION
  118. out.Images = imgcount
  119. if os.Getenv("DEBUG") == "1" {
  120. out.Debug = true
  121. out.NFd = getTotalUsedFds()
  122. out.NGoroutines = runtime.NumGoroutine()
  123. }
  124. b, err := json.Marshal(out)
  125. if err != nil {
  126. http.Error(w, err.Error(), http.StatusInternalServerError)
  127. } else {
  128. w.Write(b)
  129. }
  130. })
  131. r.Path("/images/{name:.*}/history").Methods("GET").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  132. log.Println(r.RequestURI)
  133. vars := mux.Vars(r)
  134. name := vars["name"]
  135. image, err := rtime.repositories.LookupImage(name)
  136. if err != nil {
  137. http.Error(w, err.Error(), http.StatusInternalServerError)
  138. return
  139. }
  140. var outs []HistoryOut
  141. err = image.WalkHistory(func(img *Image) error {
  142. var out HistoryOut
  143. out.Id = rtime.repositories.ImageName(img.ShortId())
  144. out.Created = HumanDuration(time.Now().Sub(img.Created)) + " ago"
  145. out.CreatedBy = strings.Join(img.ContainerConfig.Cmd, " ")
  146. return nil
  147. })
  148. b, err := json.Marshal(outs)
  149. if err != nil {
  150. http.Error(w, err.Error(), http.StatusInternalServerError)
  151. } else {
  152. w.Write(b)
  153. }
  154. })
  155. r.Path("/containers/{name:.*}/logs").Methods("GET").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  156. log.Println(r.RequestURI)
  157. vars := mux.Vars(r)
  158. name := vars["name"]
  159. if container := rtime.Get(name); container != nil {
  160. var out LogsOut
  161. logStdout, err := container.ReadLog("stdout")
  162. if err != nil {
  163. http.Error(w, err.Error(), http.StatusInternalServerError)
  164. return
  165. }
  166. logStderr, err := container.ReadLog("stderr")
  167. if err != nil {
  168. http.Error(w, err.Error(), http.StatusInternalServerError)
  169. return
  170. }
  171. stdout, errStdout := ioutil.ReadAll(logStdout)
  172. if errStdout != nil {
  173. http.Error(w, errStdout.Error(), http.StatusInternalServerError)
  174. return
  175. } else {
  176. out.Stdout = fmt.Sprintf("%s", stdout)
  177. }
  178. stderr, errStderr := ioutil.ReadAll(logStderr)
  179. if errStderr != nil {
  180. http.Error(w, errStderr.Error(), http.StatusInternalServerError)
  181. return
  182. } else {
  183. out.Stderr = fmt.Sprintf("%s", stderr)
  184. }
  185. b, err := json.Marshal(out)
  186. if err != nil {
  187. http.Error(w, err.Error(), http.StatusInternalServerError)
  188. } else {
  189. w.Write(b)
  190. }
  191. } else {
  192. http.Error(w, "No such container: "+name, http.StatusInternalServerError)
  193. }
  194. })
  195. r.Path("/containers").Methods("GET").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  196. log.Println(r.RequestURI)
  197. All := r.Form.Get("all")
  198. NoTrunc := r.Form.Get("notrunc")
  199. Quiet := r.Form.Get("quiet")
  200. Last := r.Form.Get("n")
  201. n, err := strconv.Atoi(Last)
  202. if err != nil {
  203. n = -1
  204. }
  205. var outs []PsOut
  206. for i, container := range rtime.List() {
  207. if !container.State.Running && All != "true" && n == -1 {
  208. continue
  209. }
  210. if i == n {
  211. break
  212. }
  213. var out PsOut
  214. out.Id = container.ShortId()
  215. if Quiet != "true" {
  216. command := fmt.Sprintf("%s %s", container.Path, strings.Join(container.Args, " "))
  217. if NoTrunc != "true" {
  218. command = Trunc(command, 20)
  219. }
  220. out.Image = rtime.repositories.ImageName(container.Image)
  221. out.Command = command
  222. out.Created = HumanDuration(time.Now().Sub(container.Created)) + " ago"
  223. out.Status = container.State.String()
  224. }
  225. outs = append(outs, out)
  226. }
  227. b, err := json.Marshal(outs)
  228. if err != nil {
  229. http.Error(w, err.Error(), http.StatusInternalServerError)
  230. } else {
  231. w.Write(b)
  232. }
  233. })
  234. r.Path("/pull").Methods("GET", "POST").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  235. var in PullIn
  236. //json.NewDecoder(r.Body).Decode(&in)
  237. in.Name = "base"
  238. hj, ok := w.(http.Hijacker)
  239. if !ok {
  240. http.Error(w, "webserver doesn't support hijacking", http.StatusInternalServerError)
  241. return
  242. }
  243. conn, bufrw, err := hj.Hijack()
  244. if err != nil {
  245. http.Error(w, err.Error(), http.StatusInternalServerError)
  246. return
  247. }
  248. // Don't forget to close the connection:
  249. defer conn.Close()
  250. if rtime.graph.LookupRemoteImage(in.Name, rtime.authConfig) {
  251. if err := rtime.graph.PullImage(bufrw, in.Name, rtime.authConfig); err != nil {
  252. //http.Error(w, err.Error(), http.StatusInternalServerError)
  253. }
  254. return
  255. }
  256. if err := rtime.graph.PullRepository(bufrw, in.Name, "", rtime.repositories, rtime.authConfig); err != nil {
  257. //http.Error(w, err.Error(), http.StatusInternalServerError)
  258. }
  259. return
  260. })
  261. r.Path("/containers/{name:.*}/restart").Methods("POST").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  262. log.Println(r.RequestURI)
  263. vars := mux.Vars(r)
  264. name := vars["name"]
  265. if container := rtime.Get(name); container != nil {
  266. if err := container.Restart(); err != nil {
  267. http.Error(w, "Error restarting container "+name+": "+err.Error(), http.StatusInternalServerError)
  268. return
  269. }
  270. } else {
  271. http.Error(w, "No such container: "+name, http.StatusInternalServerError)
  272. return
  273. }
  274. w.WriteHeader(200)
  275. })
  276. r.Path("/containers/{name:.*}").Methods("DELETE").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  277. log.Println(r.RequestURI)
  278. vars := mux.Vars(r)
  279. name := vars["name"]
  280. if container := rtime.Get(name); container != nil {
  281. if err := rtime.Destroy(container); err != nil {
  282. http.Error(w, "Error destroying container "+name+": "+err.Error(), http.StatusInternalServerError)
  283. return
  284. }
  285. } else {
  286. http.Error(w, "No such container: "+name, http.StatusInternalServerError)
  287. return
  288. }
  289. w.WriteHeader(200)
  290. })
  291. r.Path("/images/{name:.*}").Methods("DELETE").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  292. log.Println(r.RequestURI)
  293. vars := mux.Vars(r)
  294. name := vars["name"]
  295. img, err := rtime.repositories.LookupImage(name)
  296. if err != nil {
  297. http.Error(w, "No such image: "+name, http.StatusInternalServerError)
  298. return
  299. } else {
  300. if err := rtime.graph.Delete(img.Id); err != nil {
  301. http.Error(w, "Error deleting image "+name+": "+err.Error(), http.StatusInternalServerError)
  302. return
  303. }
  304. }
  305. w.WriteHeader(200)
  306. })
  307. r.Path("/containers/{name:.*}/start").Methods("POST").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  308. log.Println(r.RequestURI)
  309. vars := mux.Vars(r)
  310. name := vars["name"]
  311. if container := rtime.Get(name); container != nil {
  312. if err := container.Start(); err != nil {
  313. http.Error(w, "Error starting container "+name+": "+err.Error(), http.StatusInternalServerError)
  314. return
  315. }
  316. } else {
  317. http.Error(w, "No such container: "+name, http.StatusInternalServerError)
  318. return
  319. }
  320. w.WriteHeader(200)
  321. })
  322. r.Path("/containers/{name:.*}/stop").Methods("POST").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  323. log.Println(r.RequestURI)
  324. vars := mux.Vars(r)
  325. name := vars["name"]
  326. if container := rtime.Get(name); container != nil {
  327. if err := container.Stop(); err != nil {
  328. http.Error(w, "Error stopping container "+name+": "+err.Error(), http.StatusInternalServerError)
  329. return
  330. }
  331. } else {
  332. http.Error(w, "No such container: "+name, http.StatusInternalServerError)
  333. return
  334. }
  335. w.WriteHeader(200)
  336. })
  337. return http.ListenAndServe(addr, r)
  338. }