2014-03-28 19:21:55 -04:00
|
|
|
package client
|
|
|
|
|
|
|
|
import (
|
2015-12-11 23:11:42 -05:00
|
|
|
"encoding/base64"
|
|
|
|
"encoding/json"
|
2014-03-28 19:21:55 -04:00
|
|
|
"fmt"
|
2015-12-13 20:25:28 -05:00
|
|
|
"io"
|
|
|
|
"io/ioutil"
|
2014-03-28 19:21:55 -04:00
|
|
|
"os"
|
|
|
|
gosignal "os/signal"
|
2015-12-13 20:25:28 -05:00
|
|
|
"path/filepath"
|
2015-03-26 18:52:16 -04:00
|
|
|
"runtime"
|
|
|
|
"time"
|
2014-03-28 19:21:55 -04:00
|
|
|
|
2016-03-16 15:19:22 -04:00
|
|
|
"golang.org/x/net/context"
|
|
|
|
|
2015-03-26 18:22:04 -04:00
|
|
|
"github.com/Sirupsen/logrus"
|
2014-11-13 13:40:22 -05:00
|
|
|
"github.com/docker/docker/pkg/signal"
|
2014-07-24 18:19:50 -04:00
|
|
|
"github.com/docker/docker/pkg/term"
|
|
|
|
"github.com/docker/docker/registry"
|
2016-01-04 19:05:26 -05:00
|
|
|
"github.com/docker/engine-api/client"
|
|
|
|
"github.com/docker/engine-api/types"
|
|
|
|
registrytypes "github.com/docker/engine-api/types/registry"
|
2014-03-28 19:21:55 -04:00
|
|
|
)
|
|
|
|
|
2016-05-21 09:57:57 -04:00
|
|
|
func (cli *DockerCli) electAuthServer(ctx context.Context) string {
|
2016-02-03 13:30:17 -05:00
|
|
|
// 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
|
2016-05-21 09:57:57 -04:00
|
|
|
if info, err := cli.client.Info(ctx); err != nil {
|
2016-02-03 13:30:17 -05:00
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2016-06-03 13:50:01 -04:00
|
|
|
// EncodeAuthToBase64 serializes the auth configuration as JSON base64 payload
|
|
|
|
func EncodeAuthToBase64(authConfig types.AuthConfig) (string, error) {
|
2015-12-11 23:11:42 -05:00
|
|
|
buf, err := json.Marshal(authConfig)
|
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
return base64.URLEncoding.EncodeToString(buf), nil
|
|
|
|
}
|
|
|
|
|
2016-06-03 13:50:01 -04:00
|
|
|
// RegistryAuthenticationPrivilegedFunc return a RequestPrivilegeFunc from the specified registry index info
|
|
|
|
// for the given command.
|
|
|
|
func (cli *DockerCli) RegistryAuthenticationPrivilegedFunc(index *registrytypes.IndexInfo, cmdName string) types.RequestPrivilegeFunc {
|
2015-12-06 15:17:34 -05:00
|
|
|
return func() (string, error) {
|
|
|
|
fmt.Fprintf(cli.out, "\nPlease login prior to %s:\n", cmdName)
|
2016-01-27 18:26:48 -05:00
|
|
|
indexServer := registry.GetAuthConfigKey(index)
|
2016-02-29 20:51:36 -05:00
|
|
|
authConfig, err := cli.configureAuth("", "", indexServer, false)
|
2016-01-27 18:26:48 -05:00
|
|
|
if err != nil {
|
2015-12-06 15:17:34 -05:00
|
|
|
return "", err
|
|
|
|
}
|
2016-06-03 13:50:01 -04:00
|
|
|
return EncodeAuthToBase64(authConfig)
|
2015-12-06 15:17:34 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-05-21 09:57:57 -04:00
|
|
|
func (cli *DockerCli) resizeTty(ctx context.Context, id string, isExec bool) {
|
2016-05-31 19:49:32 -04:00
|
|
|
height, width := cli.GetTtySize()
|
2016-06-05 12:17:39 -04:00
|
|
|
cli.ResizeTtyTo(ctx, id, height, width, isExec)
|
2016-01-22 06:35:55 -05:00
|
|
|
}
|
|
|
|
|
2016-06-05 12:17:39 -04:00
|
|
|
// ResizeTtyTo resizes tty to specific height and width
|
|
|
|
// TODO: this can be unexported again once all container related commands move to package container
|
|
|
|
func (cli *DockerCli) ResizeTtyTo(ctx context.Context, id string, height, width int, isExec bool) {
|
2014-03-28 19:21:55 -04:00
|
|
|
if height == 0 && width == 0 {
|
|
|
|
return
|
|
|
|
}
|
2014-09-16 02:43:43 -04:00
|
|
|
|
2015-12-06 18:41:57 -05:00
|
|
|
options := types.ResizeOptions{
|
|
|
|
Height: height,
|
|
|
|
Width: width,
|
|
|
|
}
|
|
|
|
|
|
|
|
var err error
|
2015-12-09 22:55:01 -05:00
|
|
|
if isExec {
|
2016-05-21 09:57:57 -04:00
|
|
|
err = cli.client.ContainerExecResize(ctx, id, options)
|
2014-09-16 02:43:43 -04:00
|
|
|
} else {
|
2016-05-21 09:57:57 -04:00
|
|
|
err = cli.client.ContainerResize(ctx, id, options)
|
2014-09-16 02:43:43 -04:00
|
|
|
}
|
|
|
|
|
2015-12-06 18:41:57 -05:00
|
|
|
if err != nil {
|
2015-03-26 18:22:04 -04:00
|
|
|
logrus.Debugf("Error resize: %s", err)
|
2014-03-28 19:21:55 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-05-31 19:49:32 -04:00
|
|
|
// GetExitCode perform an inspect on the container. It returns
|
2014-03-28 19:21:55 -04:00
|
|
|
// the running state and the exit code.
|
2016-05-31 19:49:32 -04:00
|
|
|
func (cli *DockerCli) GetExitCode(ctx context.Context, containerID string) (bool, int, error) {
|
2016-05-21 09:57:57 -04:00
|
|
|
c, err := cli.client.ContainerInspect(ctx, containerID)
|
2014-03-28 19:21:55 -04:00
|
|
|
if err != nil {
|
|
|
|
// If we can't connect, then the daemon probably died.
|
2016-01-04 19:05:26 -05:00
|
|
|
if err != client.ErrConnectionFailed {
|
2014-03-28 19:21:55 -04:00
|
|
|
return false, -1, err
|
|
|
|
}
|
|
|
|
return false, -1, nil
|
|
|
|
}
|
2014-06-04 18:11:11 -04:00
|
|
|
|
2015-04-13 10:17:14 -04:00
|
|
|
return c.State.Running, c.State.ExitCode, nil
|
2014-03-28 19:21:55 -04:00
|
|
|
}
|
|
|
|
|
2014-11-17 18:50:09 -05:00
|
|
|
// getExecExitCode perform an inspect on the exec command. It returns
|
|
|
|
// the running state and the exit code.
|
2016-05-21 09:57:57 -04:00
|
|
|
func (cli *DockerCli) getExecExitCode(ctx context.Context, execID string) (bool, int, error) {
|
|
|
|
resp, err := cli.client.ContainerExecInspect(ctx, execID)
|
2014-11-17 18:50:09 -05:00
|
|
|
if err != nil {
|
|
|
|
// If we can't connect, then the daemon probably died.
|
2016-01-04 19:05:26 -05:00
|
|
|
if err != client.ErrConnectionFailed {
|
2014-11-17 18:50:09 -05:00
|
|
|
return false, -1, err
|
|
|
|
}
|
|
|
|
return false, -1, nil
|
|
|
|
}
|
|
|
|
|
2015-12-06 00:33:38 -05:00
|
|
|
return resp.Running, resp.ExitCode, nil
|
2014-11-17 18:50:09 -05:00
|
|
|
}
|
|
|
|
|
2016-05-31 19:49:32 -04:00
|
|
|
// MonitorTtySize updates the container tty size when the terminal tty changes size
|
|
|
|
func (cli *DockerCli) MonitorTtySize(ctx context.Context, id string, isExec bool) error {
|
2016-05-21 09:57:57 -04:00
|
|
|
cli.resizeTty(ctx, id, isExec)
|
2014-03-28 19:21:55 -04:00
|
|
|
|
2015-03-26 18:52:16 -04:00
|
|
|
if runtime.GOOS == "windows" {
|
|
|
|
go func() {
|
2016-05-31 19:49:32 -04:00
|
|
|
prevH, prevW := cli.GetTtySize()
|
2015-03-26 18:52:16 -04:00
|
|
|
for {
|
|
|
|
time.Sleep(time.Millisecond * 250)
|
2016-05-31 19:49:32 -04:00
|
|
|
h, w := cli.GetTtySize()
|
2015-03-26 18:52:16 -04:00
|
|
|
|
|
|
|
if prevW != w || prevH != h {
|
2016-05-21 09:57:57 -04:00
|
|
|
cli.resizeTty(ctx, id, isExec)
|
2015-03-26 18:52:16 -04:00
|
|
|
}
|
|
|
|
prevH = h
|
2015-04-06 17:31:42 -04:00
|
|
|
prevW = w
|
2015-03-26 18:52:16 -04:00
|
|
|
}
|
|
|
|
}()
|
|
|
|
} else {
|
|
|
|
sigchan := make(chan os.Signal, 1)
|
|
|
|
gosignal.Notify(sigchan, signal.SIGWINCH)
|
|
|
|
go func() {
|
2015-04-01 18:39:37 -04:00
|
|
|
for range sigchan {
|
2016-05-21 09:57:57 -04:00
|
|
|
cli.resizeTty(ctx, id, isExec)
|
2015-03-26 18:52:16 -04:00
|
|
|
}
|
|
|
|
}()
|
|
|
|
}
|
2014-03-28 19:21:55 -04:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2016-05-31 19:49:32 -04:00
|
|
|
// GetTtySize returns the height and width in characters of the tty
|
|
|
|
func (cli *DockerCli) GetTtySize() (int, int) {
|
2014-09-10 09:35:48 -04:00
|
|
|
if !cli.isTerminalOut {
|
2014-03-28 19:21:55 -04:00
|
|
|
return 0, 0
|
|
|
|
}
|
2014-09-10 09:35:48 -04:00
|
|
|
ws, err := term.GetWinsize(cli.outFd)
|
2014-03-28 19:21:55 -04:00
|
|
|
if err != nil {
|
2015-03-26 18:22:04 -04:00
|
|
|
logrus.Debugf("Error getting size: %s", err)
|
2014-03-28 19:21:55 -04:00
|
|
|
if ws == nil {
|
|
|
|
return 0, 0
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return int(ws.Height), int(ws.Width)
|
|
|
|
}
|
2015-12-13 20:25:28 -05:00
|
|
|
|
2016-06-05 10:42:19 -04:00
|
|
|
// CopyToFile writes the content of the reader to the specifed file
|
|
|
|
func CopyToFile(outfile string, r io.Reader) error {
|
2015-12-13 20:25:28 -05:00
|
|
|
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
|
|
|
|
}
|
2016-02-03 13:55:33 -05:00
|
|
|
|
2016-06-03 13:50:01 -04:00
|
|
|
// ResolveAuthConfig is like registry.ResolveAuthConfig, but if using the
|
2016-02-03 13:55:33 -05:00
|
|
|
// default index, it uses the default index name for the daemon's platform,
|
|
|
|
// not the client's platform.
|
2016-06-03 13:50:01 -04:00
|
|
|
func (cli *DockerCli) ResolveAuthConfig(ctx context.Context, index *registrytypes.IndexInfo) types.AuthConfig {
|
2016-02-03 13:55:33 -05:00
|
|
|
configKey := index.Name
|
|
|
|
if index.Official {
|
2016-05-21 09:57:57 -04:00
|
|
|
configKey = cli.electAuthServer(ctx)
|
2016-02-03 13:55:33 -05:00
|
|
|
}
|
|
|
|
|
2016-02-07 19:55:17 -05:00
|
|
|
a, _ := getCredentials(cli.configFile, configKey)
|
|
|
|
return a
|
2016-02-03 13:55:33 -05:00
|
|
|
}
|
2016-03-01 12:04:35 -05:00
|
|
|
|
2016-06-07 12:15:44 -04:00
|
|
|
// RetrieveAuthConfigs return all credentials.
|
|
|
|
func (cli *DockerCli) RetrieveAuthConfigs() map[string]types.AuthConfig {
|
2016-03-01 12:04:35 -05:00
|
|
|
acs, _ := getAllCredentials(cli.configFile)
|
|
|
|
return acs
|
|
|
|
}
|
2016-06-05 11:03:58 -04:00
|
|
|
|
|
|
|
// ForwardAllSignals forwards signals to the contianer
|
|
|
|
// TODO: this can be unexported again once all container commands are under
|
|
|
|
// api/client/container
|
|
|
|
func (cli *DockerCli) ForwardAllSignals(ctx context.Context, cid string) chan os.Signal {
|
|
|
|
sigc := make(chan os.Signal, 128)
|
|
|
|
signal.CatchAll(sigc)
|
|
|
|
go func() {
|
|
|
|
for s := range sigc {
|
|
|
|
if s == signal.SIGCHLD || s == signal.SIGPIPE {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
var sig string
|
|
|
|
for sigStr, sigN := range signal.SignalMap {
|
|
|
|
if sigN == s {
|
|
|
|
sig = sigStr
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if sig == "" {
|
|
|
|
fmt.Fprintf(cli.err, "Unsupported signal: %v. Discarding.\n", s)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := cli.client.ContainerKill(ctx, cid, sig); err != nil {
|
|
|
|
logrus.Debugf("Error sending signal: %s", err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
return sigc
|
|
|
|
}
|