container.go 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311
  1. package docker
  2. import (
  3. "encoding/json"
  4. "errors"
  5. "io"
  6. "io/ioutil"
  7. "log"
  8. "os"
  9. "os/exec"
  10. "path"
  11. "syscall"
  12. "time"
  13. )
  14. type Container struct {
  15. Id string
  16. Root string
  17. Created time.Time
  18. Path string
  19. Args []string
  20. Config *Config
  21. Filesystem *Filesystem
  22. State *State
  23. lxcConfigPath string
  24. cmd *exec.Cmd
  25. stdout *writeBroadcaster
  26. stderr *writeBroadcaster
  27. }
  28. type Config struct {
  29. Hostname string
  30. Ram int64
  31. }
  32. func createContainer(id string, root string, command string, args []string, layers []string, config *Config) (*Container, error) {
  33. container := &Container{
  34. Id: id,
  35. Root: root,
  36. Created: time.Now(),
  37. Path: command,
  38. Args: args,
  39. Config: config,
  40. Filesystem: newFilesystem(path.Join(root, "rootfs"), path.Join(root, "rw"), layers),
  41. State: newState(),
  42. lxcConfigPath: path.Join(root, "config.lxc"),
  43. stdout: newWriteBroadcaster(),
  44. stderr: newWriteBroadcaster(),
  45. }
  46. if err := os.Mkdir(root, 0700); err != nil {
  47. return nil, err
  48. }
  49. if err := container.save(); err != nil {
  50. return nil, err
  51. }
  52. if err := container.generateLXCConfig(); err != nil {
  53. return nil, err
  54. }
  55. return container, nil
  56. }
  57. func loadContainer(containerPath string) (*Container, error) {
  58. data, err := ioutil.ReadFile(path.Join(containerPath, "config.json"))
  59. if err != nil {
  60. return nil, err
  61. }
  62. container := &Container{}
  63. if err := json.Unmarshal(data, container); err != nil {
  64. return nil, err
  65. }
  66. return container, nil
  67. }
  68. func (container *Container) loadUserData() (map[string]string, error) {
  69. jsonData, err := ioutil.ReadFile(path.Join(container.Root, "userdata.json"))
  70. if err != nil {
  71. if os.IsNotExist(err) {
  72. return make(map[string]string), nil
  73. }
  74. return nil, err
  75. }
  76. data := make(map[string]string)
  77. if err := json.Unmarshal(jsonData, &data); err != nil {
  78. return nil, err
  79. }
  80. return data, nil
  81. }
  82. func (container *Container) saveUserData(data map[string]string) error {
  83. jsonData, err := json.Marshal(data)
  84. if err != nil {
  85. return err
  86. }
  87. return ioutil.WriteFile(path.Join(container.Root, "userdata.json"), jsonData, 0700)
  88. }
  89. func (container *Container) SetUserData(key, value string) error {
  90. data, err := container.loadUserData()
  91. if err != nil {
  92. return err
  93. }
  94. data[key] = value
  95. return container.saveUserData(data)
  96. }
  97. func (container *Container) GetUserData(key string) (string) {
  98. data, err := container.loadUserData()
  99. if err != nil {
  100. return ""
  101. }
  102. if value, exists := data[key]; exists {
  103. return value
  104. }
  105. return ""
  106. }
  107. func (container *Container) save() (err error) {
  108. data, err := json.Marshal(container)
  109. if err != nil {
  110. return
  111. }
  112. return ioutil.WriteFile(path.Join(container.Root, "config.json"), data, 0700)
  113. }
  114. func (container *Container) generateLXCConfig() error {
  115. fo, err := os.Create(container.lxcConfigPath)
  116. if err != nil {
  117. return err
  118. }
  119. defer fo.Close()
  120. if err := LxcTemplateCompiled.Execute(fo, container); err != nil {
  121. return err
  122. }
  123. return nil
  124. }
  125. func (container *Container) Start() error {
  126. if err := container.Filesystem.Mount(); err != nil {
  127. return err
  128. }
  129. params := []string{
  130. "-n", container.Id,
  131. "-f", container.lxcConfigPath,
  132. "--",
  133. container.Path,
  134. }
  135. params = append(params, container.Args...)
  136. container.cmd = exec.Command("/usr/bin/lxc-start", params...)
  137. container.cmd.Stdout = container.stdout
  138. container.cmd.Stderr = container.stderr
  139. if err := container.cmd.Start(); err != nil {
  140. return err
  141. }
  142. container.State.setRunning(container.cmd.Process.Pid)
  143. container.save()
  144. go container.monitor()
  145. if err := exec.Command("/usr/bin/lxc-wait", "-n", container.Id, "-s", "RUNNING|STOPPED").Run(); err != nil {
  146. // lxc-wait might return an error if by the time we call it,
  147. // the container we just started is already STOPPED.
  148. // This is a rare race condition that happens for short living programs.
  149. //
  150. // A workaround is to discard lxc-wait errors if the container is not
  151. // running anymore.
  152. if !container.State.Running {
  153. return nil
  154. }
  155. return errors.New("Container failed to start")
  156. }
  157. return nil
  158. }
  159. func (container *Container) Run() error {
  160. if err := container.Start(); err != nil {
  161. return err
  162. }
  163. container.Wait()
  164. return nil
  165. }
  166. func (container *Container) Output() (output []byte, err error) {
  167. pipe, err := container.StdoutPipe()
  168. if err != nil {
  169. return nil, err
  170. }
  171. defer pipe.Close()
  172. if err := container.Start(); err != nil {
  173. return nil, err
  174. }
  175. output, err = ioutil.ReadAll(pipe)
  176. container.Wait()
  177. return output, err
  178. }
  179. func (container *Container) StdoutPipe() (io.ReadCloser, error) {
  180. reader, writer := io.Pipe()
  181. container.stdout.AddWriter(writer)
  182. return newBufReader(reader), nil
  183. }
  184. func (container *Container) StderrPipe() (io.ReadCloser, error) {
  185. reader, writer := io.Pipe()
  186. container.stderr.AddWriter(writer)
  187. return newBufReader(reader), nil
  188. }
  189. func (container *Container) monitor() {
  190. // Wait for the program to exit
  191. container.cmd.Wait()
  192. exitCode := container.cmd.ProcessState.Sys().(syscall.WaitStatus).ExitStatus()
  193. // Cleanup
  194. container.stdout.Close()
  195. container.stderr.Close()
  196. if err := container.Filesystem.Umount(); err != nil {
  197. log.Printf("%v: Failed to umount filesystem: %v", container.Id, err)
  198. }
  199. // Report status back
  200. container.State.setStopped(exitCode)
  201. container.save()
  202. }
  203. func (container *Container) kill() error {
  204. // This will cause the main container process to receive a SIGKILL
  205. if err := exec.Command("/usr/bin/lxc-stop", "-n", container.Id).Run(); err != nil {
  206. log.Printf("Failed to lxc-stop %v", container.Id)
  207. return err
  208. }
  209. // Wait for the container to be actually stopped
  210. container.Wait()
  211. // Make sure the underlying LXC thinks it's stopped too
  212. // LXC Issue: lxc-wait MIGHT say that the container doesn't exist
  213. // That's probably because it was destroyed and it cannot find it anymore
  214. // We are going to ignore lxc-wait's error
  215. exec.Command("/usr/bin/lxc-wait", "-n", container.Id, "-s", "STOPPED").Run()
  216. return nil
  217. }
  218. func (container *Container) Kill() error {
  219. if !container.State.Running {
  220. return nil
  221. }
  222. return container.kill()
  223. }
  224. func (container *Container) Stop() error {
  225. if !container.State.Running {
  226. return nil
  227. }
  228. // 1. Send a SIGTERM
  229. if err := exec.Command("/usr/bin/lxc-kill", "-n", container.Id, "15").Run(); err != nil {
  230. return err
  231. }
  232. // 2. Wait for the process to exit on its own
  233. if err := container.WaitTimeout(10 * time.Second); err != nil {
  234. log.Printf("Container %v failed to exit within 10 seconds of SIGTERM", container.Id)
  235. }
  236. // 3. Force kill
  237. if err := container.kill(); err != nil {
  238. return err
  239. }
  240. return nil
  241. }
  242. func (container *Container) Restart() error {
  243. if err := container.Stop(); err != nil {
  244. return err
  245. }
  246. if err := container.Start(); err != nil {
  247. return err
  248. }
  249. return nil
  250. }
  251. func (container *Container) Wait() {
  252. for container.State.Running {
  253. container.State.wait()
  254. }
  255. }
  256. func (container *Container) WaitTimeout(timeout time.Duration) error {
  257. done := make(chan bool)
  258. go func() {
  259. container.Wait()
  260. done <- true
  261. }()
  262. select {
  263. case <-time.After(timeout):
  264. return errors.New("Timed Out")
  265. case <-done:
  266. return nil
  267. }
  268. return nil
  269. }