container.go 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540
  1. package docker
  2. import (
  3. "encoding/json"
  4. "errors"
  5. "github.com/dotcloud/docker/fs"
  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. var sysInitPath string
  18. func init() {
  19. sysInitPath = SelfPath()
  20. }
  21. type Container struct {
  22. Id string
  23. Root string
  24. Created time.Time
  25. Path string
  26. Args []string
  27. Config *Config
  28. Mountpoint *fs.Mountpoint
  29. State *State
  30. Image string
  31. network *NetworkInterface
  32. networkManager *NetworkManager
  33. NetworkSettings *NetworkSettings
  34. SysInitPath string
  35. lxcConfigPath string
  36. cmd *exec.Cmd
  37. stdout *writeBroadcaster
  38. stderr *writeBroadcaster
  39. stdin io.ReadCloser
  40. stdinPipe io.WriteCloser
  41. stdoutLog *os.File
  42. stderrLog *os.File
  43. }
  44. type Config struct {
  45. Hostname string
  46. User string
  47. Memory int64 // Memory limit (in bytes)
  48. MemorySwap int64 // Total memory usage (memory + swap); set `-1' to disable swap
  49. Ports []int
  50. Tty bool // Attach standard streams to a tty, including stdin if it is not closed.
  51. OpenStdin bool // Open stdin
  52. }
  53. type NetworkSettings struct {
  54. IpAddress string
  55. IpPrefixLen int
  56. Gateway string
  57. PortMapping map[string]string
  58. }
  59. func createContainer(id string, root string, command string, args []string, image *fs.Image, config *Config, netManager *NetworkManager) (*Container, error) {
  60. mountpoint, err := image.Mountpoint(path.Join(root, "rootfs"), path.Join(root, "rw"))
  61. if err != nil {
  62. return nil, err
  63. }
  64. container := &Container{
  65. Id: id,
  66. Root: root,
  67. Created: time.Now(),
  68. Path: command,
  69. Args: args,
  70. Config: config,
  71. Image: image.Id,
  72. Mountpoint: mountpoint,
  73. State: newState(),
  74. networkManager: netManager,
  75. NetworkSettings: &NetworkSettings{},
  76. SysInitPath: sysInitPath,
  77. lxcConfigPath: path.Join(root, "config.lxc"),
  78. stdout: newWriteBroadcaster(),
  79. stderr: newWriteBroadcaster(),
  80. }
  81. if err := os.Mkdir(root, 0700); err != nil {
  82. return nil, err
  83. }
  84. // Setup logging of stdout and stderr to disk
  85. if stdoutLog, err := os.OpenFile(path.Join(container.Root, id+"-stdout.log"), os.O_RDWR|os.O_APPEND|os.O_CREATE, 0600); err != nil {
  86. return nil, err
  87. } else {
  88. container.stdoutLog = stdoutLog
  89. }
  90. if stderrLog, err := os.OpenFile(path.Join(container.Root, id+"-stderr.log"), os.O_RDWR|os.O_APPEND|os.O_CREATE, 0600); err != nil {
  91. return nil, err
  92. } else {
  93. container.stderrLog = stderrLog
  94. }
  95. if container.Config.OpenStdin {
  96. container.stdin, container.stdinPipe = io.Pipe()
  97. } else {
  98. container.stdinPipe = NopWriteCloser(ioutil.Discard) // Silently drop stdin
  99. }
  100. container.stdout.AddWriter(NopWriteCloser(container.stdoutLog))
  101. container.stderr.AddWriter(NopWriteCloser(container.stderrLog))
  102. if err := container.save(); err != nil {
  103. return nil, err
  104. }
  105. return container, nil
  106. }
  107. func loadContainer(store *fs.Store, containerPath string, netManager *NetworkManager) (*Container, error) {
  108. data, err := ioutil.ReadFile(path.Join(containerPath, "config.json"))
  109. if err != nil {
  110. return nil, err
  111. }
  112. mountpoint, err := store.FetchMountpoint(
  113. path.Join(containerPath, "rootfs"),
  114. path.Join(containerPath, "rw"),
  115. )
  116. if err != nil {
  117. return nil, err
  118. } else if mountpoint == nil {
  119. return nil, errors.New("Couldn't load container: unregistered mountpoint.")
  120. }
  121. container := &Container{
  122. stdout: newWriteBroadcaster(),
  123. stderr: newWriteBroadcaster(),
  124. lxcConfigPath: path.Join(containerPath, "config.lxc"),
  125. networkManager: netManager,
  126. NetworkSettings: &NetworkSettings{},
  127. Mountpoint: mountpoint,
  128. }
  129. // Load container settings
  130. if err := json.Unmarshal(data, container); err != nil {
  131. return nil, err
  132. }
  133. // Setup logging of stdout and stderr to disk
  134. if stdoutLog, err := os.OpenFile(path.Join(container.Root, container.Id+"-stdout.log"), os.O_RDWR|os.O_APPEND|os.O_CREATE, 0600); err != nil {
  135. return nil, err
  136. } else {
  137. container.stdoutLog = stdoutLog
  138. }
  139. if stderrLog, err := os.OpenFile(path.Join(container.Root, container.Id+"-stderr.log"), os.O_RDWR|os.O_APPEND|os.O_CREATE, 0600); err != nil {
  140. return nil, err
  141. } else {
  142. container.stderrLog = stderrLog
  143. }
  144. container.stdout.AddWriter(NopWriteCloser(container.stdoutLog))
  145. container.stderr.AddWriter(NopWriteCloser(container.stderrLog))
  146. if container.Config.OpenStdin {
  147. container.stdin, container.stdinPipe = io.Pipe()
  148. } else {
  149. container.stdinPipe = NopWriteCloser(ioutil.Discard) // Silently drop stdin
  150. }
  151. container.State = newState()
  152. return container, nil
  153. }
  154. func (container *Container) Cmd() *exec.Cmd {
  155. return container.cmd
  156. }
  157. func (container *Container) When() time.Time {
  158. return container.Created
  159. }
  160. func (container *Container) loadUserData() (map[string]string, error) {
  161. jsonData, err := ioutil.ReadFile(path.Join(container.Root, "userdata.json"))
  162. if err != nil {
  163. if os.IsNotExist(err) {
  164. return make(map[string]string), nil
  165. }
  166. return nil, err
  167. }
  168. data := make(map[string]string)
  169. if err := json.Unmarshal(jsonData, &data); err != nil {
  170. return nil, err
  171. }
  172. return data, nil
  173. }
  174. func (container *Container) saveUserData(data map[string]string) error {
  175. jsonData, err := json.Marshal(data)
  176. if err != nil {
  177. return err
  178. }
  179. return ioutil.WriteFile(path.Join(container.Root, "userdata.json"), jsonData, 0700)
  180. }
  181. func (container *Container) SetUserData(key, value string) error {
  182. data, err := container.loadUserData()
  183. if err != nil {
  184. return err
  185. }
  186. data[key] = value
  187. return container.saveUserData(data)
  188. }
  189. func (container *Container) GetUserData(key string) string {
  190. data, err := container.loadUserData()
  191. if err != nil {
  192. return ""
  193. }
  194. if value, exists := data[key]; exists {
  195. return value
  196. }
  197. return ""
  198. }
  199. func (container *Container) save() (err error) {
  200. data, err := json.Marshal(container)
  201. if err != nil {
  202. return
  203. }
  204. return ioutil.WriteFile(path.Join(container.Root, "config.json"), data, 0666)
  205. }
  206. func (container *Container) generateLXCConfig() error {
  207. fo, err := os.Create(container.lxcConfigPath)
  208. if err != nil {
  209. return err
  210. }
  211. defer fo.Close()
  212. if err := LxcTemplateCompiled.Execute(fo, container); err != nil {
  213. return err
  214. }
  215. return nil
  216. }
  217. func (container *Container) startPty() error {
  218. stdout_master, stdout_slave, err := pty.Open()
  219. if err != nil {
  220. return err
  221. }
  222. container.cmd.Stdout = stdout_slave
  223. stderr_master, stderr_slave, err := pty.Open()
  224. if err != nil {
  225. return err
  226. }
  227. container.cmd.Stderr = stderr_slave
  228. // Copy the PTYs to our broadcasters
  229. go func() {
  230. defer container.stdout.Close()
  231. io.Copy(container.stdout, stdout_master)
  232. }()
  233. go func() {
  234. defer container.stderr.Close()
  235. io.Copy(container.stderr, stderr_master)
  236. }()
  237. // stdin
  238. var stdin_slave io.ReadCloser
  239. if container.Config.OpenStdin {
  240. stdin_master, stdin_slave, err := pty.Open()
  241. if err != nil {
  242. return err
  243. }
  244. container.cmd.Stdin = stdin_slave
  245. // FIXME: The following appears to be broken.
  246. // "cannot set terminal process group (-1): Inappropriate ioctl for device"
  247. // container.cmd.SysProcAttr = &syscall.SysProcAttr{Setctty: true, Setsid: true}
  248. go func() {
  249. defer container.stdin.Close()
  250. io.Copy(stdin_master, container.stdin)
  251. }()
  252. }
  253. if err := container.cmd.Start(); err != nil {
  254. return err
  255. }
  256. stdout_slave.Close()
  257. stderr_slave.Close()
  258. if stdin_slave != nil {
  259. stdin_slave.Close()
  260. }
  261. return nil
  262. }
  263. func (container *Container) start() error {
  264. container.cmd.Stdout = container.stdout
  265. container.cmd.Stderr = container.stderr
  266. if container.Config.OpenStdin {
  267. stdin, err := container.cmd.StdinPipe()
  268. if err != nil {
  269. return err
  270. }
  271. go func() {
  272. defer stdin.Close()
  273. io.Copy(stdin, container.stdin)
  274. }()
  275. }
  276. return container.cmd.Start()
  277. }
  278. func (container *Container) Start() error {
  279. if err := container.Mountpoint.EnsureMounted(); err != nil {
  280. return err
  281. }
  282. if err := container.allocateNetwork(); err != nil {
  283. return err
  284. }
  285. if err := container.generateLXCConfig(); err != nil {
  286. return err
  287. }
  288. params := []string{
  289. "-n", container.Id,
  290. "-f", container.lxcConfigPath,
  291. "--",
  292. "/sbin/init",
  293. }
  294. // Networking
  295. params = append(params, "-g", container.network.Gateway.String())
  296. // User
  297. if container.Config.User != "" {
  298. params = append(params, "-u", container.Config.User)
  299. }
  300. // Program
  301. params = append(params, "--", container.Path)
  302. params = append(params, container.Args...)
  303. container.cmd = exec.Command("/usr/bin/lxc-start", params...)
  304. var err error
  305. if container.Config.Tty {
  306. err = container.startPty()
  307. } else {
  308. err = container.start()
  309. }
  310. if err != nil {
  311. return err
  312. }
  313. container.State.setRunning(container.cmd.Process.Pid)
  314. container.save()
  315. go container.monitor()
  316. return nil
  317. }
  318. func (container *Container) Run() error {
  319. if err := container.Start(); err != nil {
  320. return err
  321. }
  322. container.Wait()
  323. return nil
  324. }
  325. func (container *Container) Output() (output []byte, err error) {
  326. pipe, err := container.StdoutPipe()
  327. if err != nil {
  328. return nil, err
  329. }
  330. defer pipe.Close()
  331. if err := container.Start(); err != nil {
  332. return nil, err
  333. }
  334. output, err = ioutil.ReadAll(pipe)
  335. container.Wait()
  336. return output, err
  337. }
  338. // StdinPipe() returns a pipe connected to the standard input of the container's
  339. // active process.
  340. //
  341. func (container *Container) StdinPipe() (io.WriteCloser, error) {
  342. return container.stdinPipe, nil
  343. }
  344. func (container *Container) StdoutPipe() (io.ReadCloser, error) {
  345. reader, writer := io.Pipe()
  346. container.stdout.AddWriter(writer)
  347. return newBufReader(reader), nil
  348. }
  349. func (container *Container) StdoutLog() io.Reader {
  350. r, err := os.Open(container.stdoutLog.Name())
  351. if err != nil {
  352. return nil
  353. }
  354. return r
  355. }
  356. func (container *Container) StderrPipe() (io.ReadCloser, error) {
  357. reader, writer := io.Pipe()
  358. container.stderr.AddWriter(writer)
  359. return newBufReader(reader), nil
  360. }
  361. func (container *Container) StderrLog() io.Reader {
  362. r, err := os.Open(container.stderrLog.Name())
  363. if err != nil {
  364. return nil
  365. }
  366. return r
  367. }
  368. func (container *Container) allocateNetwork() error {
  369. iface, err := container.networkManager.Allocate()
  370. if err != nil {
  371. return err
  372. }
  373. container.NetworkSettings.PortMapping = make(map[string]string)
  374. for _, port := range container.Config.Ports {
  375. if extPort, err := iface.AllocatePort(port); err != nil {
  376. iface.Release()
  377. return err
  378. } else {
  379. container.NetworkSettings.PortMapping[strconv.Itoa(port)] = strconv.Itoa(extPort)
  380. }
  381. }
  382. container.network = iface
  383. container.NetworkSettings.IpAddress = iface.IPNet.IP.String()
  384. container.NetworkSettings.IpPrefixLen, _ = iface.IPNet.Mask.Size()
  385. container.NetworkSettings.Gateway = iface.Gateway.String()
  386. return nil
  387. }
  388. func (container *Container) releaseNetwork() error {
  389. err := container.network.Release()
  390. container.network = nil
  391. container.NetworkSettings = &NetworkSettings{}
  392. return err
  393. }
  394. func (container *Container) monitor() {
  395. // Wait for the program to exit
  396. container.cmd.Wait()
  397. exitCode := container.cmd.ProcessState.Sys().(syscall.WaitStatus).ExitStatus()
  398. // Cleanup
  399. if err := container.releaseNetwork(); err != nil {
  400. log.Printf("%v: Failed to release network: %v", container.Id, err)
  401. }
  402. container.stdout.Close()
  403. container.stderr.Close()
  404. if err := container.Mountpoint.Umount(); err != nil {
  405. log.Printf("%v: Failed to umount filesystem: %v", container.Id, err)
  406. }
  407. // Re-create a brand new stdin pipe once the container exited
  408. if container.Config.OpenStdin {
  409. container.stdin, container.stdinPipe = io.Pipe()
  410. }
  411. // Report status back
  412. container.State.setStopped(exitCode)
  413. container.save()
  414. }
  415. func (container *Container) kill() error {
  416. if err := container.cmd.Process.Kill(); err != nil {
  417. return err
  418. }
  419. // Wait for the container to be actually stopped
  420. container.Wait()
  421. return nil
  422. }
  423. func (container *Container) Kill() error {
  424. if !container.State.Running {
  425. return nil
  426. }
  427. return container.kill()
  428. }
  429. func (container *Container) Stop() error {
  430. if !container.State.Running {
  431. return nil
  432. }
  433. // 1. Send a SIGTERM
  434. if output, err := exec.Command("/usr/bin/lxc-kill", "-n", container.Id, "15").CombinedOutput(); err != nil {
  435. log.Printf(string(output))
  436. log.Printf("Failed to send SIGTERM to the process, force killing")
  437. if err := container.Kill(); err != nil {
  438. return err
  439. }
  440. }
  441. // 2. Wait for the process to exit on its own
  442. if err := container.WaitTimeout(10 * time.Second); err != nil {
  443. log.Printf("Container %v failed to exit within 10 seconds of SIGTERM - using the force", container.Id)
  444. if err := container.Kill(); err != nil {
  445. return err
  446. }
  447. }
  448. return nil
  449. }
  450. func (container *Container) Restart() error {
  451. if err := container.Stop(); err != nil {
  452. return err
  453. }
  454. if err := container.Start(); err != nil {
  455. return err
  456. }
  457. return nil
  458. }
  459. // Wait blocks until the container stops running, then returns its exit code.
  460. func (container *Container) Wait() int {
  461. for container.State.Running {
  462. container.State.wait()
  463. }
  464. return container.State.ExitCode
  465. }
  466. func (container *Container) WaitTimeout(timeout time.Duration) error {
  467. done := make(chan bool)
  468. go func() {
  469. container.Wait()
  470. done <- true
  471. }()
  472. select {
  473. case <-time.After(timeout):
  474. return errors.New("Timed Out")
  475. case <-done:
  476. return nil
  477. }
  478. return nil
  479. }