package client import ( "encoding/base64" "encoding/json" "fmt" "io" "io/ioutil" "os" gosignal "os/signal" "path/filepath" "runtime" "time" "golang.org/x/net/context" "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(ctx context.Context) 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(ctx); 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) registryAuthenticationPrivilegedFunc(index *registrytypes.IndexInfo, cmdName string) types.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, false) if err != nil { return "", err } return encodeAuthToBase64(authConfig) } } func (cli *DockerCli) resizeTty(ctx context.Context, id string, isExec bool) { height, width := cli.getTtySize() cli.resizeTtyTo(ctx, id, height, width, isExec) } func (cli *DockerCli) resizeTtyTo(ctx context.Context, id string, height, width int, isExec bool) { if height == 0 && width == 0 { return } options := types.ResizeOptions{ Height: height, Width: width, } var err error if isExec { err = cli.client.ContainerExecResize(ctx, id, options) } else { err = cli.client.ContainerResize(ctx, id, 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 (cli *DockerCli) getExitCode(ctx context.Context, containerID string) (bool, int, error) { c, err := cli.client.ContainerInspect(ctx, 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 (cli *DockerCli) getExecExitCode(ctx context.Context, execID string) (bool, int, error) { resp, err := cli.client.ContainerExecInspect(ctx, 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(ctx context.Context, id string, isExec bool) error { cli.resizeTty(ctx, 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(ctx, 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(ctx, 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) } func copyToFile(outfile string, r io.Reader) error { tmpFile, err := ioutil.TempFile(filepath.Dir(outfile), ".docker_temp_") if err != nil { return err } tmpPath := tmpFile.Name() _, err = io.Copy(tmpFile, r) tmpFile.Close() if err != nil { os.Remove(tmpPath) return err } if err = os.Rename(tmpPath, outfile); err != nil { os.Remove(tmpPath) return err } return nil } // 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(ctx context.Context, index *registrytypes.IndexInfo) types.AuthConfig { configKey := index.Name if index.Official { configKey = cli.electAuthServer(ctx) } a, _ := getCredentials(cli.configFile, configKey) return a } func (cli *DockerCli) retrieveAuthConfigs() map[string]types.AuthConfig { acs, _ := getAllCredentials(cli.configFile) return acs }