commands.go 27 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006
  1. package commands
  2. import (
  3. "bufio"
  4. "bytes"
  5. "encoding/json"
  6. "errors"
  7. "fmt"
  8. "github.com/dotcloud/docker"
  9. "github.com/dotcloud/docker/fs"
  10. "github.com/dotcloud/docker/future"
  11. "github.com/dotcloud/docker/rcli"
  12. "io"
  13. "io/ioutil"
  14. "net/http"
  15. "net/url"
  16. "os"
  17. "path"
  18. "strconv"
  19. "strings"
  20. "sync"
  21. "text/tabwriter"
  22. "time"
  23. )
  24. const VERSION = "0.0.2"
  25. func (srv *Server) Name() string {
  26. return "docker"
  27. }
  28. // FIXME: Stop violating DRY by repeating usage here and in Subcmd declarations
  29. func (srv *Server) Help() string {
  30. help := "Usage: docker COMMAND [arg...]\n\nA self-sufficient runtime for linux containers.\n\nCommands:\n"
  31. for _, cmd := range [][]interface{}{
  32. {"run", "Run a command in a container"},
  33. {"ps", "Display a list of containers"},
  34. {"import", "Create a new filesystem image from the contents of a tarball"},
  35. {"attach", "Attach to a running container"},
  36. {"cat", "Write the contents of a container's file to standard output"},
  37. {"commit", "Create a new image from a container's changes"},
  38. {"cp", "Create a copy of IMAGE and call it NAME"},
  39. {"debug", "(debug only) (No documentation available)"},
  40. {"diff", "Inspect changes on a container's filesystem"},
  41. {"images", "List images"},
  42. {"info", "Display system-wide information"},
  43. {"inspect", "Return low-level information on a container"},
  44. {"kill", "Kill a running container"},
  45. {"layers", "(debug only) List filesystem layers"},
  46. {"logs", "Fetch the logs of a container"},
  47. {"ls", "List the contents of a container's directory"},
  48. {"mirror", "(debug only) (No documentation available)"},
  49. {"port", "Lookup the public-facing port which is NAT-ed to PRIVATE_PORT"},
  50. {"ps", "List containers"},
  51. {"reset", "Reset changes to a container's filesystem"},
  52. {"restart", "Restart a running container"},
  53. {"rm", "Remove a container"},
  54. {"rmimage", "Remove an image"},
  55. {"run", "Run a command in a new container"},
  56. {"start", "Start a stopped container"},
  57. {"stop", "Stop a running container"},
  58. {"tar", "Stream the contents of a container as a tar archive"},
  59. {"umount", "(debug only) Mount a container's filesystem"},
  60. {"version", "Show the docker version information"},
  61. {"wait", "Block until a container stops, then print its exit code"},
  62. {"web", "A web UI for docker"},
  63. {"write", "Write the contents of standard input to a container's file"},
  64. } {
  65. help += fmt.Sprintf(" %-10.10s%s\n", cmd...)
  66. }
  67. return help
  68. }
  69. // 'docker wait': block until a container stops
  70. func (srv *Server) CmdWait(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
  71. cmd := rcli.Subcmd(stdout, "wait", "[OPTIONS] NAME", "Block until a container stops, then print its exit code.")
  72. if err := cmd.Parse(args); err != nil {
  73. return nil
  74. }
  75. if cmd.NArg() < 1 {
  76. cmd.Usage()
  77. return nil
  78. }
  79. for _, name := range cmd.Args() {
  80. if container := srv.containers.Get(name); container != nil {
  81. fmt.Fprintln(stdout, container.Wait())
  82. } else {
  83. return errors.New("No such container: " + name)
  84. }
  85. }
  86. return nil
  87. }
  88. // 'docker version': show version information
  89. func (srv *Server) CmdVersion(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
  90. fmt.Fprintf(stdout, "Version:%s\n", VERSION)
  91. return nil
  92. }
  93. // 'docker info': display system-wide information.
  94. func (srv *Server) CmdInfo(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
  95. images, _ := srv.images.Images()
  96. var imgcount int
  97. if images == nil {
  98. imgcount = 0
  99. } else {
  100. imgcount = len(images)
  101. }
  102. cmd := rcli.Subcmd(stdout, "info", "", "Display system-wide information.")
  103. if err := cmd.Parse(args); err != nil {
  104. return nil
  105. }
  106. if cmd.NArg() > 0 {
  107. cmd.Usage()
  108. return nil
  109. }
  110. fmt.Fprintf(stdout, "containers: %d\nversion: %s\nimages: %d\n",
  111. len(srv.containers.List()),
  112. VERSION,
  113. imgcount)
  114. return nil
  115. }
  116. func (srv *Server) CmdStop(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
  117. cmd := rcli.Subcmd(stdout, "stop", "[OPTIONS] NAME", "Stop a running container")
  118. if err := cmd.Parse(args); err != nil {
  119. return nil
  120. }
  121. if cmd.NArg() < 1 {
  122. cmd.Usage()
  123. return nil
  124. }
  125. for _, name := range cmd.Args() {
  126. if container := srv.containers.Get(name); container != nil {
  127. if err := container.Stop(); err != nil {
  128. return err
  129. }
  130. fmt.Fprintln(stdout, container.Id)
  131. } else {
  132. return errors.New("No such container: " + name)
  133. }
  134. }
  135. return nil
  136. }
  137. func (srv *Server) CmdRestart(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
  138. cmd := rcli.Subcmd(stdout, "restart", "[OPTIONS] NAME", "Restart a running container")
  139. if err := cmd.Parse(args); err != nil {
  140. return nil
  141. }
  142. if cmd.NArg() < 1 {
  143. cmd.Usage()
  144. return nil
  145. }
  146. for _, name := range cmd.Args() {
  147. if container := srv.containers.Get(name); container != nil {
  148. if err := container.Restart(); err != nil {
  149. return err
  150. }
  151. fmt.Fprintln(stdout, container.Id)
  152. } else {
  153. return errors.New("No such container: " + name)
  154. }
  155. }
  156. return nil
  157. }
  158. func (srv *Server) CmdStart(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
  159. cmd := rcli.Subcmd(stdout, "start", "[OPTIONS] NAME", "Start a stopped container")
  160. if err := cmd.Parse(args); err != nil {
  161. return nil
  162. }
  163. if cmd.NArg() < 1 {
  164. cmd.Usage()
  165. return nil
  166. }
  167. for _, name := range cmd.Args() {
  168. if container := srv.containers.Get(name); container != nil {
  169. if err := container.Start(); err != nil {
  170. return err
  171. }
  172. fmt.Fprintln(stdout, container.Id)
  173. } else {
  174. return errors.New("No such container: " + name)
  175. }
  176. }
  177. return nil
  178. }
  179. func (srv *Server) CmdUmount(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
  180. cmd := rcli.Subcmd(stdout, "umount", "[OPTIONS] NAME", "umount a container's filesystem (debug only)")
  181. if err := cmd.Parse(args); err != nil {
  182. return nil
  183. }
  184. if cmd.NArg() < 1 {
  185. cmd.Usage()
  186. return nil
  187. }
  188. for _, name := range cmd.Args() {
  189. if container := srv.containers.Get(name); container != nil {
  190. if err := container.Mountpoint.Umount(); err != nil {
  191. return err
  192. }
  193. fmt.Fprintln(stdout, container.Id)
  194. } else {
  195. return errors.New("No such container: " + name)
  196. }
  197. }
  198. return nil
  199. }
  200. func (srv *Server) CmdMount(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
  201. cmd := rcli.Subcmd(stdout, "umount", "[OPTIONS] NAME", "mount a container's filesystem (debug only)")
  202. if err := cmd.Parse(args); err != nil {
  203. return nil
  204. }
  205. if cmd.NArg() < 1 {
  206. cmd.Usage()
  207. return nil
  208. }
  209. for _, name := range cmd.Args() {
  210. if container := srv.containers.Get(name); container != nil {
  211. if err := container.Mountpoint.EnsureMounted(); err != nil {
  212. return err
  213. }
  214. fmt.Fprintln(stdout, container.Id)
  215. } else {
  216. return errors.New("No such container: " + name)
  217. }
  218. }
  219. return nil
  220. }
  221. func (srv *Server) CmdCat(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
  222. cmd := rcli.Subcmd(stdout, "cat", "[OPTIONS] CONTAINER PATH", "write the contents of a container's file to standard output")
  223. if err := cmd.Parse(args); err != nil {
  224. return nil
  225. }
  226. if cmd.NArg() < 2 {
  227. cmd.Usage()
  228. return nil
  229. }
  230. name, path := cmd.Arg(0), cmd.Arg(1)
  231. if container := srv.containers.Get(name); container != nil {
  232. if f, err := container.Mountpoint.OpenFile(path, os.O_RDONLY, 0); err != nil {
  233. return err
  234. } else if _, err := io.Copy(stdout, f); err != nil {
  235. return err
  236. }
  237. return nil
  238. }
  239. return errors.New("No such container: " + name)
  240. }
  241. func (srv *Server) CmdWrite(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
  242. cmd := rcli.Subcmd(stdout, "write", "[OPTIONS] CONTAINER PATH", "write the contents of standard input to a container's file")
  243. if err := cmd.Parse(args); err != nil {
  244. return nil
  245. }
  246. if cmd.NArg() < 2 {
  247. cmd.Usage()
  248. return nil
  249. }
  250. name, path := cmd.Arg(0), cmd.Arg(1)
  251. if container := srv.containers.Get(name); container != nil {
  252. if f, err := container.Mountpoint.OpenFile(path, os.O_WRONLY|os.O_CREATE, 0600); err != nil {
  253. return err
  254. } else if _, err := io.Copy(f, stdin); err != nil {
  255. return err
  256. }
  257. return nil
  258. }
  259. return errors.New("No such container: " + name)
  260. }
  261. func (srv *Server) CmdLs(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
  262. cmd := rcli.Subcmd(stdout, "ls", "[OPTIONS] CONTAINER PATH", "List the contents of a container's directory")
  263. if err := cmd.Parse(args); err != nil {
  264. return nil
  265. }
  266. if cmd.NArg() < 2 {
  267. cmd.Usage()
  268. return nil
  269. }
  270. name, path := cmd.Arg(0), cmd.Arg(1)
  271. if container := srv.containers.Get(name); container != nil {
  272. if files, err := container.Mountpoint.ReadDir(path); err != nil {
  273. return err
  274. } else {
  275. for _, f := range files {
  276. fmt.Fprintln(stdout, f.Name())
  277. }
  278. }
  279. return nil
  280. }
  281. return errors.New("No such container: " + name)
  282. }
  283. func (srv *Server) CmdInspect(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
  284. cmd := rcli.Subcmd(stdout, "inspect", "[OPTIONS] CONTAINER", "Return low-level information on a 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. name := cmd.Arg(0)
  293. var obj interface{}
  294. if container := srv.containers.Get(name); container != nil {
  295. obj = container
  296. } else if image, err := srv.images.Find(name); err != nil {
  297. return err
  298. } else if image != nil {
  299. obj = image
  300. } else {
  301. // No output means the object does not exist
  302. // (easier to script since stdout and stderr are not differentiated atm)
  303. return nil
  304. }
  305. data, err := json.Marshal(obj)
  306. if err != nil {
  307. return err
  308. }
  309. indented := new(bytes.Buffer)
  310. if err = json.Indent(indented, data, "", " "); err != nil {
  311. return err
  312. }
  313. if _, err := io.Copy(stdout, indented); err != nil {
  314. return err
  315. }
  316. stdout.Write([]byte{'\n'})
  317. return nil
  318. }
  319. func (srv *Server) CmdPort(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
  320. cmd := rcli.Subcmd(stdout, "port", "[OPTIONS] CONTAINER PRIVATE_PORT", "Lookup the public-facing port which is NAT-ed to PRIVATE_PORT")
  321. if err := cmd.Parse(args); err != nil {
  322. return nil
  323. }
  324. if cmd.NArg() != 2 {
  325. cmd.Usage()
  326. return nil
  327. }
  328. name := cmd.Arg(0)
  329. privatePort := cmd.Arg(1)
  330. if container := srv.containers.Get(name); container == nil {
  331. return errors.New("No such container: " + name)
  332. } else {
  333. if frontend, exists := container.NetworkSettings.PortMapping[privatePort]; !exists {
  334. return fmt.Errorf("No private port '%s' allocated on %s", privatePort, name)
  335. } else {
  336. fmt.Fprintln(stdout, frontend)
  337. }
  338. }
  339. return nil
  340. }
  341. // 'docker rmi NAME' removes all images with the name NAME
  342. func (srv *Server) CmdRmi(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
  343. cmd := rcli.Subcmd(stdout, "rmimage", "[OPTIONS] IMAGE", "Remove an image")
  344. fl_all := cmd.Bool("a", false, "Use IMAGE as a path and remove ALL images in this path")
  345. if err := cmd.Parse(args); err != nil {
  346. cmd.Usage()
  347. return nil
  348. }
  349. if cmd.NArg() < 1 {
  350. cmd.Usage()
  351. return nil
  352. }
  353. for _, name := range cmd.Args() {
  354. var err error
  355. if *fl_all {
  356. err = srv.images.RemoveInPath(name)
  357. } else {
  358. image, err := srv.images.Get(name)
  359. if err != nil {
  360. return err
  361. } else if image == nil {
  362. return errors.New("No such image: " + name)
  363. }
  364. err = srv.images.Remove(image)
  365. }
  366. if err != nil {
  367. return err
  368. }
  369. }
  370. return nil
  371. }
  372. func (srv *Server) CmdRm(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
  373. cmd := rcli.Subcmd(stdout, "rm", "[OPTIONS] CONTAINER", "Remove a container")
  374. if err := cmd.Parse(args); err != nil {
  375. return nil
  376. }
  377. for _, name := range cmd.Args() {
  378. container := srv.containers.Get(name)
  379. if container == nil {
  380. return errors.New("No such container: " + name)
  381. }
  382. if err := srv.containers.Destroy(container); err != nil {
  383. fmt.Fprintln(stdout, "Error destroying container "+name+": "+err.Error())
  384. }
  385. }
  386. return nil
  387. }
  388. // 'docker kill NAME' kills a running container
  389. func (srv *Server) CmdKill(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
  390. cmd := rcli.Subcmd(stdout, "kill", "[OPTIONS] CONTAINER [CONTAINER...]", "Kill a running container")
  391. if err := cmd.Parse(args); err != nil {
  392. return nil
  393. }
  394. for _, name := range cmd.Args() {
  395. container := srv.containers.Get(name)
  396. if container == nil {
  397. return errors.New("No such container: " + name)
  398. }
  399. if err := container.Kill(); err != nil {
  400. fmt.Fprintln(stdout, "Error killing container "+name+": "+err.Error())
  401. }
  402. }
  403. return nil
  404. }
  405. func (srv *Server) CmdImport(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
  406. cmd := rcli.Subcmd(stdout, "import", "[OPTIONS] NAME", "Create a new filesystem image from the contents of a tarball")
  407. fl_stdin := cmd.Bool("stdin", false, "Read tarball from stdin")
  408. var archive io.Reader
  409. var resp *http.Response
  410. if err := cmd.Parse(args); err != nil {
  411. return nil
  412. }
  413. name := cmd.Arg(0)
  414. if name == "" {
  415. return errors.New("Not enough arguments")
  416. }
  417. if *fl_stdin {
  418. archive = stdin
  419. } else {
  420. u, err := url.Parse(name)
  421. if err != nil {
  422. return err
  423. }
  424. if u.Scheme == "" {
  425. u.Scheme = "http"
  426. }
  427. if u.Host == "" {
  428. u.Host = "get.docker.io"
  429. u.Path = path.Join("/images", u.Path)
  430. }
  431. fmt.Fprintf(stdout, "Downloading from %s\n", u.String())
  432. // Download with curl (pretty progress bar)
  433. // If curl is not available, fallback to http.Get()
  434. resp, err = future.Download(u.String(), stdout)
  435. if err != nil {
  436. return err
  437. }
  438. archive = future.ProgressReader(resp.Body, int(resp.ContentLength), stdout)
  439. }
  440. fmt.Fprintf(stdout, "Unpacking to %s\n", name)
  441. img, err := srv.images.Create(archive, nil, name, "")
  442. if err != nil {
  443. return err
  444. }
  445. fmt.Fprintln(stdout, img.Id)
  446. return nil
  447. }
  448. func (srv *Server) CmdImages(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
  449. cmd := rcli.Subcmd(stdout, "images", "[OPTIONS] [NAME]", "List images")
  450. limit := cmd.Int("l", 0, "Only show the N most recent versions of each image")
  451. quiet := cmd.Bool("q", false, "only show numeric IDs")
  452. if err := cmd.Parse(args); err != nil {
  453. return nil
  454. }
  455. if cmd.NArg() > 1 {
  456. cmd.Usage()
  457. return nil
  458. }
  459. var nameFilter string
  460. if cmd.NArg() == 1 {
  461. nameFilter = cmd.Arg(0)
  462. }
  463. w := tabwriter.NewWriter(stdout, 20, 1, 3, ' ', 0)
  464. if !*quiet {
  465. fmt.Fprintf(w, "NAME\tID\tCREATED\tPARENT\n")
  466. }
  467. paths, err := srv.images.Paths()
  468. if err != nil {
  469. return err
  470. }
  471. for _, name := range paths {
  472. if nameFilter != "" && nameFilter != name {
  473. continue
  474. }
  475. ids, err := srv.images.List(name)
  476. if err != nil {
  477. return err
  478. }
  479. for idx, img := range ids {
  480. if *limit > 0 && idx >= *limit {
  481. break
  482. }
  483. if !*quiet {
  484. for idx, field := range []string{
  485. /* NAME */ name,
  486. /* ID */ img.Id,
  487. /* CREATED */ future.HumanDuration(time.Now().Sub(time.Unix(img.Created, 0))) + " ago",
  488. /* PARENT */ img.Parent,
  489. } {
  490. if idx == 0 {
  491. w.Write([]byte(field))
  492. } else {
  493. w.Write([]byte("\t" + field))
  494. }
  495. }
  496. w.Write([]byte{'\n'})
  497. } else {
  498. stdout.Write([]byte(img.Id + "\n"))
  499. }
  500. }
  501. }
  502. if !*quiet {
  503. w.Flush()
  504. }
  505. return nil
  506. }
  507. func (srv *Server) CmdPs(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
  508. cmd := rcli.Subcmd(stdout,
  509. "ps", "[OPTIONS]", "List containers")
  510. quiet := cmd.Bool("q", false, "Only display numeric IDs")
  511. fl_all := cmd.Bool("a", false, "Show all containers. Only running containers are shown by default.")
  512. fl_full := cmd.Bool("notrunc", false, "Don't truncate output")
  513. if err := cmd.Parse(args); err != nil {
  514. return nil
  515. }
  516. w := tabwriter.NewWriter(stdout, 12, 1, 3, ' ', 0)
  517. if !*quiet {
  518. fmt.Fprintf(w, "ID\tIMAGE\tCOMMAND\tCREATED\tSTATUS\tCOMMENT\n")
  519. }
  520. for _, container := range srv.containers.List() {
  521. comment := container.GetUserData("comment")
  522. if !container.State.Running && !*fl_all {
  523. continue
  524. }
  525. if !*quiet {
  526. command := fmt.Sprintf("%s %s", container.Path, strings.Join(container.Args, " "))
  527. if !*fl_full {
  528. command = docker.Trunc(command, 20)
  529. }
  530. for idx, field := range []string{
  531. /* ID */ container.Id,
  532. /* IMAGE */ container.GetUserData("image"),
  533. /* COMMAND */ command,
  534. /* CREATED */ future.HumanDuration(time.Now().Sub(container.Created)) + " ago",
  535. /* STATUS */ container.State.String(),
  536. /* COMMENT */ comment,
  537. } {
  538. if idx == 0 {
  539. w.Write([]byte(field))
  540. } else {
  541. w.Write([]byte("\t" + field))
  542. }
  543. }
  544. w.Write([]byte{'\n'})
  545. } else {
  546. stdout.Write([]byte(container.Id + "\n"))
  547. }
  548. }
  549. if !*quiet {
  550. w.Flush()
  551. }
  552. return nil
  553. }
  554. func (srv *Server) CmdLayers(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
  555. cmd := rcli.Subcmd(stdout,
  556. "layers", "[OPTIONS]",
  557. "List filesystem layers (debug only)")
  558. if err := cmd.Parse(args); err != nil {
  559. return nil
  560. }
  561. for _, layer := range srv.images.Layers() {
  562. fmt.Fprintln(stdout, layer)
  563. }
  564. return nil
  565. }
  566. func (srv *Server) CmdCp(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
  567. cmd := rcli.Subcmd(stdout,
  568. "cp", "[OPTIONS] IMAGE NAME",
  569. "Create a copy of IMAGE and call it NAME")
  570. if err := cmd.Parse(args); err != nil {
  571. return nil
  572. }
  573. if image, err := srv.images.Get(cmd.Arg(0)); err != nil {
  574. return err
  575. } else if image == nil {
  576. return errors.New("Image " + cmd.Arg(0) + " does not exist")
  577. } else {
  578. if img, err := image.Copy(cmd.Arg(1)); err != nil {
  579. return err
  580. } else {
  581. fmt.Fprintln(stdout, img.Id)
  582. }
  583. }
  584. return nil
  585. }
  586. func (srv *Server) CmdCommit(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
  587. cmd := rcli.Subcmd(stdout,
  588. "commit", "[OPTIONS] CONTAINER [DEST]",
  589. "Create a new image from a container's changes")
  590. if err := cmd.Parse(args); err != nil {
  591. return nil
  592. }
  593. containerName, imgName := cmd.Arg(0), cmd.Arg(1)
  594. if containerName == "" || imgName == "" {
  595. cmd.Usage()
  596. return nil
  597. }
  598. if container := srv.containers.Get(containerName); container != nil {
  599. // FIXME: freeze the container before copying it to avoid data corruption?
  600. rwTar, err := fs.Tar(container.Mountpoint.Rw, fs.Uncompressed)
  601. if err != nil {
  602. return err
  603. }
  604. // Create a new image from the container's base layers + a new layer from container changes
  605. parentImg, err := srv.images.Get(container.Image)
  606. if err != nil {
  607. return err
  608. }
  609. img, err := srv.images.Create(rwTar, parentImg, imgName, "")
  610. if err != nil {
  611. return err
  612. }
  613. fmt.Fprintln(stdout, img.Id)
  614. return nil
  615. }
  616. return errors.New("No such container: " + containerName)
  617. }
  618. func (srv *Server) CmdTar(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
  619. cmd := rcli.Subcmd(stdout,
  620. "tar", "CONTAINER",
  621. "Stream the contents of a container as a tar archive")
  622. fl_sparse := cmd.Bool("s", false, "Generate a sparse tar stream (top layer + reference to bottom layers)")
  623. if err := cmd.Parse(args); err != nil {
  624. return nil
  625. }
  626. if *fl_sparse {
  627. return errors.New("Sparse mode not yet implemented") // FIXME
  628. }
  629. name := cmd.Arg(0)
  630. if container := srv.containers.Get(name); container != nil {
  631. if err := container.Mountpoint.EnsureMounted(); err != nil {
  632. return err
  633. }
  634. data, err := fs.Tar(container.Mountpoint.Root, fs.Uncompressed)
  635. if err != nil {
  636. return err
  637. }
  638. // Stream the entire contents of the container (basically a volatile snapshot)
  639. if _, err := io.Copy(stdout, data); err != nil {
  640. return err
  641. }
  642. return nil
  643. }
  644. return errors.New("No such container: " + name)
  645. }
  646. func (srv *Server) CmdDiff(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
  647. cmd := rcli.Subcmd(stdout,
  648. "diff", "CONTAINER [OPTIONS]",
  649. "Inspect changes on a container's filesystem")
  650. if err := cmd.Parse(args); err != nil {
  651. return nil
  652. }
  653. if cmd.NArg() < 1 {
  654. return errors.New("Not enough arguments")
  655. }
  656. if container := srv.containers.Get(cmd.Arg(0)); container == nil {
  657. return errors.New("No such container")
  658. } else {
  659. changes, err := srv.images.Changes(container.Mountpoint)
  660. if err != nil {
  661. return err
  662. }
  663. for _, change := range changes {
  664. fmt.Fprintln(stdout, change.String())
  665. }
  666. }
  667. return nil
  668. }
  669. func (srv *Server) CmdReset(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
  670. cmd := rcli.Subcmd(stdout,
  671. "reset", "CONTAINER [OPTIONS]",
  672. "Reset changes to a container's filesystem")
  673. if err := cmd.Parse(args); err != nil {
  674. return nil
  675. }
  676. if cmd.NArg() < 1 {
  677. return errors.New("Not enough arguments")
  678. }
  679. for _, name := range cmd.Args() {
  680. if container := srv.containers.Get(name); container != nil {
  681. if err := container.Mountpoint.Reset(); err != nil {
  682. return errors.New("Reset " + container.Id + ": " + err.Error())
  683. }
  684. }
  685. }
  686. return nil
  687. }
  688. func (srv *Server) CmdLogs(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
  689. cmd := rcli.Subcmd(stdout, "logs", "[OPTIONS] CONTAINER", "Fetch the logs of a container")
  690. if err := cmd.Parse(args); err != nil {
  691. return nil
  692. }
  693. if cmd.NArg() != 1 {
  694. cmd.Usage()
  695. return nil
  696. }
  697. name := cmd.Arg(0)
  698. if container := srv.containers.Get(name); container != nil {
  699. if _, err := io.Copy(stdout, container.StdoutLog()); err != nil {
  700. return err
  701. }
  702. if _, err := io.Copy(stdout, container.StderrLog()); err != nil {
  703. return err
  704. }
  705. return nil
  706. }
  707. return errors.New("No such container: " + cmd.Arg(0))
  708. }
  709. func (srv *Server) CreateContainer(img *fs.Image, ports []int, user string, tty bool, openStdin bool, memory int64, comment string, cmd string, args ...string) (*docker.Container, error) {
  710. id := future.RandomId()[:8]
  711. container, err := srv.containers.Create(id, cmd, args, img,
  712. &docker.Config{
  713. Hostname: id,
  714. Ports: ports,
  715. User: user,
  716. Tty: tty,
  717. OpenStdin: openStdin,
  718. Memory: memory,
  719. })
  720. if err != nil {
  721. return nil, err
  722. }
  723. if err := container.SetUserData("image", img.Id); err != nil {
  724. srv.containers.Destroy(container)
  725. return nil, errors.New("Error setting container userdata: " + err.Error())
  726. }
  727. if err := container.SetUserData("comment", comment); err != nil {
  728. srv.containers.Destroy(container)
  729. return nil, errors.New("Error setting container userdata: " + err.Error())
  730. }
  731. return container, nil
  732. }
  733. func (srv *Server) CmdAttach(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
  734. cmd := rcli.Subcmd(stdout, "attach", "[OPTIONS]", "Attach to a running container")
  735. fl_i := cmd.Bool("i", false, "Attach to stdin")
  736. fl_o := cmd.Bool("o", true, "Attach to stdout")
  737. fl_e := cmd.Bool("e", true, "Attach to stderr")
  738. if err := cmd.Parse(args); err != nil {
  739. return nil
  740. }
  741. if cmd.NArg() != 1 {
  742. cmd.Usage()
  743. return nil
  744. }
  745. name := cmd.Arg(0)
  746. container := srv.containers.Get(name)
  747. if container == nil {
  748. return errors.New("No such container: " + name)
  749. }
  750. var wg sync.WaitGroup
  751. if *fl_i {
  752. c_stdin, err := container.StdinPipe()
  753. if err != nil {
  754. return err
  755. }
  756. wg.Add(1)
  757. go func() { io.Copy(c_stdin, stdin); wg.Add(-1) }()
  758. }
  759. if *fl_o {
  760. c_stdout, err := container.StdoutPipe()
  761. if err != nil {
  762. return err
  763. }
  764. wg.Add(1)
  765. go func() { io.Copy(stdout, c_stdout); wg.Add(-1) }()
  766. }
  767. if *fl_e {
  768. c_stderr, err := container.StderrPipe()
  769. if err != nil {
  770. return err
  771. }
  772. wg.Add(1)
  773. go func() { io.Copy(stdout, c_stderr); wg.Add(-1) }()
  774. }
  775. wg.Wait()
  776. return nil
  777. }
  778. // Ports type - Used to parse multiple -p flags
  779. type ports []int
  780. func (p *ports) String() string {
  781. return fmt.Sprint(*p)
  782. }
  783. func (p *ports) Set(value string) error {
  784. port, err := strconv.Atoi(value)
  785. if err != nil {
  786. return fmt.Errorf("Invalid port: %v", value)
  787. }
  788. *p = append(*p, port)
  789. return nil
  790. }
  791. func (srv *Server) CmdRun(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
  792. cmd := rcli.Subcmd(stdout, "run", "[OPTIONS] IMAGE COMMAND [ARG...]", "Run a command in a new container")
  793. fl_user := cmd.String("u", "", "Username or UID")
  794. fl_attach := cmd.Bool("a", false, "Attach stdin and stdout")
  795. fl_stdin := cmd.Bool("i", false, "Keep stdin open even if not attached")
  796. fl_tty := cmd.Bool("t", false, "Allocate a pseudo-tty")
  797. fl_comment := cmd.String("c", "", "Comment")
  798. fl_memory := cmd.Int64("m", 0, "Memory limit (in bytes)")
  799. var fl_ports ports
  800. cmd.Var(&fl_ports, "p", "Map a network port to the container")
  801. if err := cmd.Parse(args); err != nil {
  802. return nil
  803. }
  804. name := cmd.Arg(0)
  805. var img_name string
  806. //var img_version string // Only here for reference
  807. var cmdline []string
  808. if len(cmd.Args()) >= 2 {
  809. cmdline = cmd.Args()[1:]
  810. }
  811. // Choose a default image if needed
  812. if name == "" {
  813. name = "base"
  814. }
  815. // Choose a default command if needed
  816. if len(cmdline) == 0 {
  817. *fl_stdin = true
  818. *fl_tty = true
  819. *fl_attach = true
  820. cmdline = []string{"/bin/bash", "-i"}
  821. }
  822. // Find the image
  823. img, err := srv.images.Find(name)
  824. if err != nil {
  825. return err
  826. } else if img == nil {
  827. // Separate the name:version tag
  828. if strings.Contains(name, ":") {
  829. parts := strings.SplitN(name, ":", 2)
  830. img_name = parts[0]
  831. //img_version = parts[1] // Only here for reference
  832. } else {
  833. img_name = name
  834. }
  835. stdin_noclose := ioutil.NopCloser(stdin)
  836. if err := srv.CmdImport(stdin_noclose, stdout, img_name); err != nil {
  837. return err
  838. }
  839. img, err = srv.images.Find(name)
  840. if err != nil || img == nil {
  841. return errors.New("Could not find image after downloading: " + name)
  842. }
  843. }
  844. // Create new container
  845. container, err := srv.CreateContainer(img, fl_ports, *fl_user, *fl_tty,
  846. *fl_stdin, *fl_memory, *fl_comment, cmdline[0], cmdline[1:]...)
  847. if err != nil {
  848. return errors.New("Error creating container: " + err.Error())
  849. }
  850. if *fl_stdin {
  851. cmd_stdin, err := container.StdinPipe()
  852. if err != nil {
  853. return err
  854. }
  855. if *fl_attach {
  856. future.Go(func() error {
  857. _, err := io.Copy(cmd_stdin, stdin)
  858. cmd_stdin.Close()
  859. return err
  860. })
  861. }
  862. }
  863. // Run the container
  864. if *fl_attach {
  865. cmd_stderr, err := container.StderrPipe()
  866. if err != nil {
  867. return err
  868. }
  869. cmd_stdout, err := container.StdoutPipe()
  870. if err != nil {
  871. return err
  872. }
  873. if err := container.Start(); err != nil {
  874. return err
  875. }
  876. sending_stdout := future.Go(func() error {
  877. _, err := io.Copy(stdout, cmd_stdout)
  878. return err
  879. })
  880. sending_stderr := future.Go(func() error {
  881. _, err := io.Copy(stdout, cmd_stderr)
  882. return err
  883. })
  884. err_sending_stdout := <-sending_stdout
  885. err_sending_stderr := <-sending_stderr
  886. if err_sending_stdout != nil {
  887. return err_sending_stdout
  888. }
  889. if err_sending_stderr != nil {
  890. return err_sending_stderr
  891. }
  892. container.Wait()
  893. } else {
  894. if err := container.Start(); err != nil {
  895. return err
  896. }
  897. fmt.Fprintln(stdout, container.Id)
  898. }
  899. return nil
  900. }
  901. func New() (*Server, error) {
  902. future.Seed()
  903. // if err != nil {
  904. // return nil, err
  905. // }
  906. containers, err := docker.New()
  907. if err != nil {
  908. return nil, err
  909. }
  910. srv := &Server{
  911. images: containers.Store,
  912. containers: containers,
  913. }
  914. return srv, nil
  915. }
  916. func (srv *Server) CmdMirror(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
  917. _, err := io.Copy(stdout, stdin)
  918. return err
  919. }
  920. func (srv *Server) CmdDebug(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
  921. for {
  922. if line, err := bufio.NewReader(stdin).ReadString('\n'); err == nil {
  923. fmt.Printf("--- %s", line)
  924. } else if err == io.EOF {
  925. if len(line) > 0 {
  926. fmt.Printf("--- %s\n", line)
  927. }
  928. break
  929. } else {
  930. return err
  931. }
  932. }
  933. return nil
  934. }
  935. func (srv *Server) CmdWeb(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
  936. cmd := rcli.Subcmd(stdout, "web", "[OPTIONS]", "A web UI for docker")
  937. showurl := cmd.Bool("u", false, "Return the URL of the web UI")
  938. if err := cmd.Parse(args); err != nil {
  939. return nil
  940. }
  941. if *showurl {
  942. fmt.Fprintln(stdout, "http://localhost:4242/web")
  943. } else {
  944. if file, err := os.Open("dockerweb.html"); err != nil {
  945. return err
  946. } else if _, err := io.Copy(stdout, file); err != nil {
  947. return err
  948. }
  949. }
  950. return nil
  951. }
  952. type Server struct {
  953. containers *docker.Docker
  954. images *fs.Store
  955. }