power.go 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300
  1. package power
  2. import (
  3. "errors"
  4. "fmt"
  5. "os"
  6. "path/filepath"
  7. "strconv"
  8. "strings"
  9. "syscall"
  10. "time"
  11. "golang.org/x/net/context"
  12. "github.com/docker/engine-api/types"
  13. "github.com/docker/engine-api/types/container"
  14. "github.com/docker/engine-api/types/filters"
  15. "github.com/rancher/os/config"
  16. "github.com/rancher/os/log"
  17. "github.com/rancher/os/docker"
  18. "github.com/rancher/os/util"
  19. )
  20. func runDocker(name string) error {
  21. if os.ExpandEnv("${IN_DOCKER}") == "true" {
  22. return nil
  23. }
  24. client, err := docker.NewSystemClient()
  25. if err != nil {
  26. return err
  27. }
  28. cmd := []string{name}
  29. if name == "" {
  30. name = filepath.Base(os.Args[0])
  31. cmd = os.Args
  32. }
  33. existing, err := client.ContainerInspect(context.Background(), name)
  34. if err == nil && existing.ID != "" {
  35. err := client.ContainerRemove(context.Background(), types.ContainerRemoveOptions{
  36. ContainerID: existing.ID,
  37. })
  38. if err != nil {
  39. return err
  40. }
  41. }
  42. currentContainerID, err := util.GetCurrentContainerID()
  43. if err != nil {
  44. return err
  45. }
  46. currentContainer, err := client.ContainerInspect(context.Background(), currentContainerID)
  47. if err != nil {
  48. return err
  49. }
  50. powerContainer, err := client.ContainerCreate(context.Background(),
  51. &container.Config{
  52. Image: currentContainer.Config.Image,
  53. Cmd: cmd,
  54. Env: []string{
  55. "IN_DOCKER=true",
  56. },
  57. },
  58. &container.HostConfig{
  59. PidMode: "host",
  60. VolumesFrom: []string{
  61. currentContainer.ID,
  62. },
  63. Privileged: true,
  64. }, nil, name)
  65. if err != nil {
  66. return err
  67. }
  68. err = client.ContainerStart(context.Background(), powerContainer.ID)
  69. if err != nil {
  70. return err
  71. }
  72. reader, err := client.ContainerLogs(context.Background(), types.ContainerLogsOptions{
  73. ContainerID: powerContainer.ID,
  74. ShowStderr: true,
  75. ShowStdout: true,
  76. Follow: true,
  77. })
  78. if err != nil {
  79. log.Fatal(err)
  80. }
  81. for {
  82. p := make([]byte, 4096)
  83. n, err := reader.Read(p)
  84. if err != nil {
  85. log.Error(err)
  86. if n == 0 {
  87. reader.Close()
  88. break
  89. }
  90. }
  91. if n > 0 {
  92. fmt.Print(string(p))
  93. }
  94. }
  95. if err != nil {
  96. log.Fatal(err)
  97. }
  98. os.Exit(0)
  99. return nil
  100. }
  101. func common(name string) {
  102. if os.Geteuid() != 0 {
  103. log.Fatalf("%s: Need to be root", os.Args[0])
  104. }
  105. if err := runDocker(name); err != nil {
  106. log.Fatal(err)
  107. }
  108. }
  109. func Off() {
  110. common("poweroff")
  111. reboot(syscall.LINUX_REBOOT_CMD_POWER_OFF)
  112. }
  113. func Reboot() {
  114. common("reboot")
  115. reboot(syscall.LINUX_REBOOT_CMD_RESTART)
  116. }
  117. func Halt() {
  118. common("halt")
  119. reboot(syscall.LINUX_REBOOT_CMD_HALT)
  120. }
  121. func reboot(code uint) {
  122. cfg := config.LoadConfig()
  123. timeoutValue := cfg.Rancher.ShutdownTimeout
  124. if timeoutValue == 0 {
  125. timeoutValue = 60
  126. }
  127. if timeoutValue < 5 {
  128. timeoutValue = 5
  129. }
  130. log.Infof("Setting %s timeout to %d (rancher.shutdown_timeout set to %d)", os.Args[0], timeoutValue, cfg.Rancher.ShutdownTimeout)
  131. go func() {
  132. timeout := time.After(time.Duration(timeoutValue) * time.Second)
  133. tick := time.Tick(100 * time.Millisecond)
  134. // Keep trying until we're timed out or got a result or got an error
  135. for {
  136. select {
  137. // Got a timeout! fail with a timeout error
  138. case <-timeout:
  139. log.Errorf("Container shutdown taking too long, forcing %s.", os.Args[0])
  140. syscall.Sync()
  141. syscall.Reboot(int(code))
  142. case <-tick:
  143. fmt.Printf(".")
  144. }
  145. }
  146. }()
  147. err := shutDownContainers()
  148. if err != nil {
  149. log.Error(err)
  150. }
  151. syscall.Sync()
  152. err = syscall.Reboot(int(code))
  153. if err != nil {
  154. log.Fatal(err)
  155. }
  156. }
  157. func shutDownContainers() error {
  158. var err error
  159. shutDown := true
  160. timeout := 2
  161. for i, arg := range os.Args {
  162. if arg == "-f" || arg == "--f" || arg == "--force" {
  163. shutDown = false
  164. }
  165. if arg == "-t" || arg == "--t" || arg == "--timeout" {
  166. if len(os.Args) > i+1 {
  167. t, err := strconv.Atoi(os.Args[i+1])
  168. if err != nil {
  169. return err
  170. }
  171. timeout = t
  172. } else {
  173. log.Error("please specify a timeout")
  174. }
  175. }
  176. }
  177. if !shutDown {
  178. return nil
  179. }
  180. client, err := docker.NewSystemClient()
  181. if err != nil {
  182. return err
  183. }
  184. filter := filters.NewArgs()
  185. filter.Add("status", "running")
  186. opts := types.ContainerListOptions{
  187. All: true,
  188. Filter: filter,
  189. }
  190. containers, err := client.ContainerList(context.Background(), opts)
  191. if err != nil {
  192. return err
  193. }
  194. currentContainerID, err := util.GetCurrentContainerID()
  195. if err != nil {
  196. return err
  197. }
  198. var stopErrorStrings []string
  199. consoleContainerIdx := -1
  200. for idx, container := range containers {
  201. if container.ID == currentContainerID {
  202. continue
  203. }
  204. if container.Names[0] == "/console" {
  205. consoleContainerIdx = idx
  206. continue
  207. }
  208. log.Infof("Stopping %s : %s", container.Names[0], container.ID[:12])
  209. stopErr := client.ContainerStop(context.Background(), container.ID, timeout)
  210. if stopErr != nil {
  211. log.Errorf("------- Error Stopping %s : %s", container.Names[0], stopErr.Error())
  212. stopErrorStrings = append(stopErrorStrings, " ["+container.ID+"] "+stopErr.Error())
  213. }
  214. }
  215. // lets see what containers are still running and only wait on those
  216. containers, err = client.ContainerList(context.Background(), opts)
  217. if err != nil {
  218. return err
  219. }
  220. var waitErrorStrings []string
  221. for idx, container := range containers {
  222. if container.ID == currentContainerID {
  223. continue
  224. }
  225. if container.Names[0] == "/console" {
  226. consoleContainerIdx = idx
  227. continue
  228. }
  229. log.Infof("Waiting %s : %s", container.Names[0], container.ID[:12])
  230. _, waitErr := client.ContainerWait(context.Background(), container.ID)
  231. if waitErr != nil {
  232. log.Errorf("------- Error Waiting %s : %s", container.Names[0], waitErr.Error())
  233. waitErrorStrings = append(waitErrorStrings, " ["+container.ID+"] "+waitErr.Error())
  234. }
  235. }
  236. // and now stop the console
  237. if consoleContainerIdx != -1 {
  238. container := containers[consoleContainerIdx]
  239. log.Infof("Console Stopping %v : %s", container.Names, container.ID[:12])
  240. stopErr := client.ContainerStop(context.Background(), container.ID, timeout)
  241. if stopErr != nil {
  242. log.Errorf("------- Error Stopping %v : %s", container.Names, stopErr.Error())
  243. stopErrorStrings = append(stopErrorStrings, " ["+container.ID+"] "+stopErr.Error())
  244. }
  245. log.Infof("Console Waiting %v : %s", container.Names, container.ID[:12])
  246. _, waitErr := client.ContainerWait(context.Background(), container.ID)
  247. if waitErr != nil {
  248. log.Errorf("------- Error Waiting %v : %s", container.Names, waitErr.Error())
  249. waitErrorStrings = append(waitErrorStrings, " ["+container.ID+"] "+waitErr.Error())
  250. }
  251. }
  252. if len(waitErrorStrings) != 0 || len(stopErrorStrings) != 0 {
  253. return errors.New("error while stopping \n1. STOP Errors [" + strings.Join(stopErrorStrings, ",") + "] \n2. WAIT Errors [" + strings.Join(waitErrorStrings, ",") + "]")
  254. }
  255. return nil
  256. }