commands.go 25 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072
  1. package docker
  2. import (
  3. _"bytes"
  4. "encoding/json"
  5. "flag"
  6. "fmt"
  7. _"io"
  8. "io/ioutil"
  9. "net/http"
  10. "net/url"
  11. "os"
  12. "strconv"
  13. "text/tabwriter"
  14. )
  15. const VERSION = "0.1.4"
  16. var (
  17. GIT_COMMIT string
  18. NO_MEMORY_LIMIT bool
  19. )
  20. func ParseCommands(args []string) error {
  21. cmds := map[string]func(args []string) error{
  22. "images": CmdImages,
  23. "info": CmdInfo,
  24. "history": CmdHistory,
  25. "kill": CmdKill,
  26. "logs": CmdLogs,
  27. "ps": CmdPs,
  28. "restart": CmdRestart,
  29. "rm": CmdRm,
  30. "rmi": CmdRmi,
  31. "start": CmdStart,
  32. "stop": CmdStop,
  33. "version": CmdVersion,
  34. }
  35. if len(args) > 0 {
  36. cmd, exists := cmds[args[0]]
  37. if !exists {
  38. //TODO display commend not found
  39. return cmdHelp(args)
  40. }
  41. return cmd(args[1:])
  42. }
  43. return cmdHelp(args)
  44. }
  45. func cmdHelp(args []string) error {
  46. help := "Usage: docker COMMAND [arg...]\n\nA self-sufficient runtime for linux containers.\n\nCommands:\n"
  47. for _, cmd := range [][]string{
  48. // {"attach", "Attach to a running container"},
  49. // {"commit", "Create a new image from a container's changes"},
  50. // {"diff", "Inspect changes on a container's filesystem"},
  51. // {"export", "Stream the contents of a container as a tar archive"},
  52. {"history", "Show the history of an image"},
  53. {"images", "List images"},
  54. // {"import", "Create a new filesystem image from the contents of a tarball"},
  55. {"info", "Display system-wide information"},
  56. // {"inspect", "Return low-level information on a container"},
  57. {"kill", "Kill a running container"},
  58. // {"login", "Register or Login to the docker registry server"},
  59. {"logs", "Fetch the logs of a container"},
  60. // {"port", "Lookup the public-facing port which is NAT-ed to PRIVATE_PORT"},
  61. {"ps", "List containers"},
  62. // {"pull", "Pull an image or a repository from the docker registry server"},
  63. // {"push", "Push an image or a repository to the docker registry server"},
  64. {"restart", "Restart a running container"},
  65. {"rm", "Remove a container"},
  66. {"rmi", "Remove an image"},
  67. // {"run", "Run a command in a new container"},
  68. {"start", "Start a stopped container"},
  69. {"stop", "Stop a running container"},
  70. // {"tag", "Tag an image into a repository"},
  71. {"version", "Show the docker version information"},
  72. // {"wait", "Block until a container stops, then print its exit code"},
  73. } {
  74. help += fmt.Sprintf(" %-10.10s%s\n", cmd[0], cmd[1])
  75. }
  76. fmt.Println(help)
  77. return nil
  78. }
  79. /*
  80. // 'docker login': login / register a user to registry service.
  81. func (srv *Server) CmdLogin(stdin io.ReadCloser, stdout rcli.DockerConn, args ...string) error {
  82. // Read a line on raw terminal with support for simple backspace
  83. // sequences and echo.
  84. //
  85. // This function is necessary because the login command must be done in a
  86. // raw terminal for two reasons:
  87. // - we have to read a password (without echoing it);
  88. // - the rcli "protocol" only supports cannonical and raw modes and you
  89. // can't tune it once the command as been started.
  90. var readStringOnRawTerminal = func(stdin io.Reader, stdout io.Writer, echo bool) string {
  91. char := make([]byte, 1)
  92. buffer := make([]byte, 64)
  93. var i = 0
  94. for i < len(buffer) {
  95. n, err := stdin.Read(char)
  96. if n > 0 {
  97. if char[0] == '\r' || char[0] == '\n' {
  98. stdout.Write([]byte{'\r', '\n'})
  99. break
  100. } else if char[0] == 127 || char[0] == '\b' {
  101. if i > 0 {
  102. if echo {
  103. stdout.Write([]byte{'\b', ' ', '\b'})
  104. }
  105. i--
  106. }
  107. } else if !unicode.IsSpace(rune(char[0])) &&
  108. !unicode.IsControl(rune(char[0])) {
  109. if echo {
  110. stdout.Write(char)
  111. }
  112. buffer[i] = char[0]
  113. i++
  114. }
  115. }
  116. if err != nil {
  117. if err != io.EOF {
  118. fmt.Fprintf(stdout, "Read error: %v\r\n", err)
  119. }
  120. break
  121. }
  122. }
  123. return string(buffer[:i])
  124. }
  125. var readAndEchoString = func(stdin io.Reader, stdout io.Writer) string {
  126. return readStringOnRawTerminal(stdin, stdout, true)
  127. }
  128. var readString = func(stdin io.Reader, stdout io.Writer) string {
  129. return readStringOnRawTerminal(stdin, stdout, false)
  130. }
  131. stdout.SetOptionRawTerminal()
  132. cmd := rcli.Subcmd(stdout, "login", "", "Register or Login to the docker registry server")
  133. if err := cmd.Parse(args); err != nil {
  134. return nil
  135. }
  136. var username string
  137. var password string
  138. var email string
  139. fmt.Fprint(stdout, "Username (", srv.runtime.authConfig.Username, "): ")
  140. username = readAndEchoString(stdin, stdout)
  141. if username == "" {
  142. username = srv.runtime.authConfig.Username
  143. }
  144. if username != srv.runtime.authConfig.Username {
  145. fmt.Fprint(stdout, "Password: ")
  146. password = readString(stdin, stdout)
  147. if password == "" {
  148. return fmt.Errorf("Error : Password Required")
  149. }
  150. fmt.Fprint(stdout, "Email (", srv.runtime.authConfig.Email, "): ")
  151. email = readAndEchoString(stdin, stdout)
  152. if email == "" {
  153. email = srv.runtime.authConfig.Email
  154. }
  155. } else {
  156. password = srv.runtime.authConfig.Password
  157. email = srv.runtime.authConfig.Email
  158. }
  159. newAuthConfig := auth.NewAuthConfig(username, password, email, srv.runtime.root)
  160. status, err := auth.Login(newAuthConfig)
  161. if err != nil {
  162. fmt.Fprintf(stdout, "Error: %s\r\n", err)
  163. } else {
  164. srv.runtime.authConfig = newAuthConfig
  165. }
  166. if status != "" {
  167. fmt.Fprint(stdout, status)
  168. }
  169. return nil
  170. }
  171. */
  172. /*
  173. // 'docker wait': block until a container stops
  174. func (srv *Server) CmdWait(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
  175. cmd := rcli.Subcmd(stdout, "wait", "CONTAINER [CONTAINER...]", "Block until a container stops, then print its exit code.")
  176. if err := cmd.Parse(args); err != nil {
  177. return nil
  178. }
  179. if cmd.NArg() < 1 {
  180. cmd.Usage()
  181. return nil
  182. }
  183. for _, name := range cmd.Args() {
  184. if container := srv.runtime.Get(name); container != nil {
  185. fmt.Fprintln(stdout, container.Wait())
  186. } else {
  187. return fmt.Errorf("No such container: %s", name)
  188. }
  189. }
  190. return nil
  191. }
  192. */
  193. // 'docker version': show version information
  194. func CmdVersion(args []string) error {
  195. cmd := Subcmd("version", "", "Show the docker version information.")
  196. if err := cmd.Parse(args); err != nil {
  197. return nil
  198. }
  199. if cmd.NArg() > 0 {
  200. cmd.Usage()
  201. return nil
  202. }
  203. body, err := call("GET", "version")
  204. if err != nil {
  205. return err
  206. }
  207. var out VersionOut
  208. err = json.Unmarshal(body, &out)
  209. if err != nil {
  210. return err
  211. }
  212. fmt.Println("Version:", out.Version)
  213. fmt.Println("Git Commit:", out.GitCommit)
  214. if out.MemoryLimitDisabled {
  215. fmt.Println("Memory limit disabled")
  216. }
  217. return nil
  218. }
  219. // 'docker info': display system-wide information.
  220. func CmdInfo(args []string) error {
  221. cmd := Subcmd("info", "", "Display system-wide information")
  222. if err := cmd.Parse(args); err != nil {
  223. return nil
  224. }
  225. if cmd.NArg() > 0 {
  226. cmd.Usage()
  227. return nil
  228. }
  229. body, err := call("GET", "info")
  230. if err != nil {
  231. return err
  232. }
  233. var out InfoOut
  234. err = json.Unmarshal(body, &out)
  235. if err != nil {
  236. return err
  237. }
  238. fmt.Printf("containers: %d\nversion: %s\nimages: %d\n", out.Containers, out.Version, out.Images)
  239. if out.Debug {
  240. fmt.Println("debug mode enabled")
  241. fmt.Printf("fds: %d\ngoroutines: %d\n", out.NFd, out.NGoroutines)
  242. }
  243. return nil
  244. }
  245. func CmdStop(args []string) error {
  246. cmd := Subcmd("stop", "CONTAINER [CONTAINER...]", "Stop a running container")
  247. if err := cmd.Parse(args); err != nil {
  248. return nil
  249. }
  250. if cmd.NArg() < 1 {
  251. cmd.Usage()
  252. return nil
  253. }
  254. for _, name := range args {
  255. _, err := call("GET", "/containers/"+name+"/stop")
  256. if err != nil {
  257. fmt.Printf("%s", err)
  258. } else {
  259. fmt.Println(name)
  260. }
  261. }
  262. return nil
  263. }
  264. func CmdRestart(args []string) error {
  265. cmd := Subcmd("restart", "CONTAINER [CONTAINER...]", "Restart a running container")
  266. if err := cmd.Parse(args); err != nil {
  267. return nil
  268. }
  269. if cmd.NArg() < 1 {
  270. cmd.Usage()
  271. return nil
  272. }
  273. for _, name := range args {
  274. _, err := call("GET", "/containers/"+name+"/restart")
  275. if err != nil {
  276. fmt.Printf("%s", err)
  277. } else {
  278. fmt.Println(name)
  279. }
  280. }
  281. return nil
  282. }
  283. func CmdStart(args []string) error {
  284. cmd := Subcmd("start", "CONTAINER [CONTAINER...]", "Restart a stopped container")
  285. if err := cmd.Parse(args); err != nil {
  286. return nil
  287. }
  288. if cmd.NArg() < 1 {
  289. cmd.Usage()
  290. return nil
  291. }
  292. for _, name := range args {
  293. _, err := call("GET", "/containers/"+name+"/start")
  294. if err != nil {
  295. fmt.Printf("%s", err)
  296. } else {
  297. fmt.Println(name)
  298. }
  299. }
  300. return nil
  301. }
  302. /*
  303. func (srv *Server) CmdInspect(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
  304. cmd := rcli.Subcmd(stdout, "inspect", "CONTAINER", "Return low-level information on a container")
  305. if err := cmd.Parse(args); err != nil {
  306. return nil
  307. }
  308. if cmd.NArg() < 1 {
  309. cmd.Usage()
  310. return nil
  311. }
  312. name := cmd.Arg(0)
  313. var obj interface{}
  314. if container := srv.runtime.Get(name); container != nil {
  315. obj = container
  316. } else if image, err := srv.runtime.repositories.LookupImage(name); err == nil && image != nil {
  317. obj = image
  318. } else {
  319. // No output means the object does not exist
  320. // (easier to script since stdout and stderr are not differentiated atm)
  321. return nil
  322. }
  323. data, err := json.Marshal(obj)
  324. if err != nil {
  325. return err
  326. }
  327. indented := new(bytes.Buffer)
  328. if err = json.Indent(indented, data, "", " "); err != nil {
  329. return err
  330. }
  331. if _, err := io.Copy(stdout, indented); err != nil {
  332. return err
  333. }
  334. stdout.Write([]byte{'\n'})
  335. return nil
  336. }
  337. */
  338. /*
  339. func (srv *Server) CmdPort(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
  340. cmd := rcli.Subcmd(stdout, "port", "CONTAINER PRIVATE_PORT", "Lookup the public-facing port which is NAT-ed to PRIVATE_PORT")
  341. if err := cmd.Parse(args); err != nil {
  342. return nil
  343. }
  344. if cmd.NArg() != 2 {
  345. cmd.Usage()
  346. return nil
  347. }
  348. name := cmd.Arg(0)
  349. privatePort := cmd.Arg(1)
  350. if container := srv.runtime.Get(name); container == nil {
  351. return fmt.Errorf("No such container: %s", name)
  352. } else {
  353. if frontend, exists := container.NetworkSettings.PortMapping[privatePort]; !exists {
  354. return fmt.Errorf("No private port '%s' allocated on %s", privatePort, name)
  355. } else {
  356. fmt.Fprintln(stdout, frontend)
  357. }
  358. }
  359. return nil
  360. }
  361. */
  362. // 'docker rmi IMAGE' removes all images with the name IMAGE
  363. func CmdRmi(args []string) error {
  364. cmd := Subcmd("rmi", "IMAGE [IMAGE...]", "Remove an image")
  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. for _, name := range args {
  373. _, err := call("DELETE", "/images/"+name)
  374. if err != nil {
  375. fmt.Printf("%s", err)
  376. } else {
  377. fmt.Println(name)
  378. }
  379. }
  380. return nil
  381. }
  382. func CmdHistory(args []string) error {
  383. cmd := Subcmd("history", "IMAGE", "Show the history of an image")
  384. if err := cmd.Parse(args); err != nil {
  385. return nil
  386. }
  387. if cmd.NArg() != 1 {
  388. cmd.Usage()
  389. return nil
  390. }
  391. body, err := call("GET", "images/"+cmd.Arg(0)+"/history")
  392. if err != nil {
  393. return err
  394. }
  395. var outs []HistoryOut
  396. err = json.Unmarshal(body, &outs)
  397. if err != nil {
  398. return err
  399. }
  400. w := tabwriter.NewWriter(os.Stdout, 20, 1, 3, ' ', 0)
  401. fmt.Fprintln(w, "ID\tCREATED\tCREATED BY")
  402. for _, out := range outs {
  403. fmt.Fprintf(w, "%s\t%s\t%s\n", out.Id, out.Created, out.CreatedBy)
  404. }
  405. w.Flush()
  406. return nil
  407. }
  408. func CmdRm(args []string) error {
  409. cmd := Subcmd("rm", "CONTAINER [CONTAINER...]", "Remove a container")
  410. if err := cmd.Parse(args); err != nil {
  411. return nil
  412. }
  413. if cmd.NArg() < 1 {
  414. cmd.Usage()
  415. return nil
  416. }
  417. for _, name := range args {
  418. _, err := call("DELETE", "/containers/"+name)
  419. if err != nil {
  420. fmt.Printf("%s", err)
  421. } else {
  422. fmt.Println(name)
  423. }
  424. }
  425. return nil
  426. }
  427. // 'docker kill NAME' kills a running container
  428. func CmdKill(args []string) error {
  429. cmd := Subcmd("kill", "CONTAINER [CONTAINER...]", "Kill a running container")
  430. if err := cmd.Parse(args); err != nil {
  431. return nil
  432. }
  433. if cmd.NArg() < 1 {
  434. cmd.Usage()
  435. return nil
  436. }
  437. for _, name := range args {
  438. _, err := call("POST", "/containers/"+name+"/kill")
  439. if err != nil {
  440. fmt.Printf("%s", err)
  441. } else {
  442. fmt.Println(name)
  443. }
  444. }
  445. return nil
  446. }
  447. /*
  448. func (srv *Server) CmdImport(stdin io.ReadCloser, stdout rcli.DockerConn, args ...string) error {
  449. stdout.Flush()
  450. cmd := rcli.Subcmd(stdout, "import", "URL|- [REPOSITORY [TAG]]", "Create a new filesystem image from the contents of a tarball")
  451. var archive io.Reader
  452. var resp *http.Response
  453. if err := cmd.Parse(args); err != nil {
  454. return nil
  455. }
  456. if cmd.NArg() < 1 {
  457. cmd.Usage()
  458. return nil
  459. }
  460. src := cmd.Arg(0)
  461. if src == "-" {
  462. archive = stdin
  463. } else {
  464. u, err := url.Parse(src)
  465. if err != nil {
  466. return err
  467. }
  468. if u.Scheme == "" {
  469. u.Scheme = "http"
  470. u.Host = src
  471. u.Path = ""
  472. }
  473. fmt.Fprintln(stdout, "Downloading from", u)
  474. // Download with curl (pretty progress bar)
  475. // If curl is not available, fallback to http.Get()
  476. resp, err = Download(u.String(), stdout)
  477. if err != nil {
  478. return err
  479. }
  480. archive = ProgressReader(resp.Body, int(resp.ContentLength), stdout)
  481. }
  482. img, err := srv.runtime.graph.Create(archive, nil, "Imported from "+src)
  483. if err != nil {
  484. return err
  485. }
  486. // Optionally register the image at REPO/TAG
  487. if repository := cmd.Arg(1); repository != "" {
  488. tag := cmd.Arg(2) // Repository will handle an empty tag properly
  489. if err := srv.runtime.repositories.Set(repository, tag, img.Id, true); err != nil {
  490. return err
  491. }
  492. }
  493. fmt.Fprintln(stdout, img.ShortId())
  494. return nil
  495. }
  496. */
  497. /*
  498. func (srv *Server) CmdPush(stdin io.ReadCloser, stdout rcli.DockerConn, args ...string) error {
  499. cmd := rcli.Subcmd(stdout, "push", "NAME", "Push an image or a repository to the registry")
  500. if err := cmd.Parse(args); err != nil {
  501. return nil
  502. }
  503. local := cmd.Arg(0)
  504. if local == "" {
  505. cmd.Usage()
  506. return nil
  507. }
  508. // If the login failed, abort
  509. if srv.runtime.authConfig == nil || srv.runtime.authConfig.Username == "" {
  510. if err := srv.CmdLogin(stdin, stdout, args...); err != nil {
  511. return err
  512. }
  513. if srv.runtime.authConfig == nil || srv.runtime.authConfig.Username == "" {
  514. return fmt.Errorf("Please login prior to push. ('docker login')")
  515. }
  516. }
  517. var remote string
  518. tmp := strings.SplitN(local, "/", 2)
  519. if len(tmp) == 1 {
  520. return fmt.Errorf(
  521. "Impossible to push a \"root\" repository. Please rename your repository in <user>/<repo> (ex: %s/%s)",
  522. srv.runtime.authConfig.Username, local)
  523. } else {
  524. remote = local
  525. }
  526. Debugf("Pushing [%s] to [%s]\n", local, remote)
  527. // Try to get the image
  528. // FIXME: Handle lookup
  529. // FIXME: Also push the tags in case of ./docker push myrepo:mytag
  530. // img, err := srv.runtime.LookupImage(cmd.Arg(0))
  531. img, err := srv.runtime.graph.Get(local)
  532. if err != nil {
  533. Debugf("The push refers to a repository [%s] (len: %d)\n", local, len(srv.runtime.repositories.Repositories[local]))
  534. // If it fails, try to get the repository
  535. if localRepo, exists := srv.runtime.repositories.Repositories[local]; exists {
  536. if err := srv.runtime.graph.PushRepository(stdout, remote, localRepo, srv.runtime.authConfig); err != nil {
  537. return err
  538. }
  539. return nil
  540. }
  541. return err
  542. }
  543. err = srv.runtime.graph.PushImage(stdout, img, srv.runtime.authConfig)
  544. if err != nil {
  545. return err
  546. }
  547. return nil
  548. }
  549. */
  550. /*
  551. func (srv *Server) CmdPull(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
  552. cmd := rcli.Subcmd(stdout, "pull", "NAME", "Pull an image or a repository from the registry")
  553. if err := cmd.Parse(args); err != nil {
  554. return nil
  555. }
  556. remote := cmd.Arg(0)
  557. if remote == "" {
  558. cmd.Usage()
  559. return nil
  560. }
  561. // FIXME: CmdPull should be a wrapper around Runtime.Pull()
  562. if srv.runtime.graph.LookupRemoteImage(remote, srv.runtime.authConfig) {
  563. // if err := srv.runtime.graph.PullImage(stdout, remote, srv.runtime.authConfig); err != nil {
  564. // return err
  565. // }
  566. return nil
  567. }
  568. // FIXME: Allow pull repo:tag
  569. //if err := srv.runtime.graph.PullRepository(stdout, remote, "", srv.runtime.repositories, srv.runtime.authConfig); err != nil {
  570. // return err
  571. //}
  572. return nil
  573. }
  574. */
  575. func CmdImages(args []string) error {
  576. cmd := Subcmd("images", "[OPTIONS] [NAME]", "List images")
  577. quiet := cmd.Bool("q", false, "only show numeric IDs")
  578. all := cmd.Bool("a", false, "show all images")
  579. if err := cmd.Parse(args); err != nil {
  580. return nil
  581. }
  582. if cmd.NArg() > 1 {
  583. cmd.Usage()
  584. return nil
  585. }
  586. v := url.Values{}
  587. if cmd.NArg() == 1 {
  588. v.Set("filter", cmd.Arg(0))
  589. }
  590. if *quiet {
  591. v.Set("quiet", "true")
  592. }
  593. if *all {
  594. v.Set("all", "true")
  595. }
  596. body, err := call("GET", "images?"+v.Encode())
  597. if err != nil {
  598. return err
  599. }
  600. var outs []ImagesOut
  601. err = json.Unmarshal(body, &outs)
  602. if err != nil {
  603. return err
  604. }
  605. w := tabwriter.NewWriter(os.Stdout, 20, 1, 3, ' ', 0)
  606. if !*quiet {
  607. fmt.Fprintln(w, "REPOSITORY\tTAG\tID\tCREATED")
  608. }
  609. for _, out := range outs {
  610. if !*quiet {
  611. fmt.Fprintf(w, "%s\t%s\t%s\t%s\n", out.Repository, out.Tag, out.Id, out.Created)
  612. } else {
  613. fmt.Fprintln(w, out.Id)
  614. }
  615. }
  616. if !*quiet {
  617. w.Flush()
  618. }
  619. return nil
  620. }
  621. func CmdPs(args []string) error {
  622. cmd := Subcmd("ps", "[OPTIONS]", "List containers")
  623. quiet := cmd.Bool("q", false, "Only display numeric IDs")
  624. all := cmd.Bool("a", false, "Show all containers. Only running containers are shown by default.")
  625. noTrunc := cmd.Bool("notrunc", false, "Don't truncate output")
  626. nLatest := cmd.Bool("l", false, "Show only the latest created container, include non-running ones.")
  627. last := cmd.Int("n", -1, "Show n last created containers, include non-running ones.")
  628. if err := cmd.Parse(args); err != nil {
  629. return nil
  630. }
  631. v := url.Values{}
  632. if *last == -1 && *nLatest {
  633. *last = 1
  634. }
  635. if *quiet {
  636. v.Set("quiet", "true")
  637. }
  638. if *all {
  639. v.Set("all", "true")
  640. }
  641. if *noTrunc {
  642. v.Set("notrunc", "true")
  643. }
  644. if *last != -1 {
  645. v.Set("n", strconv.Itoa(*last))
  646. }
  647. body, err := call("GET", "containers?"+v.Encode())
  648. if err != nil {
  649. return err
  650. }
  651. var outs []PsOut
  652. err = json.Unmarshal(body, &outs)
  653. if err != nil {
  654. return err
  655. }
  656. w := tabwriter.NewWriter(os.Stdout, 20, 1, 3, ' ', 0)
  657. if !*quiet {
  658. fmt.Fprintln(w, "ID\tIMAGE\tCOMMAND\tCREATED\tSTATUS")
  659. }
  660. for _, out := range outs {
  661. if !*quiet {
  662. fmt.Fprintf(w, "%s\t%s\t%s\t%s\n", out.Id, out.Image, out.Command, out.Status, out.Created)
  663. } else {
  664. fmt.Fprintln(w, out.Id)
  665. }
  666. }
  667. if !*quiet {
  668. w.Flush()
  669. }
  670. return nil
  671. }
  672. /*
  673. func (srv *Server) CmdCommit(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
  674. cmd := rcli.Subcmd(stdout,
  675. "commit", "[OPTIONS] CONTAINER [REPOSITORY [TAG]]",
  676. "Create a new image from a container's changes")
  677. flComment := cmd.String("m", "", "Commit message")
  678. if err := cmd.Parse(args); err != nil {
  679. return nil
  680. }
  681. containerName, repository, tag := cmd.Arg(0), cmd.Arg(1), cmd.Arg(2)
  682. if containerName == "" {
  683. cmd.Usage()
  684. return nil
  685. }
  686. img, err := srv.runtime.Commit(containerName, repository, tag, *flComment)
  687. if err != nil {
  688. return err
  689. }
  690. fmt.Fprintln(stdout, img.ShortId())
  691. return nil
  692. }
  693. */
  694. /*
  695. func (srv *Server) CmdExport(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
  696. cmd := rcli.Subcmd(stdout,
  697. "export", "CONTAINER",
  698. "Export the contents of a filesystem as a tar archive")
  699. if err := cmd.Parse(args); err != nil {
  700. return nil
  701. }
  702. name := cmd.Arg(0)
  703. if container := srv.runtime.Get(name); container != nil {
  704. data, err := container.Export()
  705. if err != nil {
  706. return err
  707. }
  708. // Stream the entire contents of the container (basically a volatile snapshot)
  709. if _, err := io.Copy(stdout, data); err != nil {
  710. return err
  711. }
  712. return nil
  713. }
  714. return fmt.Errorf("No such container: %s", name)
  715. }
  716. */
  717. /*
  718. func (srv *Server) CmdDiff(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
  719. cmd := rcli.Subcmd(stdout,
  720. "diff", "CONTAINER",
  721. "Inspect changes on a container's filesystem")
  722. if err := cmd.Parse(args); err != nil {
  723. return nil
  724. }
  725. if cmd.NArg() < 1 {
  726. cmd.Usage()
  727. return nil
  728. }
  729. if container := srv.runtime.Get(cmd.Arg(0)); container == nil {
  730. return fmt.Errorf("No such container")
  731. } else {
  732. changes, err := container.Changes()
  733. if err != nil {
  734. return err
  735. }
  736. for _, change := range changes {
  737. fmt.Fprintln(stdout, change.String())
  738. }
  739. }
  740. return nil
  741. }
  742. */
  743. func CmdLogs(args []string) error {
  744. cmd := Subcmd("logs", "CONTAINER", "Fetch the logs of a container")
  745. if err := cmd.Parse(args); err != nil {
  746. return nil
  747. }
  748. if cmd.NArg() != 1 {
  749. cmd.Usage()
  750. return nil
  751. }
  752. body, err := call("GET", "containers/"+cmd.Arg(0)+"/logs")
  753. if err != nil {
  754. return err
  755. }
  756. var out LogsOut
  757. err = json.Unmarshal(body, &out)
  758. if err != nil {
  759. return err
  760. }
  761. fmt.Fprintln(os.Stdout, out.Stdout)
  762. fmt.Fprintln(os.Stderr, out.Stderr)
  763. return nil
  764. }
  765. /*
  766. func (srv *Server) CmdAttach(stdin io.ReadCloser, stdout rcli.DockerConn, args ...string) error {
  767. cmd := rcli.Subcmd(stdout, "attach", "CONTAINER", "Attach to a running container")
  768. if err := cmd.Parse(args); err != nil {
  769. return nil
  770. }
  771. if cmd.NArg() != 1 {
  772. cmd.Usage()
  773. return nil
  774. }
  775. name := cmd.Arg(0)
  776. container := srv.runtime.Get(name)
  777. if container == nil {
  778. return fmt.Errorf("No such container: %s", name)
  779. }
  780. if container.Config.Tty {
  781. stdout.SetOptionRawTerminal()
  782. }
  783. // Flush the options to make sure the client sets the raw mode
  784. stdout.Flush()
  785. return <-container.Attach(stdin, nil, stdout, stdout)
  786. }
  787. */
  788. /*
  789. // Ports type - Used to parse multiple -p flags
  790. type ports []int
  791. func (p *ports) String() string {
  792. return fmt.Sprint(*p)
  793. }
  794. func (p *ports) Set(value string) error {
  795. port, err := strconv.Atoi(value)
  796. if err != nil {
  797. return fmt.Errorf("Invalid port: %v", value)
  798. }
  799. *p = append(*p, port)
  800. return nil
  801. }
  802. */
  803. // ListOpts type
  804. type ListOpts []string
  805. func (opts *ListOpts) String() string {
  806. return fmt.Sprint(*opts)
  807. }
  808. func (opts *ListOpts) Set(value string) error {
  809. *opts = append(*opts, value)
  810. return nil
  811. }
  812. // AttachOpts stores arguments to 'docker run -a', eg. which streams to attach to
  813. type AttachOpts map[string]bool
  814. func NewAttachOpts() AttachOpts {
  815. return make(AttachOpts)
  816. }
  817. func (opts AttachOpts) String() string {
  818. // Cast to underlying map type to avoid infinite recursion
  819. return fmt.Sprintf("%v", map[string]bool(opts))
  820. }
  821. func (opts AttachOpts) Set(val string) error {
  822. if val != "stdin" && val != "stdout" && val != "stderr" {
  823. return fmt.Errorf("Unsupported stream name: %s", val)
  824. }
  825. opts[val] = true
  826. return nil
  827. }
  828. func (opts AttachOpts) Get(val string) bool {
  829. if res, exists := opts[val]; exists {
  830. return res
  831. }
  832. return false
  833. }
  834. /*
  835. func (srv *Server) CmdTag(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
  836. cmd := rcli.Subcmd(stdout, "tag", "[OPTIONS] IMAGE REPOSITORY [TAG]", "Tag an image into a repository")
  837. force := cmd.Bool("f", false, "Force")
  838. if err := cmd.Parse(args); err != nil {
  839. return nil
  840. }
  841. if cmd.NArg() < 2 {
  842. cmd.Usage()
  843. return nil
  844. }
  845. return srv.runtime.repositories.Set(cmd.Arg(1), cmd.Arg(2), cmd.Arg(0), *force)
  846. }
  847. */
  848. /*
  849. func (srv *Server) CmdRun(stdin io.ReadCloser, stdout rcli.DockerConn, args ...string) error {
  850. config, err := ParseRun(args)
  851. if err != nil {
  852. return err
  853. }
  854. if config.Image == "" {
  855. fmt.Fprintln(stdout, "Error: Image not specified")
  856. return fmt.Errorf("Image not specified")
  857. }
  858. if len(config.Cmd) == 0 {
  859. fmt.Fprintln(stdout, "Error: Command not specified")
  860. return fmt.Errorf("Command not specified")
  861. }
  862. if config.Tty {
  863. stdout.SetOptionRawTerminal()
  864. }
  865. // Flush the options to make sure the client sets the raw mode
  866. // or tell the client there is no options
  867. stdout.Flush()
  868. // Create new container
  869. container, err := srv.runtime.Create(config)
  870. if err != nil {
  871. // If container not found, try to pull it
  872. if srv.runtime.graph.IsNotExist(err) {
  873. fmt.Fprintf(stdout, "Image %s not found, trying to pull it from registry.\r\n", config.Image)
  874. if err = srv.CmdPull(stdin, stdout, config.Image); err != nil {
  875. return err
  876. }
  877. if container, err = srv.runtime.Create(config); err != nil {
  878. return err
  879. }
  880. } else {
  881. return err
  882. }
  883. }
  884. var (
  885. cStdin io.ReadCloser
  886. cStdout, cStderr io.Writer
  887. )
  888. if config.AttachStdin {
  889. r, w := io.Pipe()
  890. go func() {
  891. defer w.Close()
  892. defer Debugf("Closing buffered stdin pipe")
  893. io.Copy(w, stdin)
  894. }()
  895. cStdin = r
  896. }
  897. if config.AttachStdout {
  898. cStdout = stdout
  899. }
  900. if config.AttachStderr {
  901. cStderr = stdout // FIXME: rcli can't differentiate stdout from stderr
  902. }
  903. attachErr := container.Attach(cStdin, stdin, cStdout, cStderr)
  904. Debugf("Starting\n")
  905. if err := container.Start(); err != nil {
  906. return err
  907. }
  908. if cStdout == nil && cStderr == nil {
  909. fmt.Fprintln(stdout, container.ShortId())
  910. }
  911. Debugf("Waiting for attach to return\n")
  912. <-attachErr
  913. // Expecting I/O pipe error, discarding
  914. return nil
  915. }
  916. */
  917. func call(method, path string) ([]byte, error) {
  918. req, err := http.NewRequest(method, "http://0.0.0.0:4243/" + path, nil)
  919. if err != nil {
  920. return nil, err
  921. }
  922. resp, err := http.DefaultClient.Do(req)
  923. if err != nil {
  924. return nil, err
  925. }
  926. defer resp.Body.Close()
  927. body, err := ioutil.ReadAll(resp.Body)
  928. if err != nil {
  929. return nil, err
  930. }
  931. if resp.StatusCode != 200 {
  932. return nil, fmt.Errorf("error: %s", body)
  933. }
  934. return body, nil
  935. }
  936. /*
  937. func apiPost(path string, data interface{}) ([]byte, error) {
  938. buf, err := json.Marshal(data)
  939. if err != nil {
  940. return nil, err
  941. }
  942. dataBuf := bytes.NewBuffer(buf)
  943. resp, err := http.Post("http://0.0.0.0:4243/"+path, "application/json", dataBuf)
  944. if err != nil {
  945. return nil, err
  946. }
  947. defer resp.Body.Close()
  948. body, err := ioutil.ReadAll(resp.Body)
  949. if err != nil {
  950. return nil, err
  951. }
  952. if resp.StatusCode != 200 {
  953. return nil, fmt.Errorf("[error] %s", body)
  954. }
  955. return body, nil
  956. }
  957. func apiPostHijack(path string, data interface{}) (io.ReadCloser, error) {
  958. buf, err := json.Marshal(data)
  959. if err != nil {
  960. return nil, err
  961. }
  962. dataBuf := bytes.NewBuffer(buf)
  963. resp, err := http.Post("http://0.0.0.0:4243/"+path, "application/json", dataBuf)
  964. if err != nil {
  965. return nil, err
  966. }
  967. //TODO check status code
  968. return resp.Body, nil
  969. }
  970. */
  971. func Subcmd(name, signature, description string) *flag.FlagSet {
  972. flags := flag.NewFlagSet(name, flag.ContinueOnError)
  973. flags.Usage = func() {
  974. fmt.Printf("\nUsage: docker %s %s\n\n%s\n\n", name, signature, description)
  975. flags.PrintDefaults()
  976. }
  977. return flags
  978. }