| 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() 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(context.Background()); 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) 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, false) |
| if err != nil { |
| return "", err |
| } |
| return encodeAuthToBase64(authConfig) |
| } |
| } |
| |
| func (cli *DockerCli) resizeTty(id string, isExec bool) { |
| height, width := cli.getTtySize() |
| cli.resizeTtyTo(id, height, width, isExec) |
| } |
| |
| func (cli *DockerCli) resizeTtyTo(id string, height, width int, isExec bool) { |
| if height == 0 && width == 0 { |
| return |
| } |
| |
| options := types.ResizeOptions{ |
| ID: id, |
| Height: height, |
| Width: width, |
| } |
| |
| var err error |
| if isExec { |
| err = cli.client.ContainerExecResize(context.Background(), options) |
| } else { |
| err = cli.client.ContainerResize(context.Background(), 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(context.Background(), 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(context.Background(), 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) |
| } |
| |
| 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(index *registrytypes.IndexInfo) types.AuthConfig { |
| configKey := index.Name |
| if index.Official { |
| configKey = cli.electAuthServer() |
| } |
| |
| a, _ := getCredentials(cli.configFile, configKey) |
| return a |
| } |
| |
| func (cli *DockerCli) retrieveAuthConfigs() map[string]types.AuthConfig { |
| acs, _ := getAllCredentials(cli.configFile) |
| return acs |
| } |