dockerd.go 14 KB

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