commands2.go 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528
  1. package docker
  2. import (
  3. "bytes"
  4. "encoding/json"
  5. "flag"
  6. "fmt"
  7. "io"
  8. "io/ioutil"
  9. "net/http"
  10. "os"
  11. "text/tabwriter"
  12. )
  13. func ParseCommands(args []string) error {
  14. cmds := map[string]func(args []string) error{
  15. "images": cmdImages,
  16. "info": cmdInfo,
  17. "history": cmdHistory,
  18. "kill": cmdKill,
  19. "logs": cmdLogs,
  20. "ps": cmdPs,
  21. "restart": cmdRestart,
  22. "rm": cmdRm,
  23. "rmi": cmdRmi,
  24. "run": cmdRun,
  25. "start": cmdStart,
  26. "stop": cmdStop,
  27. "version": cmdVersion,
  28. }
  29. if len(args) > 0 {
  30. cmd, exists := cmds[args[0]]
  31. if !exists {
  32. //TODO display commend not found
  33. return cmdHelp(args)
  34. }
  35. return cmd(args[1:])
  36. }
  37. return cmdHelp(args)
  38. }
  39. func cmdHelp(args []string) error {
  40. help := "Usage: docker COMMAND [arg...]\n\nA self-sufficient runtime for linux containers.\n\nCommands:\n"
  41. for _, cmd := range [][]string{
  42. // {"attach", "Attach to a running container"},
  43. // {"commit", "Create a new image from a container's changes"},
  44. // {"diff", "Inspect changes on a container's filesystem"},
  45. // {"export", "Stream the contents of a container as a tar archive"},
  46. {"history", "Show the history of an image"},
  47. {"images", "List images"},
  48. // {"import", "Create a new filesystem image from the contents of a tarball"},
  49. {"info", "Display system-wide information"},
  50. // {"inspect", "Return low-level information on a container"},
  51. {"kill", "Kill a running container"},
  52. // {"login", "Register or Login to the docker registry server"},
  53. {"logs", "Fetch the logs of a container"},
  54. // {"port", "Lookup the public-facing port which is NAT-ed to PRIVATE_PORT"},
  55. {"ps", "List containers"},
  56. // {"pull", "Pull an image or a repository from the docker registry server"},
  57. // {"push", "Push an image or a repository to the docker registry server"},
  58. {"restart", "Restart a running container"},
  59. {"rm", "Remove a container"},
  60. {"rmi", "Remove an image"},
  61. {"run", "Run a command in a new container"},
  62. {"start", "Start a stopped container"},
  63. {"stop", "Stop a running container"},
  64. // {"tag", "Tag an image into a repository"},
  65. {"version", "Show the docker version information"},
  66. // {"wait", "Block until a container stops, then print its exit code"},
  67. } {
  68. help += fmt.Sprintf(" %-10.10s%s\n", cmd[0], cmd[1])
  69. }
  70. fmt.Println(help)
  71. return nil
  72. }
  73. func cmdImages(args []string) error {
  74. cmd := Subcmd("images", "[OPTIONS] [NAME]", "List images")
  75. var in ImagesIn
  76. cmd.BoolVar(&in.Quiet, "q", false, "only show numeric IDs")
  77. cmd.BoolVar(&in.All, "a", false, "show all images")
  78. if err := cmd.Parse(args); err != nil {
  79. return nil
  80. }
  81. if cmd.NArg() > 1 {
  82. cmd.Usage()
  83. return nil
  84. }
  85. if cmd.NArg() == 1 {
  86. in.NameFilter = cmd.Arg(0)
  87. }
  88. body, err := apiPost("images", in)
  89. if err != nil {
  90. return err
  91. }
  92. var outs []ImagesOut
  93. err = json.Unmarshal(body, &outs)
  94. if err != nil {
  95. return err
  96. }
  97. w := tabwriter.NewWriter(os.Stdout, 20, 1, 3, ' ', 0)
  98. if !in.Quiet {
  99. fmt.Fprintln(w, "REPOSITORY\tTAG\tID\tCREATED")
  100. }
  101. for _, out := range outs {
  102. if !in.Quiet {
  103. fmt.Fprintf(w, "%s\t%s\t%s\t%s\n", out.Repository, out.Tag, out.Id, out.Created)
  104. } else {
  105. fmt.Fprintln(w, out.Id)
  106. }
  107. }
  108. if !in.Quiet {
  109. w.Flush()
  110. }
  111. return nil
  112. }
  113. func cmdInfo(args []string) error {
  114. cmd := Subcmd("info", "", "Display system-wide information")
  115. if err := cmd.Parse(args); err != nil {
  116. return nil
  117. }
  118. if cmd.NArg() > 0 {
  119. cmd.Usage()
  120. return nil
  121. }
  122. body, err := apiGet("info")
  123. if err != nil {
  124. return err
  125. }
  126. var out InfoOut
  127. err = json.Unmarshal(body, &out)
  128. if err != nil {
  129. return err
  130. }
  131. fmt.Printf("containers: %d\nversion: %s\nimages: %d\n", out.Containers, out.Version, out.Images)
  132. if out.Debug {
  133. fmt.Println("debug mode enabled")
  134. fmt.Printf("fds: %d\ngoroutines: %d\n", out.NFd, out.NGoroutines)
  135. }
  136. return nil
  137. }
  138. func cmdHistory(args []string) error {
  139. cmd := Subcmd("history", "IMAGE", "Show the history of an image")
  140. if err := cmd.Parse(args); err != nil {
  141. return nil
  142. }
  143. if cmd.NArg() != 1 {
  144. cmd.Usage()
  145. return nil
  146. }
  147. body, err := apiPost("history", HistoryIn{cmd.Arg(0)})
  148. if err != nil {
  149. return err
  150. }
  151. var outs []HistoryOut
  152. err = json.Unmarshal(body, &outs)
  153. if err != nil {
  154. return err
  155. }
  156. w := tabwriter.NewWriter(os.Stdout, 20, 1, 3, ' ', 0)
  157. fmt.Fprintln(w, "ID\tCREATED\tCREATED BY")
  158. for _, out := range outs {
  159. fmt.Fprintf(w, "%s\t%s\t%s\n", out.Id, out.Created, out.CreatedBy)
  160. }
  161. w.Flush()
  162. return nil
  163. }
  164. func cmdKill(args []string) error {
  165. cmd := Subcmd("kill", "CONTAINER [CONTAINER...]", "Kill a running container")
  166. if err := cmd.Parse(args); err != nil {
  167. return nil
  168. }
  169. if cmd.NArg() < 1 {
  170. cmd.Usage()
  171. return nil
  172. }
  173. body, err := apiPost("kill", args)
  174. if err != nil {
  175. return err
  176. }
  177. var out SimpleMessage
  178. err = json.Unmarshal(body, &out)
  179. if err != nil {
  180. return err
  181. }
  182. fmt.Print(out.Message)
  183. return nil
  184. }
  185. func cmdLogs(args []string) error {
  186. cmd := Subcmd("logs", "CONTAINER", "Fetch the logs of a container")
  187. if err := cmd.Parse(args); err != nil {
  188. return nil
  189. }
  190. if cmd.NArg() != 1 {
  191. cmd.Usage()
  192. return nil
  193. }
  194. body, err := apiPost("logs", LogsIn{cmd.Arg(0)})
  195. if err != nil {
  196. return err
  197. }
  198. var out LogsOut
  199. err = json.Unmarshal(body, &out)
  200. if err != nil {
  201. return err
  202. }
  203. fmt.Fprintln(os.Stdout, out.Stdout)
  204. fmt.Fprintln(os.Stderr, out.Stderr)
  205. return nil
  206. }
  207. func cmdPs(args []string) error {
  208. cmd := Subcmd("ps", "[OPTIONS]", "List containers")
  209. var in PsIn
  210. cmd.BoolVar(&in.Quiet, "q", false, "Only display numeric IDs")
  211. cmd.BoolVar(&in.All, "a", false, "Show all containers. Only running containers are shown by default.")
  212. cmd.BoolVar(&in.Full, "notrunc", false, "Don't truncate output")
  213. nLatest := cmd.Bool("l", false, "Show only the latest created container, include non-running ones.")
  214. cmd.IntVar(&in.Last, "n", -1, "Show n last created containers, include non-running ones.")
  215. if err := cmd.Parse(args); err != nil {
  216. return nil
  217. }
  218. if in.Last == -1 && *nLatest {
  219. in.Last = 1
  220. }
  221. body, err := apiPost("ps", in)
  222. if err != nil {
  223. return err
  224. }
  225. var outs []PsOut
  226. err = json.Unmarshal(body, &outs)
  227. if err != nil {
  228. return err
  229. }
  230. w := tabwriter.NewWriter(os.Stdout, 20, 1, 3, ' ', 0)
  231. if !in.Quiet {
  232. fmt.Fprintln(w, "ID\tIMAGE\tCOMMAND\tCREATED\tSTATUS")
  233. }
  234. for _, out := range outs {
  235. if !in.Quiet {
  236. fmt.Fprintf(w, "%s\t%s\t%s\t%s\n", out.Id, out.Image, out.Command, out.Status, out.Created)
  237. } else {
  238. fmt.Fprintln(w, out.Id)
  239. }
  240. }
  241. if !in.Quiet {
  242. w.Flush()
  243. }
  244. return nil
  245. }
  246. func cmdRestart(args []string) error {
  247. cmd := Subcmd("restart", "CONTAINER [CONTAINER...]", "Restart a running container")
  248. if err := cmd.Parse(args); err != nil {
  249. return nil
  250. }
  251. if cmd.NArg() < 1 {
  252. cmd.Usage()
  253. return nil
  254. }
  255. body, err := apiPost("restart", cmd.Args())
  256. if err != nil {
  257. return err
  258. }
  259. var out []string
  260. err = json.Unmarshal(body, &out)
  261. if err != nil {
  262. return err
  263. }
  264. for _, name := range out {
  265. fmt.Println(name)
  266. }
  267. return nil
  268. }
  269. func cmdRm(args []string) error {
  270. cmd := Subcmd("rm", "CONTAINER [CONTAINER...]", "Remove a container")
  271. if err := cmd.Parse(args); err != nil {
  272. return nil
  273. }
  274. if cmd.NArg() < 1 {
  275. cmd.Usage()
  276. return nil
  277. }
  278. body, err := apiPost("rm", cmd.Args())
  279. if err != nil {
  280. return err
  281. }
  282. var out []string
  283. err = json.Unmarshal(body, &out)
  284. if err != nil {
  285. return err
  286. }
  287. for _, name := range out {
  288. fmt.Println(name)
  289. }
  290. return nil
  291. }
  292. func cmdRmi(args []string) error {
  293. cmd := Subcmd("rmi", "IMAGE [IMAGE...]", "Remove an image")
  294. if err := cmd.Parse(args); err != nil {
  295. return nil
  296. }
  297. if cmd.NArg() < 1 {
  298. cmd.Usage()
  299. return nil
  300. }
  301. body, err := apiPost("rmi", cmd.Args())
  302. if err != nil {
  303. return err
  304. }
  305. var out []string
  306. err = json.Unmarshal(body, &out)
  307. if err != nil {
  308. return err
  309. }
  310. for _, name := range out {
  311. fmt.Println(name)
  312. }
  313. return nil
  314. }
  315. func cmdRun(args []string) error {
  316. config, err := ParseRun(args)
  317. if err != nil {
  318. return err
  319. }
  320. if config.Image == "" {
  321. fmt.Println("Error: Image not specified")
  322. return fmt.Errorf("Image not specified")
  323. }
  324. if len(config.Cmd) == 0 {
  325. fmt.Println("Error: Command not specified")
  326. return fmt.Errorf("Command not specified")
  327. }
  328. body, err := apiPostHijack("run", config)
  329. if err != nil {
  330. return err
  331. }
  332. defer body.Close()
  333. /*str, err2 := ioutil.ReadAll(body)
  334. if err2 != nil {
  335. return err2
  336. }
  337. fmt.Println(str)*/
  338. return nil
  339. }
  340. func cmdStart(args []string) error {
  341. cmd := Subcmd("start", "CONTAINER [CONTAINER...]", "Restart a stopped container")
  342. if err := cmd.Parse(args); err != nil {
  343. return nil
  344. }
  345. if cmd.NArg() < 1 {
  346. cmd.Usage()
  347. return nil
  348. }
  349. body, err := apiPost("start", cmd.Args())
  350. if err != nil {
  351. return err
  352. }
  353. var out []string
  354. err = json.Unmarshal(body, &out)
  355. if err != nil {
  356. return err
  357. }
  358. for _, name := range out {
  359. fmt.Println(name)
  360. }
  361. return nil
  362. }
  363. func cmdStop(args []string) error {
  364. cmd := Subcmd("stop", "CONTAINER [CONTAINER...]", "Restart a running container")
  365. if err := cmd.Parse(args); err != nil {
  366. return nil
  367. }
  368. if cmd.NArg() < 1 {
  369. cmd.Usage()
  370. return nil
  371. }
  372. body, err := apiPost("stop", cmd.Args())
  373. if err != nil {
  374. return err
  375. }
  376. var out []string
  377. err = json.Unmarshal(body, &out)
  378. if err != nil {
  379. return err
  380. }
  381. for _, name := range out {
  382. fmt.Println(name)
  383. }
  384. return nil
  385. }
  386. func cmdVersion(args []string) error {
  387. cmd := Subcmd("version", "", "Show the docker version information.")
  388. if err := cmd.Parse(args); err != nil {
  389. return nil
  390. }
  391. if cmd.NArg() > 0 {
  392. cmd.Usage()
  393. return nil
  394. }
  395. body, err := apiGet("version")
  396. if err != nil {
  397. return err
  398. }
  399. var out VersionOut
  400. err = json.Unmarshal(body, &out)
  401. if err != nil {
  402. return err
  403. }
  404. fmt.Println("Version:", out.Version)
  405. fmt.Println("Git Commit:", out.GitCommit)
  406. if out.MemoryLimitDisabled {
  407. fmt.Println("Memory limit disabled")
  408. }
  409. return nil
  410. }
  411. func apiGet(path string) ([]byte, error) {
  412. resp, err := http.Get("http://0.0.0.0:4243/" + path)
  413. if err != nil {
  414. return nil, err
  415. }
  416. defer resp.Body.Close()
  417. body, err := ioutil.ReadAll(resp.Body)
  418. if err != nil {
  419. return nil, err
  420. }
  421. if resp.StatusCode != 200 {
  422. return nil, fmt.Errorf("error: %s", body)
  423. }
  424. return body, nil
  425. }
  426. func apiPost(path string, data interface{}) ([]byte, error) {
  427. buf, err := json.Marshal(data)
  428. if err != nil {
  429. return nil, err
  430. }
  431. dataBuf := bytes.NewBuffer(buf)
  432. resp, err := http.Post("http://0.0.0.0:4243/"+path, "application/json", dataBuf)
  433. if err != nil {
  434. return nil, err
  435. }
  436. defer resp.Body.Close()
  437. body, err := ioutil.ReadAll(resp.Body)
  438. if err != nil {
  439. return nil, err
  440. }
  441. if resp.StatusCode != 200 {
  442. return nil, fmt.Errorf("[error] %s", body)
  443. }
  444. return body, nil
  445. }
  446. func apiPostHijack(path string, data interface{}) (io.ReadCloser, error) {
  447. buf, err := json.Marshal(data)
  448. if err != nil {
  449. return nil, err
  450. }
  451. dataBuf := bytes.NewBuffer(buf)
  452. resp, err := http.Post("http://0.0.0.0:4243/"+path, "application/json", dataBuf)
  453. if err != nil {
  454. return nil, err
  455. }
  456. //TODO check status code
  457. return resp.Body, nil
  458. }
  459. func Subcmd(name, signature, description string) *flag.FlagSet {
  460. flags := flag.NewFlagSet(name, flag.ContinueOnError)
  461. flags.Usage = func() {
  462. fmt.Printf("\nUsage: docker %s %s\n\n%s\n\n", name, signature, description)
  463. flags.PrintDefaults()
  464. }
  465. return flags
  466. }