From 2f007a47a9ceedcfebb70e945e1e9a725955c8e0 Mon Sep 17 00:00:00 2001 From: Akihiro Suda Date: Mon, 25 Mar 2019 16:31:48 +0900 Subject: [PATCH] client: add docker:// connhelper Signed-off-by: Akihiro Suda --- client/client.go | 27 +- client/connhelper/connhelper.go | 37 +++ go.mod | 2 +- go.sum | 4 +- vendor/github.com/docker/cli/AUTHORS | 21 +- .../docker/cli/cli/config/config.go | 11 +- .../docker/cli/cli/config/configfile/file.go | 88 ++++-- .../cli/connhelper/commandconn/commandconn.go | 281 ++++++++++++++++++ .../commandconn/commandconn_linux.go | 12 + .../commandconn/commandconn_nolinux.go | 10 + vendor/modules.txt | 3 +- 11 files changed, 462 insertions(+), 34 deletions(-) create mode 100644 client/connhelper/connhelper.go create mode 100644 vendor/github.com/docker/cli/cli/connhelper/commandconn/commandconn.go create mode 100644 vendor/github.com/docker/cli/cli/connhelper/commandconn/commandconn_linux.go create mode 100644 vendor/github.com/docker/cli/cli/connhelper/commandconn/commandconn_nolinux.go diff --git a/client/client.go b/client/client.go index 6126acdc1..0c66e9ffd 100644 --- a/client/client.go +++ b/client/client.go @@ -5,9 +5,12 @@ import ( "crypto/tls" "crypto/x509" "io/ioutil" + "net" + "time" "github.com/grpc-ecosystem/grpc-opentracing/go/otgrpc" controlapi "github.com/moby/buildkit/api/services/control" + "github.com/moby/buildkit/client/connhelper" "github.com/moby/buildkit/util/appdefaults" opentracing "github.com/opentracing/opentracing-go" "github.com/pkg/errors" @@ -23,8 +26,14 @@ type ClientOpt interface{} // New returns a new buildkit client. Address can be empty for the system-default address. func New(ctx context.Context, address string, opts ...ClientOpt) (*Client, error) { + dialFn, err := resolveDialer(address) + if err != nil { + return nil, err + } gopts := []grpc.DialOption{ - grpc.WithDialer(dialer), + // TODO(AkihiroSuda): use WithContextDialer (requires grpc 1.19) + // https://github.com/grpc/grpc-go/commit/40cb5618f475e7b9d61aa7920ae4b04ef9bbaf89 + grpc.WithDialer(dialFn), } needWithInsecure := true for _, o := range opts { @@ -128,3 +137,19 @@ func WithTracer(t opentracing.Tracer) ClientOpt { type withTracer struct { tracer opentracing.Tracer } + +func resolveDialer(address string) (func(string, time.Duration) (net.Conn, error), error) { + ch, err := connhelper.GetConnectionHelper(address) + if err != nil { + return nil, err + } + if ch != nil { + f := func(a string, _ time.Duration) (net.Conn, error) { + ctx := context.Background() + return ch.ContextDialer(ctx, a) + } + return f, nil + } + // basic dialer + return dialer, nil +} diff --git a/client/connhelper/connhelper.go b/client/connhelper/connhelper.go new file mode 100644 index 000000000..9fa5edcd9 --- /dev/null +++ b/client/connhelper/connhelper.go @@ -0,0 +1,37 @@ +// Package connhelper provides helpers for connecting to a remote daemon host with custom logic. +package connhelper + +import ( + "context" + "net" + "net/url" + + "github.com/docker/cli/cli/connhelper/commandconn" +) + +// ConnectionHelper allows to connect to a remote host with custom stream provider binary. +type ConnectionHelper struct { + // ContextDialer can be passed to grpc.WithContextDialer + ContextDialer func(ctx context.Context, addr string) (net.Conn, error) +} + +// GetConnectionHelper returns BuildKit-specific connection helper for the given URL. +// GetConnectionHelper returns nil without error when no helper is registered for the scheme. +// +// docker:// URL requires BuildKit v0.5.0 or later in the container. +func GetConnectionHelper(daemonURL string) (*ConnectionHelper, error) { + u, err := url.Parse(daemonURL) + if err != nil { + return nil, err + } + switch scheme := u.Scheme; scheme { + case "docker": + container := u.Host + return &ConnectionHelper{ + ContextDialer: func(ctx context.Context, addr string) (net.Conn, error) { + return commandconn.New(ctx, "docker", "exec", "-i", container, "buildctl", "dial-stdio") + }, + }, nil + } + return nil, err +} diff --git a/go.mod b/go.mod index c8ca2702d..ccde07737 100644 --- a/go.mod +++ b/go.mod @@ -15,7 +15,7 @@ require ( github.com/containerd/go-runc v0.0.0-20180907222934-5a6d9f37cfa3 github.com/containerd/typeurl v0.0.0-20180627222232-a93fcdb778cd // indirect github.com/coreos/go-systemd v0.0.0-20161114122254-48702e0da86b // indirect - github.com/docker/cli v0.0.0-20190131223713-234462756460 + github.com/docker/cli v0.0.0-20190321234815-f40f9c240ab0 github.com/docker/distribution v2.7.1-0.20190205005809-0d3efadf0154+incompatible github.com/docker/docker v1.14.0-0.20190319215453-e7b5f7dbe98c github.com/docker/docker-credential-helpers v0.6.0 // indirect diff --git a/go.sum b/go.sum index 9cf42272b..28e53e8c5 100644 --- a/go.sum +++ b/go.sum @@ -32,8 +32,8 @@ github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/docker/cli v0.0.0-20190131223713-234462756460 h1:pil/Gt3dlnN7WIX+6uS3Ok3firOdyzmxqUtYrIik27I= -github.com/docker/cli v0.0.0-20190131223713-234462756460/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= +github.com/docker/cli v0.0.0-20190321234815-f40f9c240ab0 h1:E7NTtHfZYV+iu35yZ49AbrxqhMHpiOl3FstDYm38vQ0= +github.com/docker/cli v0.0.0-20190321234815-f40f9c240ab0/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= github.com/docker/distribution v2.7.1-0.20190205005809-0d3efadf0154+incompatible h1:dvc1KSkIYTVjZgHf/CTC2diTYC8PzhaA5sFISRfNVrE= github.com/docker/distribution v2.7.1-0.20190205005809-0d3efadf0154+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= github.com/docker/docker v0.0.0-20180531152204-71cd53e4a197 h1:raQhUHOMIAZAWHmo3hLEwoIy0aVkKb2uxZdWw/Up+HI= diff --git a/vendor/github.com/docker/cli/AUTHORS b/vendor/github.com/docker/cli/AUTHORS index e29faa19f..b1e1c9642 100644 --- a/vendor/github.com/docker/cli/AUTHORS +++ b/vendor/github.com/docker/cli/AUTHORS @@ -67,6 +67,7 @@ Barnaby Gray Bastiaan Bakker BastianHofmann Ben Bonnefoy +Ben Creasy Ben Firshman Benjamin Boudreau Benoit Sigoure @@ -126,6 +127,8 @@ Colin Hebert Collin Guarino Colm Hally Corey Farrell +Corey Quon +Craig Wilhite Cristian Staretu Daehyeok Mun Dafydd Crosby @@ -185,6 +188,8 @@ Elango Sivanandam Eli Uriegas Eli Uriegas Elias Faxö +Elliot Luo <956941328@qq.com> +Eric Curtin Eric G. Noriega Eric Rosenberg Eric Sage @@ -205,9 +210,11 @@ Fabio Falci Fabrizio Soppelsa Felix Hupfeld Felix Rabe +Filip Jareš Flavio Crisciani Florian Klein Foysal Iqbal +François Scala Fred Lifton Frederic Hemberger Frederick F. Kautz IV @@ -273,8 +280,9 @@ Jasmine Hegman Jason Heiss Jason Plum Jay Kamat +Jean Rouge +Jean-Christophe Sirot Jean-Pierre Huynh -Jean-Pierre Huynh Jeff Lindsay Jeff Nickoloff Jeff Silberman @@ -412,11 +420,13 @@ Mark Oates Marsh Macy Martin Mosegaard Amdisen Mary Anthony +Mason Fish Mason Malone Mateusz Major Mathieu Champlon Matt Gucci Matt Robenolt +Matteo Orefice Matthew Heon Matthieu Hauglustaine Mauro Porras P @@ -453,6 +463,7 @@ Mindaugas Rukas Misty Stanley-Jones Mohammad Banikazemi Mohammed Aaqib Ansari +Mohini Anne Dsouza Moorthy RS Morgan Bauer Moysés Borges @@ -470,6 +481,7 @@ Nathan Hsieh Nathan LeClaire Nathan McCauley Neil Peterson +Nick Adcock Nico Stapelbroek Nicola Kabar Nicolas Borboën @@ -509,6 +521,7 @@ Peter Waller Phil Estes Philip Alexander Etling Philipp Gillé +Philipp Schmied pidster pixelistik Pratik Karki @@ -544,6 +557,8 @@ Rui Cao Ryan Belgrave Ryan Detzel Ryan Stelly +Ryan Wilson-Perkin +Ryan Zhang Sainath Grandhi Sakeven Jiang Sally O'Malley @@ -582,9 +597,11 @@ Srini Brahmaroutu Stefan S. Stefan Scherer Stefan Weil +Stephane Jeandeaux Stephen Day Stephen Rust Steve Durrheimer +Steve Richards Steven Burgess Subhajit Ghosh Sun Jianbo @@ -633,6 +650,7 @@ Tristan Carel Tycho Andersen Tycho Andersen uhayate +Ulysses Souza Umesh Yadav Valentin Lorentz Veres Lajos @@ -683,6 +701,7 @@ Zhang Wentao ZhangHang zhenghenghuo Zhou Hao +Zhoulin Xie Zhu Guihua Álex González Álvaro Lázaro diff --git a/vendor/github.com/docker/cli/cli/config/config.go b/vendor/github.com/docker/cli/cli/config/config.go index 2a6c5f0ea..f5e33f2b3 100644 --- a/vendor/github.com/docker/cli/cli/config/config.go +++ b/vendor/github.com/docker/cli/cli/config/config.go @@ -5,6 +5,7 @@ import ( "io" "os" "path/filepath" + "strings" "github.com/docker/cli/cli/config/configfile" "github.com/docker/cli/cli/config/credentials" @@ -43,12 +44,16 @@ func ContextStoreDir() string { // SetDir sets the directory the configuration file is stored in func SetDir(dir string) { - configDir = dir + configDir = filepath.Clean(dir) } // Path returns the path to a file relative to the config dir -func Path(p ...string) string { - return filepath.Join(append([]string{Dir()}, p...)...) +func Path(p ...string) (string, error) { + path := filepath.Join(append([]string{Dir()}, p...)...) + if !strings.HasPrefix(path, Dir()+string(filepath.Separator)) { + return "", errors.Errorf("path %q is outside of root config directory %q", path, Dir()) + } + return path, nil } // LegacyLoadFromReader is a convenience function that creates a ConfigFile object from diff --git a/vendor/github.com/docker/cli/cli/config/configfile/file.go b/vendor/github.com/docker/cli/cli/config/configfile/file.go index 656184993..c8d601162 100644 --- a/vendor/github.com/docker/cli/cli/config/configfile/file.go +++ b/vendor/github.com/docker/cli/cli/config/configfile/file.go @@ -24,31 +24,32 @@ const ( // ConfigFile ~/.docker/config.json file info type ConfigFile struct { - AuthConfigs map[string]types.AuthConfig `json:"auths"` - HTTPHeaders map[string]string `json:"HttpHeaders,omitempty"` - PsFormat string `json:"psFormat,omitempty"` - ImagesFormat string `json:"imagesFormat,omitempty"` - NetworksFormat string `json:"networksFormat,omitempty"` - PluginsFormat string `json:"pluginsFormat,omitempty"` - VolumesFormat string `json:"volumesFormat,omitempty"` - StatsFormat string `json:"statsFormat,omitempty"` - DetachKeys string `json:"detachKeys,omitempty"` - CredentialsStore string `json:"credsStore,omitempty"` - CredentialHelpers map[string]string `json:"credHelpers,omitempty"` - Filename string `json:"-"` // Note: for internal use only - ServiceInspectFormat string `json:"serviceInspectFormat,omitempty"` - ServicesFormat string `json:"servicesFormat,omitempty"` - TasksFormat string `json:"tasksFormat,omitempty"` - SecretFormat string `json:"secretFormat,omitempty"` - ConfigFormat string `json:"configFormat,omitempty"` - NodesFormat string `json:"nodesFormat,omitempty"` - PruneFilters []string `json:"pruneFilters,omitempty"` - Proxies map[string]ProxyConfig `json:"proxies,omitempty"` - Experimental string `json:"experimental,omitempty"` - StackOrchestrator string `json:"stackOrchestrator,omitempty"` - Kubernetes *KubernetesConfig `json:"kubernetes,omitempty"` - CurrentContext string `json:"currentContext,omitempty"` - CLIPluginsExtraDirs []string `json:"cliPluginsExtraDirs,omitempty"` + AuthConfigs map[string]types.AuthConfig `json:"auths"` + HTTPHeaders map[string]string `json:"HttpHeaders,omitempty"` + PsFormat string `json:"psFormat,omitempty"` + ImagesFormat string `json:"imagesFormat,omitempty"` + NetworksFormat string `json:"networksFormat,omitempty"` + PluginsFormat string `json:"pluginsFormat,omitempty"` + VolumesFormat string `json:"volumesFormat,omitempty"` + StatsFormat string `json:"statsFormat,omitempty"` + DetachKeys string `json:"detachKeys,omitempty"` + CredentialsStore string `json:"credsStore,omitempty"` + CredentialHelpers map[string]string `json:"credHelpers,omitempty"` + Filename string `json:"-"` // Note: for internal use only + ServiceInspectFormat string `json:"serviceInspectFormat,omitempty"` + ServicesFormat string `json:"servicesFormat,omitempty"` + TasksFormat string `json:"tasksFormat,omitempty"` + SecretFormat string `json:"secretFormat,omitempty"` + ConfigFormat string `json:"configFormat,omitempty"` + NodesFormat string `json:"nodesFormat,omitempty"` + PruneFilters []string `json:"pruneFilters,omitempty"` + Proxies map[string]ProxyConfig `json:"proxies,omitempty"` + Experimental string `json:"experimental,omitempty"` + StackOrchestrator string `json:"stackOrchestrator,omitempty"` + Kubernetes *KubernetesConfig `json:"kubernetes,omitempty"` + CurrentContext string `json:"currentContext,omitempty"` + CLIPluginsExtraDirs []string `json:"cliPluginsExtraDirs,omitempty"` + Plugins map[string]map[string]string `json:"plugins,omitempty"` } // ProxyConfig contains proxy configuration settings @@ -70,6 +71,7 @@ func New(fn string) *ConfigFile { AuthConfigs: make(map[string]types.AuthConfig), HTTPHeaders: make(map[string]string), Filename: fn, + Plugins: make(map[string]map[string]string), } } @@ -330,6 +332,42 @@ func (configFile *ConfigFile) GetFilename() string { return configFile.Filename } +// PluginConfig retrieves the requested option for the given plugin. +func (configFile *ConfigFile) PluginConfig(pluginname, option string) (string, bool) { + if configFile.Plugins == nil { + return "", false + } + pluginConfig, ok := configFile.Plugins[pluginname] + if !ok { + return "", false + } + value, ok := pluginConfig[option] + return value, ok +} + +// SetPluginConfig sets the option to the given value for the given +// plugin. Passing a value of "" will remove the option. If removing +// the final config item for a given plugin then also cleans up the +// overall plugin entry. +func (configFile *ConfigFile) SetPluginConfig(pluginname, option, value string) { + if configFile.Plugins == nil { + configFile.Plugins = make(map[string]map[string]string) + } + pluginConfig, ok := configFile.Plugins[pluginname] + if !ok { + pluginConfig = make(map[string]string) + configFile.Plugins[pluginname] = pluginConfig + } + if value != "" { + pluginConfig[option] = value + } else { + delete(pluginConfig, option) + } + if len(pluginConfig) == 0 { + delete(configFile.Plugins, pluginname) + } +} + func checkKubernetesConfiguration(kubeConfig *KubernetesConfig) error { if kubeConfig == nil { return nil diff --git a/vendor/github.com/docker/cli/cli/connhelper/commandconn/commandconn.go b/vendor/github.com/docker/cli/cli/connhelper/commandconn/commandconn.go new file mode 100644 index 000000000..7e03741fa --- /dev/null +++ b/vendor/github.com/docker/cli/cli/connhelper/commandconn/commandconn.go @@ -0,0 +1,281 @@ +// Package commandconn provides a net.Conn implementation that can be used for +// proxying (or emulating) stream via a custom command. +// +// For example, to provide an http.Client that can connect to a Docker daemon +// running in a Docker container ("DIND"): +// +// httpClient := &http.Client{ +// Transport: &http.Transport{ +// DialContext: func(ctx context.Context, _network, _addr string) (net.Conn, error) { +// return commandconn.New(ctx, "docker", "exec", "-it", containerID, "docker", "system", "dial-stdio") +// }, +// }, +// } +package commandconn + +import ( + "bytes" + "context" + "fmt" + "io" + "net" + "os" + "os/exec" + "runtime" + "strings" + "sync" + "syscall" + "time" + + "github.com/pkg/errors" + "github.com/sirupsen/logrus" +) + +// New returns net.Conn +func New(ctx context.Context, cmd string, args ...string) (net.Conn, error) { + var ( + c commandConn + err error + ) + c.cmd = exec.CommandContext(ctx, cmd, args...) + // we assume that args never contains sensitive information + logrus.Debugf("commandconn: starting %s with %v", cmd, args) + c.cmd.Env = os.Environ() + setPdeathsig(c.cmd) + 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 = &stderrWriter{ + stderrMu: &c.stderrMu, + stderr: &c.stderr, + debugPrefix: fmt.Sprintf("commandconn (%s):", cmd), + } + c.localAddr = dummyAddr{network: "dummy", s: "dummy-0"} + c.remoteAddr = dummyAddr{network: "dummy", s: "dummy-1"} + return &c, c.cmd.Start() +} + +// commandConn implements net.Conn +type commandConn struct { + cmd *exec.Cmd + cmdExited bool + cmdWaitErr error + cmdMutex sync.Mutex + stdin io.WriteCloser + stdout io.ReadCloser + stderrMu sync.Mutex + stderr bytes.Buffer + stdioClosedMu sync.Mutex // for stdinClosed and stdoutClosed + stdinClosed bool + stdoutClosed bool + localAddr net.Addr + remoteAddr net.Addr +} + +// killIfStdioClosed kills the cmd if both stdin and stdout are closed. +func (c *commandConn) killIfStdioClosed() error { + c.stdioClosedMu.Lock() + stdioClosed := c.stdoutClosed && c.stdinClosed + c.stdioClosedMu.Unlock() + if !stdioClosed { + return nil + } + return c.kill() +} + +// killAndWait tries sending SIGTERM to the process before sending SIGKILL. +func killAndWait(cmd *exec.Cmd) error { + var werr error + if runtime.GOOS != "windows" { + werrCh := make(chan error) + go func() { werrCh <- cmd.Wait() }() + cmd.Process.Signal(syscall.SIGTERM) + select { + case werr = <-werrCh: + case <-time.After(3 * time.Second): + cmd.Process.Kill() + werr = <-werrCh + } + } else { + cmd.Process.Kill() + werr = cmd.Wait() + } + return werr +} + +// kill returns nil if the command terminated, regardless to the exit status. +func (c *commandConn) kill() error { + var werr error + c.cmdMutex.Lock() + if c.cmdExited { + werr = c.cmdWaitErr + } else { + werr = killAndWait(c.cmd) + c.cmdWaitErr = werr + c.cmdExited = true + } + c.cmdMutex.Unlock() + if werr == nil { + return nil + } + wExitErr, ok := werr.(*exec.ExitError) + if ok { + if wExitErr.ProcessState.Exited() { + return nil + } + } + return errors.Wrapf(werr, "commandconn: failed to wait") +} + +func (c *commandConn) onEOF(eof error) error { + // when we got EOF, the command is going to be terminated + var werr error + c.cmdMutex.Lock() + if c.cmdExited { + werr = c.cmdWaitErr + } else { + werrCh := make(chan error) + go func() { werrCh <- c.cmd.Wait() }() + select { + case werr = <-werrCh: + c.cmdWaitErr = werr + c.cmdExited = true + case <-time.After(10 * time.Second): + c.cmdMutex.Unlock() + c.stderrMu.Lock() + stderr := c.stderr.String() + c.stderrMu.Unlock() + return errors.Errorf("command %v did not exit after %v: stderr=%q", c.cmd.Args, eof, stderr) + } + } + c.cmdMutex.Unlock() + if werr == nil { + return eof + } + c.stderrMu.Lock() + stderr := c.stderr.String() + c.stderrMu.Unlock() + return errors.Errorf("command %v has exited with %v, please make sure the URL is valid, and Docker 18.09 or later is installed on the remote host: stderr=%s", c.cmd.Args, werr, stderr) +} + +func ignorableCloseError(err error) bool { + errS := err.Error() + ss := []string{ + os.ErrClosed.Error(), + } + for _, s := range ss { + if strings.Contains(errS, s) { + return true + } + } + return false +} + +func (c *commandConn) CloseRead() error { + // NOTE: maybe already closed here + if err := c.stdout.Close(); err != nil && !ignorableCloseError(err) { + logrus.Warnf("commandConn.CloseRead: %v", err) + } + c.stdioClosedMu.Lock() + c.stdoutClosed = true + c.stdioClosedMu.Unlock() + if err := c.killIfStdioClosed(); err != nil { + logrus.Warnf("commandConn.CloseRead: %v", err) + } + return nil +} + +func (c *commandConn) Read(p []byte) (int, error) { + n, err := c.stdout.Read(p) + if err == io.EOF { + err = c.onEOF(err) + } + return n, err +} + +func (c *commandConn) CloseWrite() error { + // NOTE: maybe already closed here + if err := c.stdin.Close(); err != nil && !ignorableCloseError(err) { + logrus.Warnf("commandConn.CloseWrite: %v", err) + } + c.stdioClosedMu.Lock() + c.stdinClosed = true + c.stdioClosedMu.Unlock() + if err := c.killIfStdioClosed(); err != nil { + logrus.Warnf("commandConn.CloseWrite: %v", err) + } + return nil +} + +func (c *commandConn) Write(p []byte) (int, error) { + n, err := c.stdin.Write(p) + if err == io.EOF { + err = c.onEOF(err) + } + return n, err +} + +func (c *commandConn) Close() error { + var err error + if err = c.CloseRead(); err != nil { + logrus.Warnf("commandConn.Close: CloseRead: %v", err) + } + if err = c.CloseWrite(); err != nil { + logrus.Warnf("commandConn.Close: CloseWrite: %v", err) + } + return err +} + +func (c *commandConn) LocalAddr() net.Addr { + return c.localAddr +} +func (c *commandConn) RemoteAddr() net.Addr { + return c.remoteAddr +} +func (c *commandConn) SetDeadline(t time.Time) error { + logrus.Debugf("unimplemented call: SetDeadline(%v)", t) + return nil +} +func (c *commandConn) SetReadDeadline(t time.Time) error { + logrus.Debugf("unimplemented call: SetReadDeadline(%v)", t) + return nil +} +func (c *commandConn) 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 stderrWriter struct { + stderrMu *sync.Mutex + stderr *bytes.Buffer + debugPrefix string +} + +func (w *stderrWriter) Write(p []byte) (int, error) { + logrus.Debugf("%s%s", w.debugPrefix, string(p)) + w.stderrMu.Lock() + if w.stderr.Len() > 4096 { + w.stderr.Reset() + } + n, err := w.stderr.Write(p) + w.stderrMu.Unlock() + return n, err +} diff --git a/vendor/github.com/docker/cli/cli/connhelper/commandconn/commandconn_linux.go b/vendor/github.com/docker/cli/cli/connhelper/commandconn/commandconn_linux.go new file mode 100644 index 000000000..7d8b122e3 --- /dev/null +++ b/vendor/github.com/docker/cli/cli/connhelper/commandconn/commandconn_linux.go @@ -0,0 +1,12 @@ +package commandconn + +import ( + "os/exec" + "syscall" +) + +func setPdeathsig(cmd *exec.Cmd) { + cmd.SysProcAttr = &syscall.SysProcAttr{ + Pdeathsig: syscall.SIGKILL, + } +} diff --git a/vendor/github.com/docker/cli/cli/connhelper/commandconn/commandconn_nolinux.go b/vendor/github.com/docker/cli/cli/connhelper/commandconn/commandconn_nolinux.go new file mode 100644 index 000000000..ab0716672 --- /dev/null +++ b/vendor/github.com/docker/cli/cli/connhelper/commandconn/commandconn_nolinux.go @@ -0,0 +1,10 @@ +// +build !linux + +package commandconn + +import ( + "os/exec" +) + +func setPdeathsig(cmd *exec.Cmd) { +} diff --git a/vendor/modules.txt b/vendor/modules.txt index bd8c2627a..b035a207f 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -106,7 +106,8 @@ github.com/containerd/go-runc github.com/containerd/typeurl # github.com/davecgh/go-spew v1.1.1 github.com/davecgh/go-spew/spew -# github.com/docker/cli v0.0.0-20190131223713-234462756460 +# github.com/docker/cli v0.0.0-20190321234815-f40f9c240ab0 +github.com/docker/cli/cli/connhelper/commandconn github.com/docker/cli/cli/config github.com/docker/cli/cli/config/configfile github.com/docker/cli/cli/config/credentials