mirror of
https://github.com/moby/moby.git
synced 2022-11-09 12:21:53 -05:00

This change implements communication with an external credentials store, ala git-credential-helper. The client falls back the plain text store, what we're currently using, if there is no remote store configured. It shells out to helper program when a credential store is configured. Those programs can be implemented with any language as long as they follow the convention to pass arguments and information. There is an implementation for the OS X keychain in https://github.com/calavera/docker-credential-helpers. That package also provides basic structure to create other helpers. Signed-off-by: David Calavera <david.calavera@gmail.com>
195 lines
4.8 KiB
Go
195 lines
4.8 KiB
Go
package client
|
|
|
|
import (
|
|
"encoding/base64"
|
|
"encoding/json"
|
|
"fmt"
|
|
"io"
|
|
"io/ioutil"
|
|
"os"
|
|
gosignal "os/signal"
|
|
"path/filepath"
|
|
"runtime"
|
|
"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) 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()
|
|
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(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)
|
|
}
|
|
|
|
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
|
|
}
|