Merge pull request #1960 from thaJeztah/0.11_backport_update_cli

[0.11 backport] vendor: github.com/docker/cli v24.0.4
pull/1977/head
CrazyMax 2 years ago committed by GitHub
commit d5bfd8334f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -52,17 +52,6 @@ func NewRootCmd(name string, isPlugin bool, dockerCli command.Cli) *cobra.Comman
"using default config store", "using default config store",
)) ))
// filter out useless commandConn.CloseWrite warning message that can occur
// when listing builder instances with "buildx ls" for those that are
// unreachable: "commandConn.CloseWrite: commandconn: failed to wait: signal: killed"
// https://github.com/docker/cli/blob/3fb4fb83dfb5db0c0753a8316f21aea54dab32c5/cli/connhelper/commandconn/commandconn.go#L203-L214
logrus.AddHook(logutil.NewFilter([]logrus.Level{
logrus.WarnLevel,
},
"commandConn.CloseWrite:",
"commandConn.CloseRead:",
))
addCommands(cmd, dockerCli) addCommands(cmd, dockerCli)
return cmd return cmd
} }

@ -10,7 +10,7 @@ require (
github.com/containerd/containerd v1.7.2 github.com/containerd/containerd v1.7.2
github.com/containerd/continuity v0.4.1 github.com/containerd/continuity v0.4.1
github.com/containerd/typeurl/v2 v2.1.1 github.com/containerd/typeurl/v2 v2.1.1
github.com/docker/cli v24.0.2+incompatible github.com/docker/cli v24.0.4+incompatible
github.com/docker/cli-docs-tool v0.6.0 github.com/docker/cli-docs-tool v0.6.0
github.com/docker/distribution v2.8.2+incompatible github.com/docker/distribution v2.8.2+incompatible
github.com/docker/docker v24.0.5-0.20230714235725-36e9e796c6fc+incompatible // 24.0 github.com/docker/docker v24.0.5-0.20230714235725-36e9e796c6fc+incompatible // 24.0

@ -149,8 +149,8 @@ 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/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/distribution/distribution/v3 v3.0.0-20230214150026-36d8c594d7aa h1:L9Ay/slwQ4ERSPaurC+TVkZrM0K98GNrEEo1En3e8as= github.com/distribution/distribution/v3 v3.0.0-20230214150026-36d8c594d7aa h1:L9Ay/slwQ4ERSPaurC+TVkZrM0K98GNrEEo1En3e8as=
github.com/distribution/distribution/v3 v3.0.0-20230214150026-36d8c594d7aa/go.mod h1:WHNsWjnIn2V1LYOrME7e8KxSeKunYHsxEm4am0BUtcI= github.com/distribution/distribution/v3 v3.0.0-20230214150026-36d8c594d7aa/go.mod h1:WHNsWjnIn2V1LYOrME7e8KxSeKunYHsxEm4am0BUtcI=
github.com/docker/cli v24.0.2+incompatible h1:QdqR7znue1mtkXIJ+ruQMGQhpw2JzMJLRXp6zpzF6tM= github.com/docker/cli v24.0.4+incompatible h1:Y3bYF9ekNTm2VFz5U/0BlMdJy73D+Y1iAAZ8l63Ydzw=
github.com/docker/cli v24.0.2+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= github.com/docker/cli v24.0.4+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
github.com/docker/cli-docs-tool v0.6.0 h1:Z9x10SaZgFaB6jHgz3OWooynhSa40CsWkpe5hEnG/qA= github.com/docker/cli-docs-tool v0.6.0 h1:Z9x10SaZgFaB6jHgz3OWooynhSa40CsWkpe5hEnG/qA=
github.com/docker/cli-docs-tool v0.6.0/go.mod h1:zMjqTFCU361PRh8apiXzeAZ1Q/xupbIwTusYpzCXS/o= github.com/docker/cli-docs-tool v0.6.0/go.mod h1:zMjqTFCU361PRh8apiXzeAZ1Q/xupbIwTusYpzCXS/o=
github.com/docker/distribution v2.8.2+incompatible h1:T3de5rq0dB1j30rp0sA2rER+m322EBzniBPB6ZIzuh8= github.com/docker/distribution v2.8.2+incompatible h1:T3de5rq0dB1j30rp0sA2rER+m322EBzniBPB6ZIzuh8=

@ -8,7 +8,6 @@ import (
"path/filepath" "path/filepath"
"runtime" "runtime"
"strconv" "strconv"
"strings"
"sync" "sync"
"time" "time"
@ -327,13 +326,8 @@ func (cli *DockerCli) getInitTimeout() time.Duration {
func (cli *DockerCli) initializeFromClient() { func (cli *DockerCli) initializeFromClient() {
ctx := context.Background() ctx := context.Background()
if !strings.HasPrefix(cli.dockerEndpoint.Host, "ssh://") { ctx, cancel := context.WithTimeout(ctx, cli.getInitTimeout())
// @FIXME context.WithTimeout doesn't work with connhelper / ssh connections defer cancel()
// time="2020-04-10T10:16:26Z" level=warning msg="commandConn.CloseWrite: commandconn: failed to wait: signal: killed"
var cancel func()
ctx, cancel = context.WithTimeout(ctx, cli.getInitTimeout())
defer cancel()
}
ping, err := cli.client.Ping(ctx) ping, err := cli.client.Ping(ctx)
if err != nil { if err != nil {
@ -381,7 +375,7 @@ func (cli *DockerCli) ContextStore() store.Store {
// the "default" context is used if: // the "default" context is used if:
// //
// - The "--host" option is set // - The "--host" option is set
// - The "DOCKER_HOST" ([DefaultContextName]) environment variable is set // - The "DOCKER_HOST" ([client.EnvOverrideHost]) environment variable is set
// to a non-empty value. // to a non-empty value.
// //
// In these cases, the default context is used, which uses the host as // In these cases, the default context is used, which uses the host as

@ -23,6 +23,7 @@ import (
"runtime" "runtime"
"strings" "strings"
"sync" "sync"
"sync/atomic"
"syscall" "syscall"
"time" "time"
@ -64,81 +65,68 @@ func New(_ context.Context, cmd string, args ...string) (net.Conn, error) {
// commandConn implements net.Conn // commandConn implements net.Conn
type commandConn struct { type commandConn struct {
cmd *exec.Cmd cmdMutex sync.Mutex // for cmd, cmdWaitErr
cmdExited bool cmd *exec.Cmd
cmdWaitErr error cmdWaitErr error
cmdMutex sync.Mutex cmdExited atomic.Bool
stdin io.WriteCloser stdin io.WriteCloser
stdout io.ReadCloser stdout io.ReadCloser
stderrMu sync.Mutex stderrMu sync.Mutex // for stderr
stderr bytes.Buffer stderr bytes.Buffer
stdioClosedMu sync.Mutex // for stdinClosed and stdoutClosed stdinClosed atomic.Bool
stdinClosed bool stdoutClosed atomic.Bool
stdoutClosed bool closing atomic.Bool
localAddr net.Addr localAddr net.Addr
remoteAddr net.Addr remoteAddr net.Addr
} }
// killIfStdioClosed kills the cmd if both stdin and stdout are closed. // kill terminates the process. On Windows it kills the process directly,
func (c *commandConn) killIfStdioClosed() error { // whereas on other platforms, a SIGTERM is sent, before forcefully terminating
c.stdioClosedMu.Lock() // the process after 3 seconds.
stdioClosed := c.stdoutClosed && c.stdinClosed func (c *commandConn) kill() {
c.stdioClosedMu.Unlock() if c.cmdExited.Load() {
if !stdioClosed { return
return nil
} }
return c.kill() c.cmdMutex.Lock()
}
// killAndWait tries sending SIGTERM to the process before sending SIGKILL.
func killAndWait(cmd *exec.Cmd) error {
var werr error var werr error
if runtime.GOOS != "windows" { if runtime.GOOS != "windows" {
werrCh := make(chan error) werrCh := make(chan error)
go func() { werrCh <- cmd.Wait() }() go func() { werrCh <- c.cmd.Wait() }()
cmd.Process.Signal(syscall.SIGTERM) _ = c.cmd.Process.Signal(syscall.SIGTERM)
select { select {
case werr = <-werrCh: case werr = <-werrCh:
case <-time.After(3 * time.Second): case <-time.After(3 * time.Second):
cmd.Process.Kill() _ = c.cmd.Process.Kill()
werr = <-werrCh werr = <-werrCh
} }
} else { } else {
cmd.Process.Kill() _ = c.cmd.Process.Kill()
werr = cmd.Wait() werr = c.cmd.Wait()
} }
return werr c.cmdWaitErr = werr
c.cmdMutex.Unlock()
c.cmdExited.Store(true)
} }
// kill returns nil if the command terminated, regardless to the exit status. // handleEOF handles io.EOF errors while reading or writing from the underlying
func (c *commandConn) kill() error { // command pipes.
var werr error //
c.cmdMutex.Lock() // When we've received an EOF we expect that the command will
if c.cmdExited { // be terminated soon. As such, we call Wait() on the command
werr = c.cmdWaitErr // and return EOF or the error depending on whether the command
} else { // exited with an error.
werr = killAndWait(c.cmd) //
c.cmdWaitErr = werr // If Wait() does not return within 10s, an error is returned
c.cmdExited = true func (c *commandConn) handleEOF(err error) error {
} if err != io.EOF {
c.cmdMutex.Unlock() return err
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() c.cmdMutex.Lock()
if c.cmdExited { defer c.cmdMutex.Unlock()
var werr error
if c.cmdExited.Load() {
werr = c.cmdWaitErr werr = c.cmdWaitErr
} else { } else {
werrCh := make(chan error) werrCh := make(chan error)
@ -146,18 +134,17 @@ func (c *commandConn) onEOF(eof error) error {
select { select {
case werr = <-werrCh: case werr = <-werrCh:
c.cmdWaitErr = werr c.cmdWaitErr = werr
c.cmdExited = true c.cmdExited.Store(true)
case <-time.After(10 * time.Second): case <-time.After(10 * time.Second):
c.cmdMutex.Unlock()
c.stderrMu.Lock() c.stderrMu.Lock()
stderr := c.stderr.String() stderr := c.stderr.String()
c.stderrMu.Unlock() c.stderrMu.Unlock()
return errors.Errorf("command %v did not exit after %v: stderr=%q", c.cmd.Args, eof, stderr) return errors.Errorf("command %v did not exit after %v: stderr=%q", c.cmd.Args, err, stderr)
} }
} }
c.cmdMutex.Unlock()
if werr == nil { if werr == nil {
return eof return err
} }
c.stderrMu.Lock() c.stderrMu.Lock()
stderr := c.stderr.String() stderr := c.stderr.String()
@ -166,71 +153,86 @@ func (c *commandConn) onEOF(eof error) error {
} }
func ignorableCloseError(err error) bool { func ignorableCloseError(err error) bool {
errS := err.Error() return strings.Contains(err.Error(), os.ErrClosed.Error())
ss := []string{ }
os.ErrClosed.Error(),
func (c *commandConn) Read(p []byte) (int, error) {
n, err := c.stdout.Read(p)
// check after the call to Read, since
// it is blocking, and while waiting on it
// Close might get called
if c.closing.Load() {
// If we're currently closing the connection
// we don't want to call onEOF
return n, err
} }
for _, s := range ss {
if strings.Contains(errS, s) { return n, c.handleEOF(err)
return true }
}
func (c *commandConn) Write(p []byte) (int, error) {
n, err := c.stdin.Write(p)
// check after the call to Write, since
// it is blocking, and while waiting on it
// Close might get called
if c.closing.Load() {
// If we're currently closing the connection
// we don't want to call onEOF
return n, err
} }
return false
return n, c.handleEOF(err)
} }
// CloseRead allows commandConn to implement halfCloser
func (c *commandConn) CloseRead() error { func (c *commandConn) CloseRead() error {
// NOTE: maybe already closed here // NOTE: maybe already closed here
if err := c.stdout.Close(); err != nil && !ignorableCloseError(err) { if err := c.stdout.Close(); err != nil && !ignorableCloseError(err) {
logrus.Warnf("commandConn.CloseRead: %v", err) return err
} }
c.stdioClosedMu.Lock() c.stdoutClosed.Store(true)
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) { if c.stdinClosed.Load() {
n, err := c.stdout.Read(p) c.kill()
if err == io.EOF {
err = c.onEOF(err)
} }
return n, err
return nil
} }
// CloseWrite allows commandConn to implement halfCloser
func (c *commandConn) CloseWrite() error { func (c *commandConn) CloseWrite() error {
// NOTE: maybe already closed here // NOTE: maybe already closed here
if err := c.stdin.Close(); err != nil && !ignorableCloseError(err) { if err := c.stdin.Close(); err != nil && !ignorableCloseError(err) {
logrus.Warnf("commandConn.CloseWrite: %v", err) return err
}
c.stdioClosedMu.Lock()
c.stdinClosed = true
c.stdioClosedMu.Unlock()
if err := c.killIfStdioClosed(); err != nil {
logrus.Warnf("commandConn.CloseWrite: %v", err)
} }
return nil c.stdinClosed.Store(true)
}
func (c *commandConn) Write(p []byte) (int, error) { if c.stdoutClosed.Load() {
n, err := c.stdin.Write(p) c.kill()
if err == io.EOF {
err = c.onEOF(err)
} }
return n, err return nil
} }
// Close is the net.Conn func that gets called
// by the transport when a dial is cancelled
// due to it's context timing out. Any blocked
// Read or Write calls will be unblocked and
// return errors. It will block until the underlying
// command has terminated.
func (c *commandConn) Close() error { func (c *commandConn) Close() error {
var err error c.closing.Store(true)
if err = c.CloseRead(); err != nil { defer c.closing.Store(false)
if err := c.CloseRead(); err != nil {
logrus.Warnf("commandConn.Close: CloseRead: %v", err) logrus.Warnf("commandConn.Close: CloseRead: %v", err)
return err
} }
if err = c.CloseWrite(); err != nil { if err := c.CloseWrite(); err != nil {
logrus.Warnf("commandConn.Close: CloseWrite: %v", err) logrus.Warnf("commandConn.Close: CloseWrite: %v", err)
return err
} }
return err
return nil
} }
func (c *commandConn) LocalAddr() net.Addr { func (c *commandConn) LocalAddr() net.Addr {

@ -5,6 +5,7 @@ import (
"context" "context"
"net" "net"
"net/url" "net/url"
"strings"
"github.com/docker/cli/cli/connhelper/commandconn" "github.com/docker/cli/cli/connhelper/commandconn"
"github.com/docker/cli/cli/connhelper/ssh" "github.com/docker/cli/cli/connhelper/ssh"
@ -51,6 +52,7 @@ func getConnectionHelper(daemonURL string, sshFlags []string) (*ConnectionHelper
if sp.Path != "" { if sp.Path != "" {
args = append(args, "--host", "unix://"+sp.Path) args = append(args, "--host", "unix://"+sp.Path)
} }
sshFlags = addSSHTimeout(sshFlags)
args = append(args, "system", "dial-stdio") args = append(args, "system", "dial-stdio")
return commandconn.New(ctx, "ssh", append(sshFlags, sp.Args(args...)...)...) return commandconn.New(ctx, "ssh", append(sshFlags, sp.Args(args...)...)...)
}, },
@ -71,3 +73,10 @@ func GetCommandConnectionHelper(cmd string, flags ...string) (*ConnectionHelper,
Host: "http://docker.example.com", Host: "http://docker.example.com",
}, nil }, nil
} }
func addSSHTimeout(sshFlags []string) []string {
if !strings.Contains(strings.Join(sshFlags, ""), "ConnectTimeout") {
sshFlags = append(sshFlags, "-o ConnectTimeout=30")
}
return sshFlags
}

@ -206,7 +206,7 @@ github.com/davecgh/go-spew/spew
# github.com/distribution/distribution/v3 v3.0.0-20230214150026-36d8c594d7aa # github.com/distribution/distribution/v3 v3.0.0-20230214150026-36d8c594d7aa
## explicit; go 1.18 ## explicit; go 1.18
github.com/distribution/distribution/v3/reference github.com/distribution/distribution/v3/reference
# github.com/docker/cli v24.0.2+incompatible # github.com/docker/cli v24.0.4+incompatible
## explicit ## explicit
github.com/docker/cli/cli github.com/docker/cli/cli
github.com/docker/cli/cli-plugins/manager github.com/docker/cli/cli-plugins/manager

Loading…
Cancel
Save