dockerd.go 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541
  1. package main
  2. import (
  3. "github.com/dotcloud/docker/rcli"
  4. "github.com/dotcloud/docker/fake"
  5. "github.com/dotcloud/docker/future"
  6. "bufio"
  7. "errors"
  8. "log"
  9. "io"
  10. "io/ioutil"
  11. "os/exec"
  12. "flag"
  13. "fmt"
  14. "github.com/kr/pty"
  15. "strings"
  16. "bytes"
  17. "text/tabwriter"
  18. "sort"
  19. "os"
  20. "time"
  21. )
  22. func (docker *Docker) Name() string {
  23. return "docker"
  24. }
  25. func (docker *Docker) Help() string {
  26. help := "Usage: docker COMMAND [arg...]\n\nA self-sufficient runtime for linux containers.\n\nCommands:\n"
  27. for _, cmd := range [][]interface{}{
  28. {"run", "Run a command in a container"},
  29. {"list", "Display a list of containers"},
  30. {"pull", "Download a tarball and create a container from it"},
  31. {"put", "Upload a tarball and create a container from it"},
  32. {"rm", "Remove containers"},
  33. {"wait", "Wait for the state of a container to change"},
  34. {"stop", "Stop a running container"},
  35. {"logs", "Fetch the logs of a container"},
  36. {"diff", "Inspect changes on a container's filesystem"},
  37. {"commit", "Save the state of a container"},
  38. {"attach", "Attach to the standard inputs and outputs of a running container"},
  39. {"info", "Display system-wide information"},
  40. {"web", "Generate a web UI"},
  41. } {
  42. help += fmt.Sprintf(" %-10.10s%s\n", cmd...)
  43. }
  44. return help
  45. }
  46. func (docker *Docker) CmdList(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
  47. flags := rcli.Subcmd(stdout, "list", "[OPTIONS] [NAME]", "List containers")
  48. limit := flags.Int("l", 0, "Only show the N most recent versions of each name")
  49. quiet := flags.Bool("q", false, "only show numeric IDs")
  50. flags.Parse(args)
  51. if flags.NArg() > 1 {
  52. flags.Usage()
  53. return nil
  54. }
  55. var nameFilter string
  56. if flags.NArg() == 1 {
  57. nameFilter = flags.Arg(0)
  58. }
  59. var names []string
  60. for name := range docker.containersByName {
  61. names = append(names, name)
  62. }
  63. sort.Strings(names)
  64. w := tabwriter.NewWriter(stdout, 20, 1, 3, ' ', 0)
  65. if (!*quiet) {
  66. fmt.Fprintf(w, "NAME\tID\tCREATED\tSOURCE\tSIZE\tCHANGES\tRUNNING\tCOMMAND\n")
  67. }
  68. for _, name := range names {
  69. if nameFilter != "" && nameFilter != name {
  70. continue
  71. }
  72. for idx, container := range *docker.containersByName[name] {
  73. if *limit > 0 && idx >= *limit {
  74. break
  75. }
  76. if !*quiet {
  77. for idx, field := range []string{
  78. /* NAME */ container.Name,
  79. /* ID */ container.Id,
  80. /* CREATED */ future.HumanDuration(time.Now().Sub(container.Created)) + " ago",
  81. /* SOURCE */ container.Source,
  82. /* SIZE */ fmt.Sprintf("%.1fM", float32(container.Size) / 1024 / 1024),
  83. /* CHANGES */ fmt.Sprintf("%.1fM", float32(container.BytesChanged) / 1024 / 1024),
  84. /* RUNNING */ fmt.Sprintf("%v", container.Running),
  85. /* COMMAND */ container.CmdString(),
  86. } {
  87. if idx == 0 {
  88. w.Write([]byte(field))
  89. } else {
  90. w.Write([]byte("\t" + field))
  91. }
  92. }
  93. w.Write([]byte{'\n'})
  94. } else {
  95. stdout.Write([]byte(container.Id + "\n"))
  96. }
  97. }
  98. }
  99. if (!*quiet) {
  100. w.Flush()
  101. }
  102. return nil
  103. }
  104. func (docker *Docker) findContainer(name string) (*Container, bool) {
  105. // 1: look for container by ID
  106. if container, exists := docker.containers[name]; exists {
  107. return container, true
  108. }
  109. // 2: look for a container by name (and pick the most recent)
  110. if containers, exists := docker.containersByName[name]; exists {
  111. return (*containers)[0], true
  112. }
  113. return nil, false
  114. }
  115. func (docker *Docker) CmdRm(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
  116. flags := rcli.Subcmd(stdout, "rm", "[OPTIONS] CONTAINER", "Remove a container")
  117. if err := flags.Parse(args); err != nil {
  118. return nil
  119. }
  120. for _, name := range flags.Args() {
  121. if _, err := docker.rm(name); err != nil {
  122. fmt.Fprintln(stdout, "Error: " + err.Error())
  123. }
  124. }
  125. return nil
  126. }
  127. func (docker *Docker) CmdPull(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
  128. if len(args) < 1 {
  129. return errors.New("Not enough arguments")
  130. }
  131. time.Sleep(2 * time.Second)
  132. layer := docker.addContainer(args[0], "download", 0)
  133. fmt.Fprintln(stdout, layer.Id)
  134. return nil
  135. }
  136. func (docker *Docker) CmdPut(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
  137. if len(args) < 1 {
  138. return errors.New("Not enough arguments")
  139. }
  140. time.Sleep(1 * time.Second)
  141. layer := docker.addContainer(args[0], "upload", 0)
  142. fmt.Fprintln(stdout, layer.Id)
  143. return nil
  144. }
  145. func (docker *Docker) CmdCommit(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
  146. flags := rcli.Subcmd(stdout,
  147. "fork", "[OPTIONS] CONTAINER [DEST]",
  148. "Duplicate a container")
  149. // FIXME "-r" to reset changes in the new container
  150. if err := flags.Parse(args); err != nil {
  151. return nil
  152. }
  153. srcName, dstName := flags.Arg(0), flags.Arg(1)
  154. if srcName == "" {
  155. flags.Usage()
  156. return nil
  157. }
  158. if dstName == "" {
  159. dstName = srcName
  160. }
  161. if src, exists := docker.findContainer(srcName); exists {
  162. dst := docker.addContainer(dstName, "snapshot:" + src.Id, src.Size)
  163. fmt.Fprintln(stdout, dst.Id)
  164. return nil
  165. }
  166. return errors.New("No such container: " + srcName)
  167. }
  168. func (docker *Docker) CmdTar(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
  169. flags := rcli.Subcmd(stdout,
  170. "tar", "CONTAINER",
  171. "Stream the contents of a container as a tar archive")
  172. if err := flags.Parse(args); err != nil {
  173. return nil
  174. }
  175. name := flags.Arg(0)
  176. if _, exists := docker.findContainer(name); exists {
  177. // Stream the entire contents of the container (basically a volatile snapshot)
  178. return fake.WriteFakeTar(stdout)
  179. }
  180. return errors.New("No such container: " + name)
  181. }
  182. func (docker *Docker) CmdDiff(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
  183. flags := rcli.Subcmd(stdout,
  184. "diff", "CONTAINER [OPTIONS]",
  185. "Inspect changes on a container's filesystem")
  186. fl_diff := flags.Bool("d", true, "Show changes in diff format")
  187. fl_bytes := flags.Bool("b", false, "Show how many bytes have been changed")
  188. fl_list := flags.Bool("l", false, "Show a list of changed files")
  189. if err := flags.Parse(args); err != nil {
  190. return nil
  191. }
  192. if flags.NArg() < 1 {
  193. return errors.New("Not enough arguments")
  194. }
  195. if container, exists := docker.findContainer(flags.Arg(0)); !exists {
  196. return errors.New("No such container")
  197. } else if *fl_bytes {
  198. fmt.Fprintf(stdout, "%d\n", container.BytesChanged)
  199. } else if *fl_list {
  200. // FAKE
  201. fmt.Fprintf(stdout, strings.Join([]string{
  202. "/etc/postgres/pg.conf",
  203. "/etc/passwd",
  204. "/var/lib/postgres",
  205. "/usr/bin/postgres",
  206. "/usr/bin/psql",
  207. "/var/log/postgres",
  208. "/var/log/postgres/postgres.log",
  209. "/var/log/postgres/postgres.log.0",
  210. "/var/log/postgres/postgres.log.1.gz"}, "\n"))
  211. } else if *fl_diff {
  212. // Achievement unlocked: embed a diff of your code as a string in your code
  213. fmt.Fprintf(stdout, `
  214. diff --git a/dockerd/dockerd.go b/dockerd/dockerd.go
  215. index 2dae694..e43caca 100644
  216. --- a/dockerd/dockerd.go
  217. +++ b/dockerd/dockerd.go
  218. @@ -158,6 +158,7 @@ func (docker *Docker) CmdDiff(stdin io.ReadCloser, stdout io.Writer, args ...str
  219. flags := rcli.Subcmd(stdout,
  220. "diff", "CONTAINER [OPTIONS]",
  221. "Inspect changes on a container's filesystem")
  222. + fl_diff := flags.Bool("d", true, "Show changes in diff format")
  223. fl_bytes := flags.Bool("b", false, "Show how many bytes have been changes")
  224. fl_list := flags.Bool("l", false, "Show a list of changed files")
  225. fl_download := flags.Bool("d", false, "Download the changes as gzipped tar stream")
  226. `)
  227. return nil
  228. } else {
  229. flags.Usage()
  230. return nil
  231. }
  232. return nil
  233. }
  234. // ByDate wraps an array of layers so they can be sorted by date (most recent first)
  235. type ByDate []*Container
  236. func (c *ByDate) Len() int {
  237. return len(*c)
  238. }
  239. func (c *ByDate) Less(i, j int) bool {
  240. containers := *c
  241. return containers[j].Created.Before(containers[i].Created)
  242. }
  243. func (c *ByDate) Swap(i, j int) {
  244. containers := *c
  245. tmp := containers[i]
  246. containers[i] = containers[j]
  247. containers[j] = tmp
  248. }
  249. func (c *ByDate) Add(container *Container) {
  250. *c = append(*c, container)
  251. sort.Sort(c)
  252. }
  253. func (c *ByDate) Del(id string) {
  254. for idx, container := range *c {
  255. if container.Id == id {
  256. *c = append((*c)[:idx], (*c)[idx + 1:]...)
  257. }
  258. }
  259. }
  260. func (docker *Docker) addContainer(name string, source string, size uint) *Container {
  261. if size == 0 {
  262. size = fake.RandomContainerSize()
  263. }
  264. c := &Container{
  265. Id: fake.RandomId(),
  266. Name: name,
  267. Created: time.Now(),
  268. Source: source,
  269. Size: size,
  270. stdinLog: new(bytes.Buffer),
  271. stdoutLog: new(bytes.Buffer),
  272. }
  273. docker.containers[c.Id] = c
  274. if _, exists := docker.containersByName[c.Name]; !exists {
  275. docker.containersByName[c.Name] = new(ByDate)
  276. }
  277. docker.containersByName[c.Name].Add(c)
  278. return c
  279. }
  280. func (docker *Docker) rm(id string) (*Container, error) {
  281. if container, exists := docker.containers[id]; exists {
  282. if container.Running {
  283. return nil, errors.New("Container is running: " + id)
  284. } else {
  285. // Remove from name lookup
  286. docker.containersByName[container.Name].Del(container.Id)
  287. // Remove from id lookup
  288. delete(docker.containers, container.Id)
  289. return container, nil
  290. }
  291. }
  292. return nil, errors.New(fmt.Sprintf("No such container: %s", id))
  293. }
  294. func (docker *Docker) CmdLogs(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
  295. flags := rcli.Subcmd(stdout, "logs", "[OPTIONS] CONTAINER", "Fetch the logs of a container")
  296. if err := flags.Parse(args); err != nil {
  297. return nil
  298. }
  299. if flags.NArg() != 1 {
  300. flags.Usage()
  301. return nil
  302. }
  303. name := flags.Arg(0)
  304. if container, exists := docker.findContainer(name); exists {
  305. if _, err := io.Copy(stdout, container.StdoutLog()); err != nil {
  306. return err
  307. }
  308. return nil
  309. }
  310. return errors.New("No such container: " + flags.Arg(0))
  311. }
  312. func (docker *Docker) CmdRun(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
  313. flags := rcli.Subcmd(stdout, "run", "[OPTIONS] CONTAINER COMMAND [ARG...]", "Run a command in a container")
  314. fl_attach := flags.Bool("a", false, "Attach stdin and stdout")
  315. if err := flags.Parse(args); err != nil {
  316. return nil
  317. }
  318. if flags.NArg() < 2 {
  319. flags.Usage()
  320. return nil
  321. }
  322. name, cmd := flags.Arg(0), flags.Args()[1:]
  323. if container, exists := docker.findContainer(name); exists {
  324. if container.Running {
  325. return errors.New("Already running: " + name)
  326. }
  327. if *fl_attach {
  328. return container.Run(cmd[0], cmd[1:], stdin, stdout)
  329. } else {
  330. go container.Run(cmd[0], cmd[1:], ioutil.NopCloser(new(bytes.Buffer)), ioutil.Discard)
  331. fmt.Fprintln(stdout, container.Id)
  332. return nil
  333. }
  334. }
  335. return errors.New("No such container: " + name)
  336. }
  337. func startCommand(cmd *exec.Cmd, interactive bool) (io.WriteCloser, io.ReadCloser, error) {
  338. if interactive {
  339. term, err := pty.Start(cmd)
  340. if err != nil {
  341. return nil, nil, err
  342. }
  343. return term, term, nil
  344. }
  345. stdin, err := cmd.StdinPipe()
  346. if err != nil {
  347. return nil, nil, err
  348. }
  349. stdout, err := cmd.StdoutPipe()
  350. if err != nil {
  351. return nil, nil, err
  352. }
  353. if err := cmd.Start(); err != nil {
  354. return nil, nil, err
  355. }
  356. return stdin, stdout, nil
  357. }
  358. func main() {
  359. fake.Seed()
  360. flag.Parse()
  361. docker := New()
  362. go func() {
  363. if err := rcli.ListenAndServeHTTP(":8080", docker); err != nil {
  364. log.Fatal(err)
  365. }
  366. }()
  367. if err := rcli.ListenAndServeTCP(":4242", docker); err != nil {
  368. log.Fatal(err)
  369. }
  370. }
  371. func New() *Docker {
  372. return &Docker{
  373. containersByName: make(map[string]*ByDate),
  374. containers: make(map[string]*Container),
  375. }
  376. }
  377. func (docker *Docker) CmdMirror(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
  378. _, err := io.Copy(stdout, stdin)
  379. return err
  380. }
  381. func (docker *Docker) CmdDebug(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
  382. for {
  383. if line, err := bufio.NewReader(stdin).ReadString('\n'); err == nil {
  384. fmt.Printf("--- %s", line)
  385. } else if err == io.EOF {
  386. if len(line) > 0 {
  387. fmt.Printf("--- %s\n", line)
  388. }
  389. break
  390. } else {
  391. return err
  392. }
  393. }
  394. return nil
  395. }
  396. func (docker *Docker) CmdWeb(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
  397. flags := rcli.Subcmd(stdout, "web", "[OPTIONS]", "A web UI for docker")
  398. showurl := flags.Bool("u", false, "Return the URL of the web UI")
  399. if err := flags.Parse(args); err != nil {
  400. return nil
  401. }
  402. if *showurl {
  403. fmt.Fprintln(stdout, "http://localhost:4242/web")
  404. } else {
  405. if file, err := os.Open("dockerweb.html"); err != nil {
  406. return err
  407. } else if _, err := io.Copy(stdout, file); err != nil {
  408. return err
  409. }
  410. }
  411. return nil
  412. }
  413. func Go(f func() error) chan error {
  414. ch := make(chan error)
  415. go func() {
  416. ch <- f()
  417. }()
  418. return ch
  419. }
  420. type Docker struct {
  421. containers map[string]*Container
  422. containersByName map[string]*ByDate
  423. }
  424. type Container struct {
  425. Id string
  426. Name string
  427. Created time.Time
  428. Source string
  429. Size uint
  430. FilesChanged uint
  431. BytesChanged uint
  432. Running bool
  433. Cmd string
  434. Args []string
  435. stdoutLog *bytes.Buffer
  436. stdinLog *bytes.Buffer
  437. }
  438. func (c *Container) Run(command string, args []string, stdin io.ReadCloser, stdout io.Writer) error {
  439. // Not thread-safe
  440. if c.Running {
  441. return errors.New("Already running")
  442. }
  443. c.Cmd = command
  444. c.Args = args
  445. // Reset logs
  446. c.stdoutLog.Reset()
  447. c.stdinLog.Reset()
  448. c.Running = true
  449. cmd := exec.Command(c.Cmd, c.Args...)
  450. cmd_stdin, cmd_stdout, err := startCommand(cmd, true)
  451. // ADD FAKE RANDOM CHANGES
  452. c.FilesChanged = fake.RandomFilesChanged()
  453. c.BytesChanged = fake.RandomBytesChanged()
  454. if err != nil {
  455. return err
  456. }
  457. copy_out := Go(func() error {
  458. _, err := io.Copy(io.MultiWriter(stdout, c.stdoutLog), cmd_stdout)
  459. return err
  460. })
  461. Go(func() error {
  462. _, err := io.Copy(io.MultiWriter(cmd_stdin, c.stdinLog), stdin)
  463. cmd_stdin.Close()
  464. stdin.Close()
  465. return err
  466. })
  467. wait := Go(func() error {
  468. err := cmd.Wait()
  469. c.Running = false
  470. return err
  471. })
  472. if err := <-copy_out; err != nil {
  473. if c.Running {
  474. return err
  475. }
  476. }
  477. if err := <-wait; err != nil {
  478. if status, ok := err.(*exec.ExitError); ok {
  479. fmt.Fprintln(stdout, status)
  480. return nil
  481. }
  482. return err
  483. }
  484. return nil
  485. }
  486. func (c *Container) StdoutLog() io.Reader {
  487. return strings.NewReader(c.stdoutLog.String())
  488. }
  489. func (c *Container) StdinLog() io.Reader {
  490. return strings.NewReader(c.stdinLog.String())
  491. }
  492. func (c *Container) CmdString() string {
  493. return strings.Join(append([]string{c.Cmd}, c.Args...), " ")
  494. }