console.go 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205
  1. package control
  2. import (
  3. "fmt"
  4. "sort"
  5. "strings"
  6. "github.com/rancher/os/cmd/control/service"
  7. "github.com/rancher/os/config"
  8. "github.com/rancher/os/pkg/compose"
  9. "github.com/rancher/os/pkg/docker"
  10. "github.com/rancher/os/pkg/log"
  11. "github.com/rancher/os/pkg/util"
  12. "github.com/rancher/os/pkg/util/network"
  13. "github.com/codegangsta/cli"
  14. "github.com/docker/docker/reference"
  15. composeConfig "github.com/docker/libcompose/config"
  16. "github.com/docker/libcompose/project/options"
  17. "golang.org/x/net/context"
  18. )
  19. func consoleSubcommands() []cli.Command {
  20. return []cli.Command{
  21. {
  22. Name: "switch",
  23. Usage: "switch console without a reboot",
  24. Action: consoleSwitch,
  25. Flags: []cli.Flag{
  26. cli.BoolFlag{
  27. Name: "force, f",
  28. Usage: "do not prompt for input",
  29. },
  30. cli.BoolFlag{
  31. Name: "no-pull",
  32. Usage: "don't pull console image",
  33. },
  34. },
  35. },
  36. {
  37. Name: "enable",
  38. Usage: "set console to be switched on next reboot",
  39. Action: consoleEnable,
  40. },
  41. {
  42. Name: "list",
  43. Usage: "list available consoles",
  44. Flags: []cli.Flag{
  45. cli.BoolFlag{
  46. Name: "update, u",
  47. Usage: "update console cache",
  48. },
  49. },
  50. Action: consoleList,
  51. },
  52. }
  53. }
  54. func consoleSwitch(c *cli.Context) error {
  55. if len(c.Args()) != 1 {
  56. log.Fatal("Must specify exactly one console to switch to")
  57. }
  58. newConsole := c.Args()[0]
  59. cfg := config.LoadConfig()
  60. validateConsole(newConsole, cfg)
  61. if newConsole == CurrentConsole() {
  62. log.Warnf("Console is already set to %s", newConsole)
  63. }
  64. if !c.Bool("force") {
  65. fmt.Println(`Switching consoles will
  66. 1. destroy the current console container
  67. 2. log you out
  68. 3. restart Docker`)
  69. if !yes("Continue") {
  70. return nil
  71. }
  72. }
  73. if !c.Bool("no-pull") && newConsole != "default" {
  74. if err := compose.StageServices(cfg, newConsole); err != nil {
  75. return err
  76. }
  77. }
  78. service, err := compose.CreateService(nil, "switch-console", &composeConfig.ServiceConfigV1{
  79. LogDriver: "json-file",
  80. Privileged: true,
  81. Net: "host",
  82. Pid: "host",
  83. Image: config.OsBase,
  84. Labels: map[string]string{
  85. config.ScopeLabel: config.System,
  86. },
  87. Command: []string{"/usr/bin/ros", "switch-console", newConsole},
  88. VolumesFrom: []string{"all-volumes"},
  89. })
  90. if err != nil {
  91. return err
  92. }
  93. if err = service.Delete(context.Background(), options.Delete{}); err != nil {
  94. return err
  95. }
  96. if err = service.Up(context.Background(), options.Up{}); err != nil {
  97. return err
  98. }
  99. return service.Log(context.Background(), true)
  100. }
  101. func consoleEnable(c *cli.Context) error {
  102. if len(c.Args()) != 1 {
  103. log.Fatal("Must specify exactly one console to enable")
  104. }
  105. newConsole := c.Args()[0]
  106. cfg := config.LoadConfig()
  107. validateConsole(newConsole, cfg)
  108. if newConsole != "default" {
  109. if err := compose.StageServices(cfg, newConsole); err != nil {
  110. return err
  111. }
  112. }
  113. if err := config.Set("rancher.console", newConsole); err != nil {
  114. log.Errorf("Failed to update 'rancher.console': %v", err)
  115. }
  116. return nil
  117. }
  118. func consoleList(c *cli.Context) error {
  119. cfg := config.LoadConfig()
  120. consoles := availableConsoles(cfg, c.Bool("update"))
  121. CurrentConsole := CurrentConsole()
  122. for _, console := range consoles {
  123. if console == CurrentConsole {
  124. fmt.Printf("current %s\n", console)
  125. } else if console == cfg.Rancher.Console {
  126. fmt.Printf("enabled %s\n", console)
  127. } else {
  128. fmt.Printf("disabled %s\n", console)
  129. }
  130. }
  131. return nil
  132. }
  133. func validateConsole(console string, cfg *config.CloudConfig) {
  134. consoles := availableConsoles(cfg, false)
  135. if !service.IsLocalOrURL(console) && !util.Contains(consoles, console) {
  136. log.Fatalf("%s is not a valid console", console)
  137. }
  138. }
  139. func availableConsoles(cfg *config.CloudConfig, update bool) []string {
  140. if update {
  141. err := network.UpdateCaches(cfg.Rancher.Repositories.ToArray(), "consoles")
  142. if err != nil {
  143. log.Debugf("Failed to update console caches: %v", err)
  144. }
  145. }
  146. consoles, err := network.GetConsoles(cfg.Rancher.Repositories.ToArray())
  147. if err != nil {
  148. log.Fatal(err)
  149. }
  150. consoles = append(consoles, "default")
  151. sort.Strings(consoles)
  152. return consoles
  153. }
  154. // CurrentConsole gets the name of the console that's running
  155. func CurrentConsole() (console string) {
  156. // TODO: replace this docker container look up with a libcompose service lookup?
  157. // sudo system-docker inspect --format "{{.Config.Image}}" console
  158. client, err := docker.NewSystemClient()
  159. if err != nil {
  160. log.Warnf("Failed to detect current console: %v", err)
  161. return
  162. }
  163. info, err := client.ContainerInspect(context.Background(), "console")
  164. if err != nil {
  165. log.Warnf("Failed to detect current console: %v", err)
  166. return
  167. }
  168. // parse image name, then remove os- prefix and the console suffix
  169. image, err := reference.ParseNamed(info.Config.Image)
  170. if err != nil {
  171. log.Warnf("Failed to detect current console(%s): %v", info.Config.Image, err)
  172. return
  173. }
  174. if strings.Contains(image.Name(), "os-console") {
  175. console = "default"
  176. return
  177. }
  178. console = strings.TrimPrefix(strings.TrimSuffix(image.Name(), "console"), "rancher/os-")
  179. return
  180. }