-
Notifications
You must be signed in to change notification settings - Fork 2k
support SSH connection #1014
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
support SSH connection #1014
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,107 @@ | ||
package system | ||
|
||
import ( | ||
"context" | ||
"io" | ||
"os" | ||
|
||
"github.com/docker/cli/cli" | ||
"github.com/docker/cli/cli/command" | ||
"github.com/pkg/errors" | ||
"github.com/sirupsen/logrus" | ||
"github.com/spf13/cobra" | ||
) | ||
|
||
// newDialStdioCommand creates a new cobra.Command for `docker system dial-stdio` | ||
func newDialStdioCommand(dockerCli command.Cli) *cobra.Command { | ||
cmd := &cobra.Command{ | ||
Use: "dial-stdio", | ||
Short: "Proxy the stdio stream to the daemon connection. Should not be invoked manually.", | ||
Args: cli.NoArgs, | ||
Hidden: true, | ||
RunE: func(cmd *cobra.Command, args []string) error { | ||
return runDialStdio(dockerCli) | ||
}, | ||
} | ||
return cmd | ||
} | ||
|
||
func runDialStdio(dockerCli command.Cli) error { | ||
ctx, cancel := context.WithCancel(context.Background()) | ||
defer cancel() | ||
dialer := dockerCli.Client().Dialer() | ||
conn, err := dialer(ctx) | ||
if err != nil { | ||
return errors.Wrap(err, "failed to open the raw stream connection") | ||
} | ||
connHalfCloser, ok := conn.(halfCloser) | ||
if !ok { | ||
return errors.New("the raw stream connection does not implement halfCloser") | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Seems like this will be passed on to the user. Maybe we can explain what this error is better? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Actually this type assertion should never fail and the error won't be printed There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe we should make the error a little more clear in this case? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. How about just panic here and |
||
} | ||
stdin2conn := make(chan error) | ||
conn2stdout := make(chan error) | ||
go func() { | ||
stdin2conn <- copier(connHalfCloser, &halfReadCloserWrapper{os.Stdin}, "stdin to stream") | ||
}() | ||
go func() { | ||
conn2stdout <- copier(&halfWriteCloserWrapper{os.Stdout}, connHalfCloser, "stream to stdout") | ||
}() | ||
select { | ||
case err = <-stdin2conn: | ||
if err != nil { | ||
return err | ||
} | ||
// wait for stdout | ||
err = <-conn2stdout | ||
case err = <-conn2stdout: | ||
// return immediately without waiting for stdin to be closed. | ||
// (stdin is never closed when tty) | ||
} | ||
return err | ||
} | ||
|
||
func copier(to halfWriteCloser, from halfReadCloser, debugDescription string) error { | ||
defer func() { | ||
if err := from.CloseRead(); err != nil { | ||
logrus.Errorf("error while CloseRead (%s): %v", debugDescription, err) | ||
} | ||
if err := to.CloseWrite(); err != nil { | ||
logrus.Errorf("error while CloseWrite (%s): %v", debugDescription, err) | ||
} | ||
}() | ||
if _, err := io.Copy(to, from); err != nil { | ||
return errors.Wrapf(err, "error while Copy (%s)", debugDescription) | ||
} | ||
return nil | ||
} | ||
|
||
type halfReadCloser interface { | ||
io.Reader | ||
CloseRead() error | ||
} | ||
|
||
type halfWriteCloser interface { | ||
io.Writer | ||
CloseWrite() error | ||
} | ||
|
||
type halfCloser interface { | ||
halfReadCloser | ||
halfWriteCloser | ||
} | ||
|
||
type halfReadCloserWrapper struct { | ||
io.ReadCloser | ||
} | ||
|
||
func (x *halfReadCloserWrapper) CloseRead() error { | ||
return x.Close() | ||
} | ||
|
||
type halfWriteCloserWrapper struct { | ||
io.WriteCloser | ||
} | ||
|
||
func (x *halfWriteCloserWrapper) CloseWrite() error { | ||
return x.Close() | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we should just add
WithDialer
instead of overriding client and removeWithHijackDialer
. Using it would create a new client with a transport pointing to the current dialer and set it to hijack as well.