container.go 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729
  1. package docker
  2. import (
  3. "encoding/json"
  4. "fmt"
  5. "github.com/dotcloud/docker/rcli"
  6. "github.com/kr/pty"
  7. "io"
  8. "io/ioutil"
  9. "log"
  10. "os"
  11. "os/exec"
  12. "path"
  13. "strconv"
  14. "syscall"
  15. "time"
  16. )
  17. type Container struct {
  18. root string
  19. Id string
  20. Created time.Time
  21. Path string
  22. Args []string
  23. Config *Config
  24. State State
  25. Image string
  26. network *NetworkInterface
  27. NetworkSettings *NetworkSettings
  28. SysInitPath string
  29. cmd *exec.Cmd
  30. stdout *writeBroadcaster
  31. stderr *writeBroadcaster
  32. stdin io.ReadCloser
  33. stdinPipe io.WriteCloser
  34. ptyStdinMaster io.Closer
  35. ptyStdoutMaster io.Closer
  36. ptyStderrMaster io.Closer
  37. runtime *Runtime
  38. }
  39. type Config struct {
  40. Hostname string
  41. User string
  42. Memory int64 // Memory limit (in bytes)
  43. MemorySwap int64 // Total memory usage (memory + swap); set `-1' to disable swap
  44. AttachStdin bool
  45. AttachStdout bool
  46. AttachStderr bool
  47. Ports []int
  48. Tty bool // Attach standard streams to a tty, including stdin if it is not closed.
  49. OpenStdin bool // Open stdin
  50. StdinOnce bool // If true, close stdin after the 1 attached client disconnects.
  51. Env []string
  52. Cmd []string
  53. Image string // Name of the image as it was passed by the operator (eg. could be symbolic)
  54. }
  55. func ParseRun(args []string, stdout io.Writer) (*Config, error) {
  56. cmd := rcli.Subcmd(stdout, "run", "[OPTIONS] IMAGE COMMAND [ARG...]", "Run a command in a new container")
  57. if len(args) > 0 && args[0] != "--help" {
  58. cmd.SetOutput(ioutil.Discard)
  59. }
  60. flHostname := cmd.String("h", "", "Container host name")
  61. flUser := cmd.String("u", "", "Username or UID")
  62. flDetach := cmd.Bool("d", false, "Detached mode: leave the container running in the background")
  63. flAttach := NewAttachOpts()
  64. cmd.Var(flAttach, "a", "Attach to stdin, stdout or stderr.")
  65. flStdin := cmd.Bool("i", false, "Keep stdin open even if not attached")
  66. flTty := cmd.Bool("t", false, "Allocate a pseudo-tty")
  67. flMemory := cmd.Int64("m", 0, "Memory limit (in bytes)")
  68. var flPorts ports
  69. cmd.Var(&flPorts, "p", "Map a network port to the container")
  70. var flEnv ListOpts
  71. cmd.Var(&flEnv, "e", "Set environment variables")
  72. if err := cmd.Parse(args); err != nil {
  73. return nil, err
  74. }
  75. if *flDetach && len(*flAttach) > 0 {
  76. return nil, fmt.Errorf("Conflicting options: -a and -d")
  77. }
  78. // If neither -d or -a are set, attach to everything by default
  79. if len(*flAttach) == 0 && !*flDetach {
  80. if !*flDetach {
  81. flAttach.Set("stdout")
  82. flAttach.Set("stderr")
  83. if *flStdin {
  84. flAttach.Set("stdin")
  85. }
  86. }
  87. }
  88. parsedArgs := cmd.Args()
  89. runCmd := []string{}
  90. image := ""
  91. if len(parsedArgs) >= 1 {
  92. image = cmd.Arg(0)
  93. }
  94. if len(parsedArgs) > 1 {
  95. runCmd = parsedArgs[1:]
  96. }
  97. config := &Config{
  98. Hostname: *flHostname,
  99. Ports: flPorts,
  100. User: *flUser,
  101. Tty: *flTty,
  102. OpenStdin: *flStdin,
  103. Memory: *flMemory,
  104. AttachStdin: flAttach.Get("stdin"),
  105. AttachStdout: flAttach.Get("stdout"),
  106. AttachStderr: flAttach.Get("stderr"),
  107. Env: flEnv,
  108. Cmd: runCmd,
  109. Image: image,
  110. }
  111. // When allocating stdin in attached mode, close stdin at client disconnect
  112. if config.OpenStdin && config.AttachStdin {
  113. config.StdinOnce = true
  114. }
  115. return config, nil
  116. }
  117. type NetworkSettings struct {
  118. IpAddress string
  119. IpPrefixLen int
  120. Gateway string
  121. PortMapping map[string]string
  122. }
  123. func (container *Container) Cmd() *exec.Cmd {
  124. return container.cmd
  125. }
  126. func (container *Container) When() time.Time {
  127. return container.Created
  128. }
  129. func (container *Container) FromDisk() error {
  130. data, err := ioutil.ReadFile(container.jsonPath())
  131. if err != nil {
  132. return err
  133. }
  134. // Load container settings
  135. if err := json.Unmarshal(data, container); err != nil {
  136. return err
  137. }
  138. return nil
  139. }
  140. func (container *Container) ToDisk() (err error) {
  141. data, err := json.Marshal(container)
  142. if err != nil {
  143. return
  144. }
  145. return ioutil.WriteFile(container.jsonPath(), data, 0666)
  146. }
  147. func (container *Container) generateLXCConfig() error {
  148. fo, err := os.Create(container.lxcConfigPath())
  149. if err != nil {
  150. return err
  151. }
  152. defer fo.Close()
  153. if err := LxcTemplateCompiled.Execute(fo, container); err != nil {
  154. return err
  155. }
  156. return nil
  157. }
  158. func (container *Container) startPty() error {
  159. stdoutMaster, stdoutSlave, err := pty.Open()
  160. if err != nil {
  161. return err
  162. }
  163. container.ptyStdoutMaster = stdoutMaster
  164. container.cmd.Stdout = stdoutSlave
  165. stderrMaster, stderrSlave, err := pty.Open()
  166. if err != nil {
  167. return err
  168. }
  169. container.ptyStderrMaster = stderrMaster
  170. container.cmd.Stderr = stderrSlave
  171. // Copy the PTYs to our broadcasters
  172. go func() {
  173. defer container.stdout.CloseWriters()
  174. Debugf("[startPty] Begin of stdout pipe")
  175. io.Copy(container.stdout, stdoutMaster)
  176. Debugf("[startPty] End of stdout pipe")
  177. }()
  178. go func() {
  179. defer container.stderr.CloseWriters()
  180. Debugf("[startPty] Begin of stderr pipe")
  181. io.Copy(container.stderr, stderrMaster)
  182. Debugf("[startPty] End of stderr pipe")
  183. }()
  184. // stdin
  185. var stdinSlave io.ReadCloser
  186. if container.Config.OpenStdin {
  187. var stdinMaster io.WriteCloser
  188. stdinMaster, stdinSlave, err = pty.Open()
  189. if err != nil {
  190. return err
  191. }
  192. container.ptyStdinMaster = stdinMaster
  193. container.cmd.Stdin = stdinSlave
  194. // FIXME: The following appears to be broken.
  195. // "cannot set terminal process group (-1): Inappropriate ioctl for device"
  196. // container.cmd.SysProcAttr = &syscall.SysProcAttr{Setctty: true, Setsid: true}
  197. go func() {
  198. defer container.stdin.Close()
  199. Debugf("[startPty] Begin of stdin pipe")
  200. io.Copy(stdinMaster, container.stdin)
  201. Debugf("[startPty] End of stdin pipe")
  202. }()
  203. }
  204. if err := container.cmd.Start(); err != nil {
  205. return err
  206. }
  207. stdoutSlave.Close()
  208. stderrSlave.Close()
  209. if stdinSlave != nil {
  210. stdinSlave.Close()
  211. }
  212. return nil
  213. }
  214. func (container *Container) start() error {
  215. container.cmd.Stdout = container.stdout
  216. container.cmd.Stderr = container.stderr
  217. if container.Config.OpenStdin {
  218. stdin, err := container.cmd.StdinPipe()
  219. if err != nil {
  220. return err
  221. }
  222. go func() {
  223. defer stdin.Close()
  224. Debugf("Begin of stdin pipe [start]")
  225. io.Copy(stdin, container.stdin)
  226. Debugf("End of stdin pipe [start]")
  227. }()
  228. }
  229. return container.cmd.Start()
  230. }
  231. func (container *Container) Attach(stdin io.ReadCloser, stdinCloser io.Closer, stdout io.Writer, stderr io.Writer) chan error {
  232. var cStdout, cStderr io.ReadCloser
  233. var nJobs int
  234. errors := make(chan error, 3)
  235. if stdin != nil && container.Config.OpenStdin {
  236. nJobs += 1
  237. if cStdin, err := container.StdinPipe(); err != nil {
  238. errors <- err
  239. } else {
  240. go func() {
  241. Debugf("[start] attach stdin\n")
  242. defer Debugf("[end] attach stdin\n")
  243. if container.Config.StdinOnce {
  244. defer cStdin.Close()
  245. }
  246. _, err := io.Copy(cStdin, stdin)
  247. if err != nil {
  248. Debugf("[error] attach stdout: %s\n", err)
  249. }
  250. // Discard error, expecting pipe error
  251. errors <- nil
  252. }()
  253. }
  254. }
  255. if stdout != nil {
  256. nJobs += 1
  257. if p, err := container.StdoutPipe(); err != nil {
  258. errors <- err
  259. } else {
  260. cStdout = p
  261. go func() {
  262. Debugf("[start] attach stdout\n")
  263. defer Debugf("[end] attach stdout\n")
  264. // If we are in StdinOnce mode, then close stdin
  265. if container.Config.StdinOnce {
  266. if stdin != nil {
  267. defer stdin.Close()
  268. }
  269. if stdinCloser != nil {
  270. defer stdinCloser.Close()
  271. }
  272. }
  273. _, err := io.Copy(stdout, cStdout)
  274. if err != nil {
  275. Debugf("[error] attach stdout: %s\n", err)
  276. }
  277. errors <- err
  278. }()
  279. }
  280. }
  281. if stderr != nil {
  282. nJobs += 1
  283. if p, err := container.StderrPipe(); err != nil {
  284. errors <- err
  285. } else {
  286. cStderr = p
  287. go func() {
  288. Debugf("[start] attach stderr\n")
  289. defer Debugf("[end] attach stderr\n")
  290. // If we are in StdinOnce mode, then close stdin
  291. if container.Config.StdinOnce {
  292. if stdin != nil {
  293. defer stdin.Close()
  294. }
  295. if stdinCloser != nil {
  296. defer stdinCloser.Close()
  297. }
  298. }
  299. _, err := io.Copy(stderr, cStderr)
  300. if err != nil {
  301. Debugf("[error] attach stderr: %s\n", err)
  302. }
  303. errors <- err
  304. }()
  305. }
  306. }
  307. return Go(func() error {
  308. if cStdout != nil {
  309. defer cStdout.Close()
  310. }
  311. if cStderr != nil {
  312. defer cStderr.Close()
  313. }
  314. // FIXME: how do clean up the stdin goroutine without the unwanted side effect
  315. // of closing the passed stdin? Add an intermediary io.Pipe?
  316. for i := 0; i < nJobs; i += 1 {
  317. Debugf("Waiting for job %d/%d\n", i+1, nJobs)
  318. if err := <-errors; err != nil {
  319. Debugf("Job %d returned error %s. Aborting all jobs\n", i+1, err)
  320. return err
  321. }
  322. Debugf("Job %d completed successfully\n", i+1)
  323. }
  324. Debugf("All jobs completed successfully\n")
  325. return nil
  326. })
  327. }
  328. func (container *Container) Start() error {
  329. if container.State.Running {
  330. return fmt.Errorf("The container %s is already running.", container.Id)
  331. }
  332. if err := container.EnsureMounted(); err != nil {
  333. return err
  334. }
  335. if err := container.allocateNetwork(); err != nil {
  336. return err
  337. }
  338. if err := container.generateLXCConfig(); err != nil {
  339. return err
  340. }
  341. params := []string{
  342. "-n", container.Id,
  343. "-f", container.lxcConfigPath(),
  344. "--",
  345. "/sbin/init",
  346. }
  347. // Networking
  348. params = append(params, "-g", container.network.Gateway.String())
  349. // User
  350. if container.Config.User != "" {
  351. params = append(params, "-u", container.Config.User)
  352. }
  353. // Program
  354. params = append(params, "--", container.Path)
  355. params = append(params, container.Args...)
  356. container.cmd = exec.Command("lxc-start", params...)
  357. // Setup environment
  358. container.cmd.Env = append(
  359. []string{
  360. "HOME=/",
  361. "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
  362. },
  363. container.Config.Env...,
  364. )
  365. // Setup logging of stdout and stderr to disk
  366. if err := container.runtime.LogToDisk(container.stdout, container.logPath("stdout")); err != nil {
  367. return err
  368. }
  369. if err := container.runtime.LogToDisk(container.stderr, container.logPath("stderr")); err != nil {
  370. return err
  371. }
  372. var err error
  373. if container.Config.Tty {
  374. container.cmd.Env = append(
  375. []string{"TERM=xterm"},
  376. container.cmd.Env...,
  377. )
  378. err = container.startPty()
  379. } else {
  380. err = container.start()
  381. }
  382. if err != nil {
  383. return err
  384. }
  385. // FIXME: save state on disk *first*, then converge
  386. // this way disk state is used as a journal, eg. we can restore after crash etc.
  387. container.State.setRunning(container.cmd.Process.Pid)
  388. container.ToDisk()
  389. go container.monitor()
  390. return nil
  391. }
  392. func (container *Container) Run() error {
  393. if err := container.Start(); err != nil {
  394. return err
  395. }
  396. container.Wait()
  397. return nil
  398. }
  399. func (container *Container) Output() (output []byte, err error) {
  400. pipe, err := container.StdoutPipe()
  401. if err != nil {
  402. return nil, err
  403. }
  404. defer pipe.Close()
  405. if err := container.Start(); err != nil {
  406. return nil, err
  407. }
  408. output, err = ioutil.ReadAll(pipe)
  409. container.Wait()
  410. return output, err
  411. }
  412. // StdinPipe() returns a pipe connected to the standard input of the container's
  413. // active process.
  414. //
  415. func (container *Container) StdinPipe() (io.WriteCloser, error) {
  416. return container.stdinPipe, nil
  417. }
  418. func (container *Container) StdoutPipe() (io.ReadCloser, error) {
  419. reader, writer := io.Pipe()
  420. container.stdout.AddWriter(writer)
  421. return newBufReader(reader), nil
  422. }
  423. func (container *Container) StderrPipe() (io.ReadCloser, error) {
  424. reader, writer := io.Pipe()
  425. container.stderr.AddWriter(writer)
  426. return newBufReader(reader), nil
  427. }
  428. func (container *Container) allocateNetwork() error {
  429. iface, err := container.runtime.networkManager.Allocate()
  430. if err != nil {
  431. return err
  432. }
  433. container.NetworkSettings.PortMapping = make(map[string]string)
  434. for _, port := range container.Config.Ports {
  435. if extPort, err := iface.AllocatePort(port); err != nil {
  436. iface.Release()
  437. return err
  438. } else {
  439. container.NetworkSettings.PortMapping[strconv.Itoa(port)] = strconv.Itoa(extPort)
  440. }
  441. }
  442. container.network = iface
  443. container.NetworkSettings.IpAddress = iface.IPNet.IP.String()
  444. container.NetworkSettings.IpPrefixLen, _ = iface.IPNet.Mask.Size()
  445. container.NetworkSettings.Gateway = iface.Gateway.String()
  446. return nil
  447. }
  448. func (container *Container) releaseNetwork() {
  449. container.network.Release()
  450. container.network = nil
  451. container.NetworkSettings = &NetworkSettings{}
  452. }
  453. func (container *Container) monitor() {
  454. // Wait for the program to exit
  455. Debugf("Waiting for process")
  456. if err := container.cmd.Wait(); err != nil {
  457. // Discard the error as any signals or non 0 returns will generate an error
  458. Debugf("%s: Process: %s", container.Id, err)
  459. }
  460. Debugf("Process finished")
  461. exitCode := container.cmd.ProcessState.Sys().(syscall.WaitStatus).ExitStatus()
  462. // Cleanup
  463. container.releaseNetwork()
  464. if container.Config.OpenStdin {
  465. if err := container.stdin.Close(); err != nil {
  466. Debugf("%s: Error close stdin: %s", container.Id, err)
  467. }
  468. }
  469. if err := container.stdout.CloseWriters(); err != nil {
  470. Debugf("%s: Error close stdout: %s", container.Id, err)
  471. }
  472. if err := container.stderr.CloseWriters(); err != nil {
  473. Debugf("%s: Error close stderr: %s", container.Id, err)
  474. }
  475. if container.ptyStdinMaster != nil {
  476. if err := container.ptyStdinMaster.Close(); err != nil {
  477. Debugf("%s: Error close pty stdin master: %s", container.Id, err)
  478. }
  479. }
  480. if container.ptyStdoutMaster != nil {
  481. if err := container.ptyStdoutMaster.Close(); err != nil {
  482. Debugf("%s: Error close pty stdout master: %s", container.Id, err)
  483. }
  484. }
  485. if container.ptyStderrMaster != nil {
  486. if err := container.ptyStderrMaster.Close(); err != nil {
  487. Debugf("%s: Error close pty stderr master: %s", container.Id, err)
  488. }
  489. }
  490. if err := container.Unmount(); err != nil {
  491. log.Printf("%v: Failed to umount filesystem: %v", container.Id, err)
  492. }
  493. // Re-create a brand new stdin pipe once the container exited
  494. if container.Config.OpenStdin {
  495. container.stdin, container.stdinPipe = io.Pipe()
  496. }
  497. // Report status back
  498. container.State.setStopped(exitCode)
  499. if err := container.ToDisk(); err != nil {
  500. // FIXME: there is a race condition here which causes this to fail during the unit tests.
  501. // If another goroutine was waiting for Wait() to return before removing the container's root
  502. // from the filesystem... At this point it may already have done so.
  503. // This is because State.setStopped() has already been called, and has caused Wait()
  504. // to return.
  505. // FIXME: why are we serializing running state to disk in the first place?
  506. //log.Printf("%s: Failed to dump configuration to the disk: %s", container.Id, err)
  507. }
  508. }
  509. func (container *Container) kill() error {
  510. if container.cmd == nil {
  511. return nil
  512. }
  513. if err := container.cmd.Process.Kill(); err != nil {
  514. return err
  515. }
  516. // Wait for the container to be actually stopped
  517. container.Wait()
  518. return nil
  519. }
  520. func (container *Container) Kill() error {
  521. if !container.State.Running {
  522. return nil
  523. }
  524. return container.kill()
  525. }
  526. func (container *Container) Stop() error {
  527. if !container.State.Running {
  528. return nil
  529. }
  530. // 1. Send a SIGTERM
  531. if output, err := exec.Command("lxc-kill", "-n", container.Id, "15").CombinedOutput(); err != nil {
  532. log.Print(string(output))
  533. log.Print("Failed to send SIGTERM to the process, force killing")
  534. if err := container.Kill(); err != nil {
  535. return err
  536. }
  537. }
  538. // 2. Wait for the process to exit on its own
  539. if err := container.WaitTimeout(10 * time.Second); err != nil {
  540. log.Printf("Container %v failed to exit within 10 seconds of SIGTERM - using the force", container.Id)
  541. if err := container.Kill(); err != nil {
  542. return err
  543. }
  544. }
  545. return nil
  546. }
  547. func (container *Container) Restart() error {
  548. if err := container.Stop(); err != nil {
  549. return err
  550. }
  551. if err := container.Start(); err != nil {
  552. return err
  553. }
  554. return nil
  555. }
  556. // Wait blocks until the container stops running, then returns its exit code.
  557. func (container *Container) Wait() int {
  558. for container.State.Running {
  559. container.State.wait()
  560. }
  561. return container.State.ExitCode
  562. }
  563. func (container *Container) ExportRw() (Archive, error) {
  564. return Tar(container.rwPath(), Uncompressed)
  565. }
  566. func (container *Container) Export() (Archive, error) {
  567. if err := container.EnsureMounted(); err != nil {
  568. return nil, err
  569. }
  570. return Tar(container.RootfsPath(), Uncompressed)
  571. }
  572. func (container *Container) WaitTimeout(timeout time.Duration) error {
  573. done := make(chan bool)
  574. go func() {
  575. container.Wait()
  576. done <- true
  577. }()
  578. select {
  579. case <-time.After(timeout):
  580. return fmt.Errorf("Timed Out")
  581. case <-done:
  582. return nil
  583. }
  584. return nil
  585. }
  586. func (container *Container) EnsureMounted() error {
  587. if mounted, err := container.Mounted(); err != nil {
  588. return err
  589. } else if mounted {
  590. return nil
  591. }
  592. return container.Mount()
  593. }
  594. func (container *Container) Mount() error {
  595. image, err := container.GetImage()
  596. if err != nil {
  597. return err
  598. }
  599. return image.Mount(container.RootfsPath(), container.rwPath())
  600. }
  601. func (container *Container) Changes() ([]Change, error) {
  602. image, err := container.GetImage()
  603. if err != nil {
  604. return nil, err
  605. }
  606. return image.Changes(container.rwPath())
  607. }
  608. func (container *Container) GetImage() (*Image, error) {
  609. if container.runtime == nil {
  610. return nil, fmt.Errorf("Can't get image of unregistered container")
  611. }
  612. return container.runtime.graph.Get(container.Image)
  613. }
  614. func (container *Container) Mounted() (bool, error) {
  615. return Mounted(container.RootfsPath())
  616. }
  617. func (container *Container) Unmount() error {
  618. return Unmount(container.RootfsPath())
  619. }
  620. // ShortId returns a shorthand version of the container's id for convenience.
  621. // A collision with other container shorthands is very unlikely, but possible.
  622. // In case of a collision a lookup with Runtime.Get() will fail, and the caller
  623. // will need to use a langer prefix, or the full-length container Id.
  624. func (container *Container) ShortId() string {
  625. return TruncateId(container.Id)
  626. }
  627. func (container *Container) logPath(name string) string {
  628. return path.Join(container.root, fmt.Sprintf("%s-%s.log", container.Id, name))
  629. }
  630. func (container *Container) ReadLog(name string) (io.Reader, error) {
  631. return os.Open(container.logPath(name))
  632. }
  633. func (container *Container) jsonPath() string {
  634. return path.Join(container.root, "config.json")
  635. }
  636. func (container *Container) lxcConfigPath() string {
  637. return path.Join(container.root, "config.lxc")
  638. }
  639. // This method must be exported to be used from the lxc template
  640. func (container *Container) RootfsPath() string {
  641. return path.Join(container.root, "rootfs")
  642. }
  643. func (container *Container) rwPath() string {
  644. return path.Join(container.root, "rw")
  645. }
  646. func validateId(id string) error {
  647. if id == "" {
  648. return fmt.Errorf("Invalid empty id")
  649. }
  650. return nil
  651. }