mirror of
https://github.com/moby/moby.git
synced 2022-11-09 12:21:53 -05:00
4c7f0d268f
Make better default usage on context.Context on the `api/client` package to share the context (it is useless if not shared, which was the case for a lot of commands). Signed-off-by: Vincent Demeester <vincent@sbr.pm>
201 lines
5.1 KiB
Go
201 lines
5.1 KiB
Go
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
|
|
}
|