blob: a92f3bf9518bf1d6722574e1971af48e29963267 [file] [log] [blame]
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{}
}