utils.go 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240
  1. package client
  2. import (
  3. "encoding/base64"
  4. "encoding/json"
  5. "fmt"
  6. "io"
  7. "io/ioutil"
  8. "os"
  9. gosignal "os/signal"
  10. "path/filepath"
  11. "runtime"
  12. "time"
  13. "golang.org/x/net/context"
  14. "github.com/Sirupsen/logrus"
  15. "github.com/docker/docker/pkg/signal"
  16. "github.com/docker/docker/pkg/term"
  17. "github.com/docker/docker/registry"
  18. "github.com/docker/engine-api/client"
  19. "github.com/docker/engine-api/types"
  20. registrytypes "github.com/docker/engine-api/types/registry"
  21. )
  22. func (cli *DockerCli) electAuthServer(ctx context.Context) string {
  23. // The daemon `/info` endpoint informs us of the default registry being
  24. // used. This is essential in cross-platforms environment, where for
  25. // example a Linux client might be interacting with a Windows daemon, hence
  26. // the default registry URL might be Windows specific.
  27. serverAddress := registry.IndexServer
  28. if info, err := cli.client.Info(ctx); err != nil {
  29. fmt.Fprintf(cli.out, "Warning: failed to get default registry endpoint from daemon (%v). Using system default: %s\n", err, serverAddress)
  30. } else {
  31. serverAddress = info.IndexServerAddress
  32. }
  33. return serverAddress
  34. }
  35. // EncodeAuthToBase64 serializes the auth configuration as JSON base64 payload
  36. func EncodeAuthToBase64(authConfig types.AuthConfig) (string, error) {
  37. buf, err := json.Marshal(authConfig)
  38. if err != nil {
  39. return "", err
  40. }
  41. return base64.URLEncoding.EncodeToString(buf), nil
  42. }
  43. // RegistryAuthenticationPrivilegedFunc return a RequestPrivilegeFunc from the specified registry index info
  44. // for the given command.
  45. func (cli *DockerCli) RegistryAuthenticationPrivilegedFunc(index *registrytypes.IndexInfo, cmdName string) types.RequestPrivilegeFunc {
  46. return func() (string, error) {
  47. fmt.Fprintf(cli.out, "\nPlease login prior to %s:\n", cmdName)
  48. indexServer := registry.GetAuthConfigKey(index)
  49. authConfig, err := cli.configureAuth("", "", indexServer, false)
  50. if err != nil {
  51. return "", err
  52. }
  53. return EncodeAuthToBase64(authConfig)
  54. }
  55. }
  56. func (cli *DockerCli) resizeTty(ctx context.Context, id string, isExec bool) {
  57. height, width := cli.GetTtySize()
  58. cli.ResizeTtyTo(ctx, id, height, width, isExec)
  59. }
  60. // ResizeTtyTo resizes tty to specific height and width
  61. // TODO: this can be unexported again once all container related commands move to package container
  62. func (cli *DockerCli) ResizeTtyTo(ctx context.Context, id string, height, width int, isExec bool) {
  63. if height == 0 && width == 0 {
  64. return
  65. }
  66. options := types.ResizeOptions{
  67. Height: height,
  68. Width: width,
  69. }
  70. var err error
  71. if isExec {
  72. err = cli.client.ContainerExecResize(ctx, id, options)
  73. } else {
  74. err = cli.client.ContainerResize(ctx, id, options)
  75. }
  76. if err != nil {
  77. logrus.Debugf("Error resize: %s", err)
  78. }
  79. }
  80. // GetExitCode perform an inspect on the container. It returns
  81. // the running state and the exit code.
  82. func (cli *DockerCli) GetExitCode(ctx context.Context, containerID string) (bool, int, error) {
  83. c, err := cli.client.ContainerInspect(ctx, containerID)
  84. if err != nil {
  85. // If we can't connect, then the daemon probably died.
  86. if err != client.ErrConnectionFailed {
  87. return false, -1, err
  88. }
  89. return false, -1, nil
  90. }
  91. return c.State.Running, c.State.ExitCode, nil
  92. }
  93. // getExecExitCode perform an inspect on the exec command. It returns
  94. // the running state and the exit code.
  95. func (cli *DockerCli) getExecExitCode(ctx context.Context, execID string) (bool, int, error) {
  96. resp, err := cli.client.ContainerExecInspect(ctx, execID)
  97. if err != nil {
  98. // If we can't connect, then the daemon probably died.
  99. if err != client.ErrConnectionFailed {
  100. return false, -1, err
  101. }
  102. return false, -1, nil
  103. }
  104. return resp.Running, resp.ExitCode, nil
  105. }
  106. // MonitorTtySize updates the container tty size when the terminal tty changes size
  107. func (cli *DockerCli) MonitorTtySize(ctx context.Context, id string, isExec bool) error {
  108. cli.resizeTty(ctx, id, isExec)
  109. if runtime.GOOS == "windows" {
  110. go func() {
  111. prevH, prevW := cli.GetTtySize()
  112. for {
  113. time.Sleep(time.Millisecond * 250)
  114. h, w := cli.GetTtySize()
  115. if prevW != w || prevH != h {
  116. cli.resizeTty(ctx, id, isExec)
  117. }
  118. prevH = h
  119. prevW = w
  120. }
  121. }()
  122. } else {
  123. sigchan := make(chan os.Signal, 1)
  124. gosignal.Notify(sigchan, signal.SIGWINCH)
  125. go func() {
  126. for range sigchan {
  127. cli.resizeTty(ctx, id, isExec)
  128. }
  129. }()
  130. }
  131. return nil
  132. }
  133. // GetTtySize returns the height and width in characters of the tty
  134. func (cli *DockerCli) GetTtySize() (int, int) {
  135. if !cli.isTerminalOut {
  136. return 0, 0
  137. }
  138. ws, err := term.GetWinsize(cli.outFd)
  139. if err != nil {
  140. logrus.Debugf("Error getting size: %s", err)
  141. if ws == nil {
  142. return 0, 0
  143. }
  144. }
  145. return int(ws.Height), int(ws.Width)
  146. }
  147. // CopyToFile writes the content of the reader to the specifed file
  148. func CopyToFile(outfile string, r io.Reader) error {
  149. tmpFile, err := ioutil.TempFile(filepath.Dir(outfile), ".docker_temp_")
  150. if err != nil {
  151. return err
  152. }
  153. tmpPath := tmpFile.Name()
  154. _, err = io.Copy(tmpFile, r)
  155. tmpFile.Close()
  156. if err != nil {
  157. os.Remove(tmpPath)
  158. return err
  159. }
  160. if err = os.Rename(tmpPath, outfile); err != nil {
  161. os.Remove(tmpPath)
  162. return err
  163. }
  164. return nil
  165. }
  166. // ResolveAuthConfig is like registry.ResolveAuthConfig, but if using the
  167. // default index, it uses the default index name for the daemon's platform,
  168. // not the client's platform.
  169. func (cli *DockerCli) ResolveAuthConfig(ctx context.Context, index *registrytypes.IndexInfo) types.AuthConfig {
  170. configKey := index.Name
  171. if index.Official {
  172. configKey = cli.electAuthServer(ctx)
  173. }
  174. a, _ := getCredentials(cli.configFile, configKey)
  175. return a
  176. }
  177. // RetrieveAuthConfigs return all credentials.
  178. func (cli *DockerCli) RetrieveAuthConfigs() map[string]types.AuthConfig {
  179. acs, _ := getAllCredentials(cli.configFile)
  180. return acs
  181. }
  182. // ForwardAllSignals forwards signals to the contianer
  183. // TODO: this can be unexported again once all container commands are under
  184. // api/client/container
  185. func (cli *DockerCli) ForwardAllSignals(ctx context.Context, cid string) chan os.Signal {
  186. sigc := make(chan os.Signal, 128)
  187. signal.CatchAll(sigc)
  188. go func() {
  189. for s := range sigc {
  190. if s == signal.SIGCHLD || s == signal.SIGPIPE {
  191. continue
  192. }
  193. var sig string
  194. for sigStr, sigN := range signal.SignalMap {
  195. if sigN == s {
  196. sig = sigStr
  197. break
  198. }
  199. }
  200. if sig == "" {
  201. fmt.Fprintf(cli.err, "Unsupported signal: %v. Discarding.\n", s)
  202. continue
  203. }
  204. if err := cli.client.ContainerKill(ctx, cid, sig); err != nil {
  205. logrus.Debugf("Error sending signal: %s", err)
  206. }
  207. }
  208. }()
  209. return sigc
  210. }