| package client |
| |
| import ( |
| "encoding/base64" |
| "encoding/json" |
| "fmt" |
| "os" |
| gosignal "os/signal" |
| "runtime" |
| "strings" |
| "time" |
| |
| "github.com/Sirupsen/logrus" |
| "github.com/docker/docker/pkg/signal" |
| "github.com/docker/docker/pkg/term" |
| "github.com/docker/docker/registry" |
| "github.com/docker/engine-api/client" |
| "github.com/docker/engine-api/types" |
| registrytypes "github.com/docker/engine-api/types/registry" |
| ) |
| |
| func (cli *DockerCli) electAuthServer() string { |
| // The daemon `/info` endpoint informs us of the default registry being |
| // used. This is essential in cross-platforms environment, where for |
| // example a Linux client might be interacting with a Windows daemon, hence |
| // the default registry URL might be Windows specific. |
| serverAddress := registry.IndexServer |
| if info, err := cli.client.Info(); err != nil { |
| fmt.Fprintf(cli.out, "Warning: failed to get default registry endpoint from daemon (%v). Using system default: %s\n", err, serverAddress) |
| } else { |
| serverAddress = info.IndexServerAddress |
| } |
| return serverAddress |
| } |
| |
| // encodeAuthToBase64 serializes the auth configuration as JSON base64 payload |
| func encodeAuthToBase64(authConfig types.AuthConfig) (string, error) { |
| buf, err := json.Marshal(authConfig) |
| if err != nil { |
| return "", err |
| } |
| return base64.URLEncoding.EncodeToString(buf), nil |
| } |
| |
| func (cli *DockerCli) encodeRegistryAuth(index *registrytypes.IndexInfo) (string, error) { |
| authConfig := registry.ResolveAuthConfig(cli.configFile.AuthConfigs, index) |
| return encodeAuthToBase64(authConfig) |
| } |
| |
| func (cli *DockerCli) registryAuthenticationPrivilegedFunc(index *registrytypes.IndexInfo, cmdName string) client.RequestPrivilegeFunc { |
| return func() (string, error) { |
| fmt.Fprintf(cli.out, "\nPlease login prior to %s:\n", cmdName) |
| indexServer := registry.GetAuthConfigKey(index) |
| authConfig, err := cli.configureAuth("", "", "", indexServer) |
| if err != nil { |
| return "", err |
| } |
| return encodeAuthToBase64(authConfig) |
| } |
| } |
| |
| func (cli *DockerCli) resizeTty(id string, isExec bool) { |
| height, width := cli.getTtySize() |
| if height == 0 && width == 0 { |
| return |
| } |
| |
| options := types.ResizeOptions{ |
| ID: id, |
| Height: height, |
| Width: width, |
| } |
| |
| var err error |
| if isExec { |
| err = cli.client.ContainerExecResize(options) |
| } else { |
| err = cli.client.ContainerResize(options) |
| } |
| |
| if err != nil { |
| logrus.Debugf("Error resize: %s", err) |
| } |
| } |
| |
| // getExitCode perform an inspect on the container. It returns |
| // the running state and the exit code. |
| func getExitCode(cli *DockerCli, containerID string) (bool, int, error) { |
| c, err := cli.client.ContainerInspect(containerID) |
| if err != nil { |
| // If we can't connect, then the daemon probably died. |
| if err != client.ErrConnectionFailed { |
| return false, -1, err |
| } |
| return false, -1, nil |
| } |
| |
| return c.State.Running, c.State.ExitCode, nil |
| } |
| |
| // getExecExitCode perform an inspect on the exec command. It returns |
| // the running state and the exit code. |
| func getExecExitCode(cli *DockerCli, execID string) (bool, int, error) { |
| resp, err := cli.client.ContainerExecInspect(execID) |
| if err != nil { |
| // If we can't connect, then the daemon probably died. |
| if err != client.ErrConnectionFailed { |
| return false, -1, err |
| } |
| return false, -1, nil |
| } |
| |
| return resp.Running, resp.ExitCode, nil |
| } |
| |
| func (cli *DockerCli) monitorTtySize(id string, isExec bool) error { |
| cli.resizeTty(id, isExec) |
| |
| if runtime.GOOS == "windows" { |
| go func() { |
| prevH, prevW := cli.getTtySize() |
| for { |
| time.Sleep(time.Millisecond * 250) |
| h, w := cli.getTtySize() |
| |
| if prevW != w || prevH != h { |
| cli.resizeTty(id, isExec) |
| } |
| prevH = h |
| prevW = w |
| } |
| }() |
| } else { |
| sigchan := make(chan os.Signal, 1) |
| gosignal.Notify(sigchan, signal.SIGWINCH) |
| go func() { |
| for range sigchan { |
| cli.resizeTty(id, isExec) |
| } |
| }() |
| } |
| return nil |
| } |
| |
| func (cli *DockerCli) getTtySize() (int, int) { |
| if !cli.isTerminalOut { |
| return 0, 0 |
| } |
| ws, err := term.GetWinsize(cli.outFd) |
| if err != nil { |
| logrus.Debugf("Error getting size: %s", err) |
| if ws == nil { |
| return 0, 0 |
| } |
| } |
| return int(ws.Height), int(ws.Width) |
| } |
| |
| // resolveAuthConfig is like registry.ResolveAuthConfig, but if using the |
| // default index, it uses the default index name for the daemon's platform, |
| // not the client's platform. |
| func (cli *DockerCli) resolveAuthConfig(authConfigs map[string]types.AuthConfig, index *registrytypes.IndexInfo) types.AuthConfig { |
| configKey := index.Name |
| if index.Official { |
| configKey = cli.electAuthServer() |
| } |
| |
| // First try the happy case |
| if c, found := authConfigs[configKey]; found || index.Official { |
| return c |
| } |
| |
| convertToHostname := func(url string) string { |
| stripped := url |
| if strings.HasPrefix(url, "http://") { |
| stripped = strings.Replace(url, "http://", "", 1) |
| } else if strings.HasPrefix(url, "https://") { |
| stripped = strings.Replace(url, "https://", "", 1) |
| } |
| |
| nameParts := strings.SplitN(stripped, "/", 2) |
| |
| return nameParts[0] |
| } |
| |
| // Maybe they have a legacy config file, we will iterate the keys converting |
| // them to the new format and testing |
| for registry, ac := range authConfigs { |
| if configKey == convertToHostname(registry) { |
| return ac |
| } |
| } |
| |
| // When all else fails, return an empty auth config |
| return types.AuthConfig{} |
| } |