container.go 11 KB

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