main.go 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259
  1. package userdocker
  2. import (
  3. "io"
  4. "io/ioutil"
  5. "os"
  6. "os/signal"
  7. "strconv"
  8. "syscall"
  9. "time"
  10. "golang.org/x/net/context"
  11. "path/filepath"
  12. log "github.com/Sirupsen/logrus"
  13. "github.com/docker/engine-api/types"
  14. composeClient "github.com/docker/libcompose/docker/client"
  15. "github.com/docker/libcompose/project"
  16. "github.com/rancher/os/cmd/control"
  17. "github.com/rancher/os/compose"
  18. "github.com/rancher/os/config"
  19. rosDocker "github.com/rancher/os/docker"
  20. "github.com/rancher/os/util"
  21. )
  22. const (
  23. DEFAULT_STORAGE_CONTEXT = "console"
  24. DOCKER_PID_FILE = "/var/run/docker.pid"
  25. DOCKER_COMMAND = "docker-init"
  26. userDocker = "user-docker"
  27. )
  28. func Main() {
  29. cfg := config.LoadConfig()
  30. execID, resp, err := startDocker(cfg)
  31. if err != nil {
  32. log.Fatal(err)
  33. }
  34. process, err := getDockerProcess()
  35. if err != nil {
  36. log.Fatal(err)
  37. }
  38. handleTerm(process)
  39. // Wait for Docker daemon to exit
  40. io.Copy(ioutil.Discard, resp.Reader)
  41. resp.Close()
  42. client, err := rosDocker.NewSystemClient()
  43. if err != nil {
  44. log.Fatal(err)
  45. }
  46. state, err := client.ContainerExecInspect(context.Background(), execID)
  47. if err != nil {
  48. log.Fatal(err)
  49. }
  50. // Proxy exit code
  51. os.Exit(state.ExitCode)
  52. }
  53. func writeCerts(cfg *config.CloudConfig) error {
  54. outDir := control.ServerTlsPath
  55. if err := os.MkdirAll(outDir, 0700); err != nil {
  56. return err
  57. }
  58. caCertPath := filepath.Join(outDir, control.CaCert)
  59. caKeyPath := filepath.Join(outDir, control.CaKey)
  60. serverCertPath := filepath.Join(outDir, control.ServerCert)
  61. serverKeyPath := filepath.Join(outDir, control.ServerKey)
  62. if cfg.Rancher.Docker.CACert != "" {
  63. if err := util.WriteFileAtomic(caCertPath, []byte(cfg.Rancher.Docker.CACert), 0400); err != nil {
  64. return err
  65. }
  66. if err := util.WriteFileAtomic(caKeyPath, []byte(cfg.Rancher.Docker.CAKey), 0400); err != nil {
  67. return err
  68. }
  69. }
  70. if cfg.Rancher.Docker.ServerCert != "" {
  71. if err := util.WriteFileAtomic(serverCertPath, []byte(cfg.Rancher.Docker.ServerCert), 0400); err != nil {
  72. return err
  73. }
  74. if err := util.WriteFileAtomic(serverKeyPath, []byte(cfg.Rancher.Docker.ServerKey), 0400); err != nil {
  75. return err
  76. }
  77. }
  78. return nil
  79. }
  80. func startDocker(cfg *config.CloudConfig) (string, types.HijackedResponse, error) {
  81. storageContext := cfg.Rancher.Docker.StorageContext
  82. if storageContext == "" {
  83. storageContext = DEFAULT_STORAGE_CONTEXT
  84. }
  85. log.Infof("Starting Docker in context: %s", storageContext)
  86. p, err := compose.GetProject(cfg, true, false)
  87. if err != nil {
  88. return "", types.HijackedResponse{}, err
  89. }
  90. pid, err := waitForPid(storageContext, p)
  91. if err != nil {
  92. return "", types.HijackedResponse{}, err
  93. }
  94. log.Infof("%s PID %d", storageContext, pid)
  95. client, err := rosDocker.NewSystemClient()
  96. if err != nil {
  97. return "", types.HijackedResponse{}, err
  98. }
  99. if err := os.Remove(DOCKER_PID_FILE); err != nil && !os.IsNotExist(err) {
  100. return "", types.HijackedResponse{}, err
  101. }
  102. dockerCfg := cfg.Rancher.Docker
  103. args := dockerCfg.FullArgs()
  104. log.Debugf("User Docker args: %v", args)
  105. if dockerCfg.TLS {
  106. if err := writeCerts(cfg); err != nil {
  107. return "", types.HijackedResponse{}, err
  108. }
  109. }
  110. cmd := []string{"env"}
  111. log.Info(dockerCfg.AppendEnv())
  112. cmd = append(cmd, dockerCfg.AppendEnv()...)
  113. cmd = append(cmd, DOCKER_COMMAND)
  114. cmd = append(cmd, args...)
  115. log.Infof("Running %v", cmd)
  116. resp, err := client.ContainerExecCreate(context.Background(), types.ExecConfig{
  117. Container: storageContext,
  118. Privileged: true,
  119. AttachStderr: true,
  120. AttachStdout: true,
  121. Cmd: cmd,
  122. })
  123. if err != nil {
  124. return "", types.HijackedResponse{}, err
  125. }
  126. attachResp, err := client.ContainerExecAttach(context.Background(), resp.ID, types.ExecConfig{
  127. Detach: false,
  128. AttachStderr: true,
  129. AttachStdout: true,
  130. })
  131. if err != nil {
  132. return "", types.HijackedResponse{}, err
  133. }
  134. if err := client.ContainerExecStart(context.Background(), resp.ID, types.ExecStartCheck{
  135. Detach: false,
  136. }); err != nil {
  137. return "", types.HijackedResponse{}, err
  138. }
  139. return resp.ID, attachResp, nil
  140. }
  141. func getDockerProcess() (*os.Process, error) {
  142. pidBytes, err := waitForFile(DOCKER_PID_FILE)
  143. if err != nil {
  144. return nil, err
  145. }
  146. dockerPid, err := strconv.Atoi(string(pidBytes))
  147. if err != nil {
  148. return nil, err
  149. }
  150. return os.FindProcess(dockerPid)
  151. }
  152. func handleTerm(process *os.Process) {
  153. term := make(chan os.Signal)
  154. signal.Notify(term, syscall.SIGTERM)
  155. go func() {
  156. <-term
  157. process.Signal(syscall.SIGTERM)
  158. }()
  159. }
  160. func waitForFile(file string) ([]byte, error) {
  161. for {
  162. contents, err := ioutil.ReadFile(file)
  163. if os.IsNotExist(err) {
  164. log.Infof("Waiting for %s", file)
  165. time.Sleep(1 * time.Second)
  166. } else if err != nil {
  167. return nil, err
  168. } else {
  169. return contents, nil
  170. }
  171. }
  172. }
  173. func waitForPid(service string, project *project.Project) (int, error) {
  174. log.Infof("Getting PID for service: %s", service)
  175. for {
  176. if pid, err := getPid(service, project); err != nil || pid == 0 {
  177. log.Infof("Waiting for %s : %d : %v", service, pid, err)
  178. time.Sleep(1 * time.Second)
  179. } else {
  180. return pid, err
  181. }
  182. }
  183. }
  184. func getPid(service string, project *project.Project) (int, error) {
  185. s, err := project.CreateService(service)
  186. if err != nil {
  187. return 0, err
  188. }
  189. containers, err := s.Containers(context.Background())
  190. if err != nil {
  191. return 0, err
  192. }
  193. if len(containers) == 0 {
  194. return 0, nil
  195. }
  196. client, err := composeClient.Create(composeClient.Options{
  197. Host: config.DOCKER_SYSTEM_HOST,
  198. })
  199. if err != nil {
  200. return 0, err
  201. }
  202. id, err := containers[0].ID()
  203. if err != nil {
  204. return 0, err
  205. }
  206. info, err := client.ContainerInspect(context.Background(), id)
  207. if err != nil || info.ID == "" {
  208. return 0, err
  209. }
  210. if info.State.Running {
  211. return info.State.Pid, nil
  212. }
  213. return 0, nil
  214. }