From d5618bac8c3e4a4868f47f2a4124735aab6afeaf Mon Sep 17 00:00:00 2001 From: Akihiro Suda Date: Mon, 19 Feb 2018 18:31:29 +0900 Subject: [PATCH] support connection helpers (e.g. docker -H ssh://me@server) Signed-off-by: Akihiro Suda --- cli/command/cli.go | 43 +++-- cli/config/configfile/file.go | 1 + cli/config/connhelper/connhelper.go | 159 ++++++++++++++++++ cli/flags/common.go | 3 +- contrib/connhelpers/README.md | 34 ++++ .../docker-connection-dind/main.go | 57 +++++++ .../connhelpers/docker-connection-ssh/main.go | 67 ++++++++ docs/reference/commandline/cli.md | 32 ++++ vendor.conf | 3 +- .../github.com/docker/docker/client/client.go | 39 ++++- .../github.com/docker/docker/client/hijack.go | 6 +- .../docker/docker/client/session.go | 2 +- .../docker/pkg/jsonmessage/jsonmessage.go | 34 +++- vendor/github.com/docker/docker/vendor.conf | 8 +- 14 files changed, 451 insertions(+), 37 deletions(-) create mode 100644 cli/config/connhelper/connhelper.go create mode 100644 contrib/connhelpers/README.md create mode 100644 contrib/connhelpers/docker-connection-dind/main.go create mode 100644 contrib/connhelpers/docker-connection-ssh/main.go diff --git a/cli/command/cli.go b/cli/command/cli.go index 55a6c436d449..4c0c07add1cd 100644 --- a/cli/command/cli.go +++ b/cli/command/cli.go @@ -13,6 +13,7 @@ import ( "github.com/docker/cli/cli/config" cliconfig "github.com/docker/cli/cli/config" "github.com/docker/cli/cli/config/configfile" + "github.com/docker/cli/cli/config/connhelper" cliflags "github.com/docker/cli/cli/flags" manifeststore "github.com/docker/cli/cli/manifest/store" registryclient "github.com/docker/cli/cli/registry/client" @@ -251,31 +252,54 @@ func NewDockerCli(in io.ReadCloser, out, err io.Writer, isTrusted bool) *DockerC // NewAPIClientFromFlags creates a new APIClient from command line flags func NewAPIClientFromFlags(opts *cliflags.CommonOptions, configFile *configfile.ConfigFile) (client.APIClient, error) { - host, err := getServerHost(opts.Hosts, opts.TLSOptions) + unparsedHost, err := getUnparsedServerHost(opts.Hosts) if err != nil { return &client.Client{}, err } + var clientOpts []func(*client.Client) error + helper, err := connhelper.GetConnectionHelper(unparsedHost, configFile.ConnectionHelpers, "docker-connection-") + if err != nil { + return &client.Client{}, err + } + if helper == nil { + clientOpts = append(clientOpts, withHTTPClient(opts.TLSOptions)) + host, err := dopts.ParseHost(opts.TLSOptions != nil, unparsedHost) + if err != nil { + return &client.Client{}, err + } + clientOpts = append(clientOpts, client.WithHost(host)) + } else { + clientOpts = append(clientOpts, func(c *client.Client) error { + httpClient := &http.Client{ + // No tls + // No proxy + Transport: &http.Transport{ + DialContext: helper.Dialer, + }, + } + return client.WithHTTPClient(httpClient)(c) + }) + clientOpts = append(clientOpts, client.WithHost(helper.DummyHost)) + clientOpts = append(clientOpts, client.WithHijackDialer(helper.Dialer)) + } customHeaders := configFile.HTTPHeaders if customHeaders == nil { customHeaders = map[string]string{} } customHeaders["User-Agent"] = UserAgent() + clientOpts = append(clientOpts, client.WithHTTPHeaders(customHeaders)) verStr := api.DefaultVersion if tmpStr := os.Getenv("DOCKER_API_VERSION"); tmpStr != "" { verStr = tmpStr } + clientOpts = append(clientOpts, client.WithVersion(verStr)) - return client.NewClientWithOpts( - withHTTPClient(opts.TLSOptions), - client.WithHTTPHeaders(customHeaders), - client.WithVersion(verStr), - client.WithHost(host), - ) + return client.NewClientWithOpts(clientOpts...) } -func getServerHost(hosts []string, tlsOptions *tlsconfig.Options) (string, error) { +func getUnparsedServerHost(hosts []string) (string, error) { var host string switch len(hosts) { case 0: @@ -285,8 +309,7 @@ func getServerHost(hosts []string, tlsOptions *tlsconfig.Options) (string, error default: return "", errors.New("Please specify only one -H") } - - return dopts.ParseHost(tlsOptions != nil, host) + return host, nil } func withHTTPClient(tlsOpts *tlsconfig.Options) func(*client.Client) error { diff --git a/cli/config/configfile/file.go b/cli/config/configfile/file.go index 9338bc4e667c..4cbe91845915 100644 --- a/cli/config/configfile/file.go +++ b/cli/config/configfile/file.go @@ -46,6 +46,7 @@ type ConfigFile struct { Proxies map[string]ProxyConfig `json:"proxies,omitempty"` Experimental string `json:"experimental,omitempty"` Orchestrator string `json:"orchestrator,omitempty"` + ConnectionHelpers map[string]string `json:"connHelpers,omitempty"` } // ProxyConfig contains proxy configuration settings diff --git a/cli/config/connhelper/connhelper.go b/cli/config/connhelper/connhelper.go new file mode 100644 index 000000000000..cfe43988a02a --- /dev/null +++ b/cli/config/connhelper/connhelper.go @@ -0,0 +1,159 @@ +// Package connhelper provides connection helpers. +// ConnectionHelper allows to connect to a remote host with custom stream provider binary. +// +// convention: +// * filename MUST be `docker-connection-%s` +// * called with args: {"connect", url} +// * stderr can be used for logging purpose +package connhelper + +import ( + "context" + "io" + "net" + "net/url" + "os/exec" + "path/filepath" + "strings" + "time" + + "github.com/pkg/errors" + "github.com/sirupsen/logrus" +) + +// ConnectionHelper allows to connect to a remote host with custom stream provider binary. +type ConnectionHelper struct { + Dialer func(ctx context.Context, network, addr string) (net.Conn, error) + DummyHost string // dummy URL used for HTTP requests. e.g. "http://docker" +} + +// GetConnectionHelper returns nil without error when no helper is registered for the scheme. +// host is like "ssh://me@server01:/var/run/docker.sock". +// cfg is like {"ssh": "ssh"}. +// prefix is like "docker-connection-". +func GetConnectionHelper(host string, cfg map[string]string, prefix string) (*ConnectionHelper, error) { + path, err := lookupConnectionHelperPath(host, cfg, prefix) + if path == "" || err != nil { + return nil, err + } + dialer := func(ctx context.Context, network, addr string) (net.Conn, error) { + return newHelperConn(ctx, path, host, network, addr) + } + return &ConnectionHelper{ + Dialer: dialer, + DummyHost: "tcp://docker", + }, nil +} + +// lookupConnectionHelperPath returns an empty string without error when no helper is registered for the scheme. +func lookupConnectionHelperPath(host string, cfg map[string]string, prefix string) (string, error) { + u, err := url.Parse(host) + if err != nil { + return "", err + } + if u.Scheme == "" { + return "", nil // unregistered + } + helperName := cfg[u.Scheme] + if helperName == "" { + return "", nil // unregistered + } + if strings.Contains(helperName, string(filepath.Separator)) { + return "", errors.Errorf("helper name (e.g. \"ssh\") should not contain path separator: %s", helperName) + } + return exec.LookPath(prefix + helperName) +} + +func newHelperConn(ctx context.Context, helper string, host, dialNetwork, dialAddr string) (net.Conn, error) { + var ( + c helperConn + err error + ) + c.cmd = exec.CommandContext(ctx, helper, "connect", host) + c.stdin, err = c.cmd.StdinPipe() + if err != nil { + return nil, err + } + c.stdout, err = c.cmd.StdoutPipe() + if err != nil { + return nil, err + } + c.cmd.Stderr = &logrusDebugWriter{ + prefix: "helper: ", + } + c.localAddr = dummyAddr{network: dialNetwork, s: "localhost"} + c.remoteAddr = dummyAddr{network: dialNetwork, s: dialAddr} + return &c, c.cmd.Start() +} + +// helperConn implements net.Conn +type helperConn struct { + cmd *exec.Cmd + stdin io.WriteCloser + stdout io.ReadCloser + localAddr net.Addr + remoteAddr net.Addr +} + +func (c *helperConn) Read(p []byte) (int, error) { + return c.stdout.Read(p) +} + +func (c *helperConn) Write(p []byte) (int, error) { + return c.stdin.Write(p) +} + +func (c *helperConn) Close() error { + if err := c.stdin.Close(); err != nil { + logrus.Warnf("error while closing stdin: %v", err) + } + if err := c.stdout.Close(); err != nil { + logrus.Warnf("error while closing stdout: %v", err) + } + if err := c.cmd.Process.Kill(); err != nil { + return err + } + _, err := c.cmd.Process.Wait() + return err +} + +func (c *helperConn) LocalAddr() net.Addr { + return c.localAddr +} +func (c *helperConn) RemoteAddr() net.Addr { + return c.remoteAddr +} +func (c *helperConn) SetDeadline(t time.Time) error { + logrus.Debugf("unimplemented call: SetDeadline(%v)", t) + return nil +} +func (c *helperConn) SetReadDeadline(t time.Time) error { + logrus.Debugf("unimplemented call: SetReadDeadline(%v)", t) + return nil +} +func (c *helperConn) SetWriteDeadline(t time.Time) error { + logrus.Debugf("unimplemented call: SetWriteDeadline(%v)", t) + return nil +} + +type dummyAddr struct { + network string + s string +} + +func (d dummyAddr) Network() string { + return d.network +} + +func (d dummyAddr) String() string { + return d.s +} + +type logrusDebugWriter struct { + prefix string +} + +func (w *logrusDebugWriter) Write(p []byte) (int, error) { + logrus.Debugf("%s%s", w.prefix, string(p)) + return len(p), nil +} diff --git a/cli/flags/common.go b/cli/flags/common.go index 6a9e79078c24..40f947b33d7a 100644 --- a/cli/flags/common.go +++ b/cli/flags/common.go @@ -70,7 +70,8 @@ func (commonOpts *CommonOptions) InstallFlags(flags *pflag.FlagSet) { flags.Var(opts.NewQuotedString(&tlsOptions.CertFile), "tlscert", "Path to TLS certificate file") flags.Var(opts.NewQuotedString(&tlsOptions.KeyFile), "tlskey", "Path to TLS key file") - hostOpt := opts.NewNamedListOptsRef("hosts", &commonOpts.Hosts, opts.ValidateHost) + // opts.ValidateHost is not used here, so as to allow connection helpers + hostOpt := opts.NewNamedListOptsRef("hosts", &commonOpts.Hosts, nil) flags.VarP(hostOpt, "host", "H", "Daemon socket(s) to connect to") } diff --git a/contrib/connhelpers/README.md b/contrib/connhelpers/README.md new file mode 100644 index 000000000000..cefc93c8c44a --- /dev/null +++ b/contrib/connhelpers/README.md @@ -0,0 +1,34 @@ +# Connection Helpers + +Connection helpers allow connecting to a remote daemon with custom connection method. + +## Installation + +You need to put the following to `~/.docker/config.json`: +``` +{ + "connHelpers": { + "ssh": "ssh", + "dind": "dind" + } +} +``` + +## docker-connection-ssh + +Usage: +``` +$ docker -H ssh://[user@]host[:port][socketpath] +``` + +Requirements: + +- public key authentication is configured +- `socat` must be installed on the remote host + +## docker-connection-dind + +Usage: +``` +$ docker -H dind://containername +``` diff --git a/contrib/connhelpers/docker-connection-dind/main.go b/contrib/connhelpers/docker-connection-dind/main.go new file mode 100644 index 000000000000..75e637f1a8a2 --- /dev/null +++ b/contrib/connhelpers/docker-connection-dind/main.go @@ -0,0 +1,57 @@ +package main + +import ( + "fmt" + "net/url" + "os" + "os/exec" + "strings" + + "github.com/pkg/errors" +) + +func main() { + if err := xmain(); err != nil { + fmt.Fprintln(os.Stderr, err) + os.Exit(1) + } +} + +func xmain() error { + if len(os.Args) != 3 || os.Args[1] != "connect" { + return errors.Errorf("usage: %s connect URL", os.Args[0]) + } + u, err := url.Parse(os.Args[2]) + if err != nil { + return err + } + if u.Scheme != "dind" { + return errors.Errorf("expected scheme: dind, got %s", u.Scheme) + } + // TODO: support setting realDockerHost and realDockerTLSVerify + // dind://containername?host=tcp%3A%2F%2Fhost%3A2376?tlsVerify=1 + return execDockerExec(u.Hostname(), "", false) +} + +func execDockerExec(containerName, realDockerHost string, realDockerTLSVerify bool) error { + // Astonishngly we can't use nc, as nc does not exit when the remote connection is closed. + // cmd := exec.Command("docker", "exec", "-i", containerName, "nc", "localhost", "2375") + cmd := exec.Command("docker", "exec", "-i", containerName, + "docker", "run", "-i", "--rm", "-v", "/var/run/docker.sock:/var/run/docker.sock", "alpine/socat:1.0.1", "unix:/var/run/docker.sock", "stdio") + cmd.Env = os.Environ() + for i, s := range cmd.Env { + if strings.HasPrefix(s, "DOCKER_HOST=") || strings.HasPrefix(s, "DOCKER_TLS_VERIFY=") { + cmd.Env = append(cmd.Env[:i], cmd.Env[i+1:]...) + } + } + if realDockerHost != "" { + cmd.Env = append(cmd.Env, "DOCKER_HOST="+realDockerHost) + } + if realDockerTLSVerify { + cmd.Env = append(cmd.Env, "DOCKER_TLS_VERIFY=1") + } + cmd.Stdin = os.Stdin + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + return cmd.Run() +} diff --git a/contrib/connhelpers/docker-connection-ssh/main.go b/contrib/connhelpers/docker-connection-ssh/main.go new file mode 100644 index 000000000000..430f4d85f2a5 --- /dev/null +++ b/contrib/connhelpers/docker-connection-ssh/main.go @@ -0,0 +1,67 @@ +package main + +import ( + "fmt" + "net/url" + "os" + "os/exec" + + "github.com/pkg/errors" +) + +func main() { + if err := xmain(); err != nil { + fmt.Fprintln(os.Stderr, err) + os.Exit(1) + } +} + +func xmain() error { + if len(os.Args) != 3 || os.Args[1] != "connect" { + return errors.Errorf("usage: %s connect URL", os.Args[0]) + } + u, err := url.Parse(os.Args[2]) + if err != nil { + return err + } + if u.Scheme != "ssh" { + return errors.Errorf("expected scheme: ssh, got %s", u.Scheme) + } + var ( + user string + host string + port string + socket = "/var/run/docker.sock" + ) + + if u.User != nil { + user = u.User.Username() + if _, ok := u.User.Password(); ok { + return errors.New("ssh does not accept plain-text password") + } + } + host = u.Hostname() + port = u.Port() + if u.Path != "" { + socket = u.Path + } + return execSSH(user, host, port, socket) +} + +func execSSH(user, host, port, socket string) error { + var args []string + if user != "" { + args = append(args, "-l", user) + } + if port != "" { + args = append(args, "-p", port) + } + // TODO: use "docker run alpine/socat" when socat is not installed? + args = append(args, host, "--", + "socat", "unix:"+socket, "stdio") + cmd := exec.Command("ssh", args...) + cmd.Stdin = os.Stdin + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + return cmd.Run() +} diff --git a/docs/reference/commandline/cli.md b/docs/reference/commandline/cli.md index f290c532504c..4e3a38d7fe08 100644 --- a/docs/reference/commandline/cli.md +++ b/docs/reference/commandline/cli.md @@ -88,6 +88,31 @@ These Go environment variables are case-insensitive. See the [Go specification](http://golang.org/pkg/net/http/) for details on these variables. +### Connection helpers + +The Docker client offers built-in support for the following socket types: +`tcp`, `unix`, and `npipe`. + +You can use connection helper programs to support other connection types +such as `ssh://user@host:/var/run/docker.sock`. + +See [`github.com/docker/cli/contrib/connhelpers`](https://github.com/docker/cli/tree/master/contrib/connhelpers) +for the available helpers. + +You need to set the `connHelpers` section in [the configuration file](#configuration-files). +e.g. +``` +{ + "connHelpers": { + "ssh": "ssh" + } +} +``` +to enable `docker-connection-ssh` helper for `ssh://...` URLs. + +When a connection helper is used, proxy environment variables will not be used +by Docker CLI itself but may be used by the connection helper. + ### Configuration files By default, the Docker command line stores its configuration files in a @@ -196,6 +221,10 @@ credentials for specific registries. If this property is set, the binary for a specific registry. For more information, see the [**Credential helpers** section in the `docker login` documentation](login.md#credential-helpers) +The property `connHelpers` specifies a set of connection helpers to use +non-standard connection protocols. + For more information, see the [**Connection helpers** section above](#connection-helpers). + Once attached to a container, users detach from it and leave it running using the using `CTRL-p CTRL-q` key sequence. This detach key sequence is customizable using the `detachKeys` property. Specify a `` value for the @@ -236,6 +265,9 @@ Following is a sample `config.json` file: "credHelpers": { "awesomereg.example.org": "hip-star", "unicorn.example.com": "vcbait" + }, + "connHelpers": { + "ssh": "ssh" } } {% endraw %} diff --git a/vendor.conf b/vendor.conf index 1c71f18c39ce..d72b402dc31d 100755 --- a/vendor.conf +++ b/vendor.conf @@ -5,7 +5,8 @@ github.com/coreos/etcd v3.2.1 github.com/cpuguy83/go-md2man v1.0.8 github.com/davecgh/go-spew 346938d642f2ec3594ed81d874461961cd0faa76 github.com/docker/distribution edc3ab29cdff8694dd6feb85cfeb4b5f1b38ed9c -github.com/docker/docker ae7016427f8cba4e4d8fcb979d6ba313ee2c0702 +# needs-vendoring: moby/moby#XXXXX +github.com/docker/docker c90e7b3d729b39261a21f84b307f704a219be6a1 https://github.com/AkihiroSuda/docker.git github.com/docker/docker-credential-helpers 3c90bd29a46b943b2a9842987b58fb91a7c1819b # the docker/go package contains a customized version of canonical/json # and is used by Notary. The package is periodically rebased on current Go versions. diff --git a/vendor/github.com/docker/docker/client/client.go b/vendor/github.com/docker/docker/client/client.go index e129bb20f31c..55c60b8a1c7f 100644 --- a/vendor/github.com/docker/docker/client/client.go +++ b/vendor/github.com/docker/docker/client/client.go @@ -63,6 +63,9 @@ import ( // ErrRedirect is the error returned by checkRedirect when the request is non-GET. var ErrRedirect = errors.New("unexpected redirect in response") +// HijackDialer is the type used for custom hijack dialer +type HijackDialer func(ctx context.Context, network, addr string) (net.Conn, error) + // Client is the API client that performs all operations // against a docker server. type Client struct { @@ -78,6 +81,9 @@ type Client struct { basePath string // client used to send and receive http requests. client *http.Client + // dialer used for hijacking. When set to non-nil, it typically corresponds to + // client.Transport.(*http.Tranport).DialContext . + hijackDialer HijackDialer // version of the server to talk to. version string // custom http headers configured by users. @@ -209,7 +215,8 @@ func WithHost(host string) func(*Client) error { } } -// WithHTTPClient overrides the client http client with the specified one +// WithHTTPClient overrides the client http client with the specified one. +// See also WithHijackDialer. func WithHTTPClient(client *http.Client) func(*Client) error { return func(c *Client) error { if client != nil { @@ -219,6 +226,17 @@ func WithHTTPClient(client *http.Client) func(*Client) error { } } +// WithHijackDialer overrides the client default hijack dialer. +// The typical usecase of WithHijackDialer is to allow implementing connection helpers. +// The hijack dialer typically corresponds to *http.Client.Transport.(*http.Tranport).DialContext . +// See WithHTTPClient. +func WithHijackDialer(hijackDialer HijackDialer) func(*Client) error { + return func(c *Client) error { + c.hijackDialer = hijackDialer + return nil + } +} + // WithHTTPHeaders overrides the client default http headers func WithHTTPHeaders(headers map[string]string) func(*Client) error { return func(c *Client) error { @@ -240,12 +258,13 @@ func NewClientWithOpts(ops ...func(*Client) error) (*Client, error) { return nil, err } c := &Client{ - host: DefaultDockerHost, - version: api.DefaultVersion, - scheme: "http", - client: client, - proto: defaultProto, - addr: defaultAddr, + host: DefaultDockerHost, + version: api.DefaultVersion, + scheme: "http", + client: client, + hijackDialer: nil, // set later if nil + proto: defaultProto, + addr: defaultAddr, } for _, op := range ops { @@ -266,7 +285,11 @@ func NewClientWithOpts(ops ...func(*Client) error) (*Client, error) { // this to avoid breaking existing clients but this should be addressed. c.scheme = "https" } - + if c.hijackDialer == nil { + c.hijackDialer = func(ctx context.Context, network, addr string) (net.Conn, error) { + return dial(network, addr, tlsConfig) + } + } return c, nil } diff --git a/vendor/github.com/docker/docker/client/hijack.go b/vendor/github.com/docker/docker/client/hijack.go index 5a6354561bdb..d4cf7a05606e 100644 --- a/vendor/github.com/docker/docker/client/hijack.go +++ b/vendor/github.com/docker/docker/client/hijack.go @@ -46,7 +46,7 @@ func (cli *Client) postHijacked(ctx context.Context, path string, query url.Valu } req = cli.addHeaders(req, headers) - conn, err := cli.setupHijackConn(req, "tcp") + conn, err := cli.setupHijackConn(ctx, req, "tcp") if err != nil { return types.HijackedResponse{}, err } @@ -151,12 +151,12 @@ func dial(proto, addr string, tlsConfig *tls.Config) (net.Conn, error) { return net.Dial(proto, addr) } -func (cli *Client) setupHijackConn(req *http.Request, proto string) (net.Conn, error) { +func (cli *Client) setupHijackConn(ctx context.Context, req *http.Request, proto string) (net.Conn, error) { req.Host = cli.addr req.Header.Set("Connection", "Upgrade") req.Header.Set("Upgrade", proto) - conn, err := dial(cli.proto, cli.addr, resolveTLSConfig(cli.client.Transport)) + conn, err := cli.hijackDialer(ctx, cli.proto, cli.addr) if err != nil { return nil, errors.Wrap(err, "cannot connect to the Docker daemon. Is 'docker daemon' running on this host?") } diff --git a/vendor/github.com/docker/docker/client/session.go b/vendor/github.com/docker/docker/client/session.go index b136538c2255..40614c642e6b 100644 --- a/vendor/github.com/docker/docker/client/session.go +++ b/vendor/github.com/docker/docker/client/session.go @@ -15,5 +15,5 @@ func (cli *Client) DialSession(ctx context.Context, proto string, meta map[strin } req = cli.addHeaders(req, meta) - return cli.setupHijackConn(req, proto) + return cli.setupHijackConn(ctx, req, proto) } diff --git a/vendor/github.com/docker/docker/pkg/jsonmessage/jsonmessage.go b/vendor/github.com/docker/docker/pkg/jsonmessage/jsonmessage.go index 368f128949d0..b9f40d3ef1ea 100644 --- a/vendor/github.com/docker/docker/pkg/jsonmessage/jsonmessage.go +++ b/vendor/github.com/docker/docker/pkg/jsonmessage/jsonmessage.go @@ -40,21 +40,17 @@ type JSONProgress struct { // If true, don't show xB/yB HideCounts bool `json:"hidecounts,omitempty"` Units string `json:"units,omitempty"` + nowFunc func() time.Time + winSize int } func (p *JSONProgress) String() string { var ( - width = 200 + width = p.width() pbBox string numbersBox string timeLeftBox string ) - - ws, err := term.GetWinsize(p.terminalFd) - if err == nil { - width = int(ws.Width) - } - if p.Current <= 0 && p.Total <= 0 { return "" } @@ -103,7 +99,7 @@ func (p *JSONProgress) String() string { } if p.Current > 0 && p.Start > 0 && percentage < 50 { - fromStart := time.Now().UTC().Sub(time.Unix(p.Start, 0)) + fromStart := p.now().Sub(time.Unix(p.Start, 0)) perEntry := fromStart / time.Duration(p.Current) left := time.Duration(p.Total-p.Current) * perEntry left = (left / time.Second) * time.Second @@ -115,6 +111,28 @@ func (p *JSONProgress) String() string { return pbBox + numbersBox + timeLeftBox } +// shim for testing +func (p *JSONProgress) now() time.Time { + if p.nowFunc == nil { + p.nowFunc = func() time.Time { + return time.Now().UTC() + } + } + return p.nowFunc() +} + +// shim for testing +func (p *JSONProgress) width() int { + if p.winSize != 0 { + return p.winSize + } + ws, err := term.GetWinsize(p.terminalFd) + if err == nil { + return int(ws.Width) + } + return 200 +} + // JSONMessage defines a message struct. It describes // the created time, where it from, status, ID of the // message. It's used for docker events. diff --git a/vendor/github.com/docker/docker/vendor.conf b/vendor/github.com/docker/docker/vendor.conf index 9b5d5693cc86..faf4f9c043a9 100644 --- a/vendor/github.com/docker/docker/vendor.conf +++ b/vendor/github.com/docker/docker/vendor.conf @@ -2,7 +2,6 @@ github.com/Azure/go-ansiterm d6e3b3328b783f23731bc4d058875b0371ff8109 github.com/Microsoft/hcsshim v0.6.8 github.com/Microsoft/go-winio v0.4.6 -github.com/davecgh/go-spew 346938d642f2ec3594ed81d874461961cd0faa76 github.com/docker/libtrust 9cbd2a1374f46905c68a4eb3694a130610adc62a github.com/go-check/check 4ed411733c5785b40214c70bce814c3a3a689609 https://github.com/cpuguy83/check.git github.com/golang/gddo 9b12a26f3fbd7397dee4e20939ddca719d840d2a @@ -19,10 +18,9 @@ golang.org/x/sys 37707fdb30a5b38865cfb95e5aab41707daec7fd github.com/docker/go-units 9e638d38cf6977a37a8ea0078f3ee75a7cdb2dd1 github.com/docker/go-connections 7beb39f0b969b075d1325fecb092faf27fd357b6 golang.org/x/text f72d8390a633d5dfb0cc84043294db9f6c935756 -github.com/stretchr/testify 4d4bfba8f1d1027c4fdbe371823030df51419987 github.com/pmezard/go-difflib v1.0.0 -github.com/gotestyourself/gotestyourself 511344eed30e4384f010579a593dfb442033a692 -github.com/google/go-cmp v0.1.0 +github.com/gotestyourself/gotestyourself cf3a5ab914a2efa8bc838d09f5918c1d44d029 +github.com/google/go-cmp v0.2.0 github.com/RackSec/srslog 456df3a81436d29ba874f3590eeeee25d666f8a5 github.com/imdario/mergo 0.2.1 @@ -34,7 +32,7 @@ github.com/tonistiigi/fsutil dea3a0da73aee887fc02142d995be764106ac5e2 #get libnetwork packages # When updating, also update LIBNETWORK_COMMIT in hack/dockerfile/install/proxy accordingly -github.com/docker/libnetwork 8892d7537c67232591f1f3af60587e3e77e61d41 +github.com/docker/libnetwork 1b91bc94094ecfdae41daa465cc0c8df37dfb3dd github.com/docker/go-events 9461782956ad83b30282bf90e31fa6a70c255ba9 github.com/armon/go-radix e39d623f12e8e41c7b5529e9a9dd67a1e2261f80 github.com/armon/go-metrics eb0af217e5e9747e41dd5303755356b62d28e3ec