utils.go 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222
  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. "strings"
  13. "time"
  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() 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(); 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. func (cli *DockerCli) registryAuthenticationPrivilegedFunc(index *registrytypes.IndexInfo, cmdName string) client.RequestPrivilegeFunc {
  44. return func() (string, error) {
  45. fmt.Fprintf(cli.out, "\nPlease login prior to %s:\n", cmdName)
  46. indexServer := registry.GetAuthConfigKey(index)
  47. authConfig, err := cli.configureAuth("", "", "", indexServer)
  48. if err != nil {
  49. return "", err
  50. }
  51. return encodeAuthToBase64(authConfig)
  52. }
  53. }
  54. func (cli *DockerCli) resizeTty(id string, isExec bool) {
  55. height, width := cli.getTtySize()
  56. cli.resizeTtyTo(id, height, width, isExec)
  57. }
  58. func (cli *DockerCli) resizeTtyTo(id string, height, width int, isExec bool) {
  59. if height == 0 && width == 0 {
  60. return
  61. }
  62. options := types.ResizeOptions{
  63. ID: id,
  64. Height: height,
  65. Width: width,
  66. }
  67. var err error
  68. if isExec {
  69. err = cli.client.ContainerExecResize(options)
  70. } else {
  71. err = cli.client.ContainerResize(options)
  72. }
  73. if err != nil {
  74. logrus.Debugf("Error resize: %s", err)
  75. }
  76. }
  77. // getExitCode perform an inspect on the container. It returns
  78. // the running state and the exit code.
  79. func getExitCode(cli *DockerCli, containerID string) (bool, int, error) {
  80. c, err := cli.client.ContainerInspect(containerID)
  81. if err != nil {
  82. // If we can't connect, then the daemon probably died.
  83. if err != client.ErrConnectionFailed {
  84. return false, -1, err
  85. }
  86. return false, -1, nil
  87. }
  88. return c.State.Running, c.State.ExitCode, nil
  89. }
  90. // getExecExitCode perform an inspect on the exec command. It returns
  91. // the running state and the exit code.
  92. func getExecExitCode(cli *DockerCli, execID string) (bool, int, error) {
  93. resp, err := cli.client.ContainerExecInspect(execID)
  94. if err != nil {
  95. // If we can't connect, then the daemon probably died.
  96. if err != client.ErrConnectionFailed {
  97. return false, -1, err
  98. }
  99. return false, -1, nil
  100. }
  101. return resp.Running, resp.ExitCode, nil
  102. }
  103. func (cli *DockerCli) monitorTtySize(id string, isExec bool) error {
  104. cli.resizeTty(id, isExec)
  105. if runtime.GOOS == "windows" {
  106. go func() {
  107. prevH, prevW := cli.getTtySize()
  108. for {
  109. time.Sleep(time.Millisecond * 250)
  110. h, w := cli.getTtySize()
  111. if prevW != w || prevH != h {
  112. cli.resizeTty(id, isExec)
  113. }
  114. prevH = h
  115. prevW = w
  116. }
  117. }()
  118. } else {
  119. sigchan := make(chan os.Signal, 1)
  120. gosignal.Notify(sigchan, signal.SIGWINCH)
  121. go func() {
  122. for range sigchan {
  123. cli.resizeTty(id, isExec)
  124. }
  125. }()
  126. }
  127. return nil
  128. }
  129. func (cli *DockerCli) getTtySize() (int, int) {
  130. if !cli.isTerminalOut {
  131. return 0, 0
  132. }
  133. ws, err := term.GetWinsize(cli.outFd)
  134. if err != nil {
  135. logrus.Debugf("Error getting size: %s", err)
  136. if ws == nil {
  137. return 0, 0
  138. }
  139. }
  140. return int(ws.Height), int(ws.Width)
  141. }
  142. func copyToFile(outfile string, r io.Reader) error {
  143. tmpFile, err := ioutil.TempFile(filepath.Dir(outfile), ".docker_temp_")
  144. if err != nil {
  145. return err
  146. }
  147. tmpPath := tmpFile.Name()
  148. _, err = io.Copy(tmpFile, r)
  149. tmpFile.Close()
  150. if err != nil {
  151. os.Remove(tmpPath)
  152. return err
  153. }
  154. if err = os.Rename(tmpPath, outfile); err != nil {
  155. os.Remove(tmpPath)
  156. return err
  157. }
  158. return nil
  159. }
  160. // resolveAuthConfig is like registry.ResolveAuthConfig, but if using the
  161. // default index, it uses the default index name for the daemon's platform,
  162. // not the client's platform.
  163. func (cli *DockerCli) resolveAuthConfig(authConfigs map[string]types.AuthConfig, index *registrytypes.IndexInfo) types.AuthConfig {
  164. configKey := index.Name
  165. if index.Official {
  166. configKey = cli.electAuthServer()
  167. }
  168. // First try the happy case
  169. if c, found := authConfigs[configKey]; found || index.Official {
  170. return c
  171. }
  172. convertToHostname := func(url string) string {
  173. stripped := url
  174. if strings.HasPrefix(url, "http://") {
  175. stripped = strings.Replace(url, "http://", "", 1)
  176. } else if strings.HasPrefix(url, "https://") {
  177. stripped = strings.Replace(url, "https://", "", 1)
  178. }
  179. nameParts := strings.SplitN(stripped, "/", 2)
  180. return nameParts[0]
  181. }
  182. // Maybe they have a legacy config file, we will iterate the keys converting
  183. // them to the new format and testing
  184. for registry, ac := range authConfigs {
  185. if configKey == convertToHostname(registry) {
  186. return ac
  187. }
  188. }
  189. // When all else fails, return an empty auth config
  190. return types.AuthConfig{}
  191. }