container.go 12 KB

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