feat: Add config-ssh command (#735)

* feat: Add config-ssh command

Closes #254 and #499.

* Fix Windows support
This commit is contained in:
Kyle Carberry
2022-03-30 17:59:54 -05:00
committed by GitHub
parent 6ab1a681c4
commit 6612e3c9c7
29 changed files with 554 additions and 115 deletions
+69 -5
View File
@@ -2,18 +2,28 @@ package cli
import (
"context"
"io"
"net"
"os"
"time"
"github.com/mattn/go-isatty"
"github.com/pion/webrtc/v3"
"github.com/spf13/cobra"
gossh "golang.org/x/crypto/ssh"
"golang.org/x/xerrors"
"github.com/coder/coder/cli/cliflag"
"github.com/coder/coder/cli/cliui"
"github.com/coder/coder/coderd/database"
"github.com/coder/coder/codersdk"
"golang.org/x/crypto/ssh/terminal"
)
func ssh() *cobra.Command {
var (
stdio bool
)
cmd := &cobra.Command{
Use: "ssh <workspace> [resource]",
RunE: func(cmd *cobra.Command, args []string) error {
@@ -25,8 +35,11 @@ func ssh() *cobra.Command {
if err != nil {
return err
}
if workspace.LatestBuild.Transition != database.WorkspaceTransitionStart {
return xerrors.New("workspace must be in start transition to ssh")
}
if workspace.LatestBuild.Job.CompletedAt == nil {
err = cliui.WorkspaceBuild(cmd, client, workspace.LatestBuild.ID, workspace.CreatedAt)
err = cliui.WorkspaceBuild(cmd.Context(), cmd.ErrOrStderr(), client, workspace.LatestBuild.ID, workspace.CreatedAt)
if err != nil {
return err
}
@@ -67,7 +80,9 @@ func ssh() *cobra.Command {
}
return xerrors.Errorf("no sshable agent with address %q: %+v", resourceAddress, resourceKeys)
}
err = cliui.Agent(cmd, cliui.AgentOptions{
// OpenSSH passes stderr directly to the calling TTY.
// This is required in "stdio" mode so a connecting indicator can be displayed.
err = cliui.Agent(cmd.Context(), cmd.ErrOrStderr(), cliui.AgentOptions{
WorkspaceName: workspace.Name,
Fetch: func(ctx context.Context) (codersdk.WorkspaceResource, error) {
return client.WorkspaceResource(ctx, resource.ID)
@@ -84,6 +99,17 @@ func ssh() *cobra.Command {
return err
}
defer conn.Close()
if stdio {
rawSSH, err := conn.SSH()
if err != nil {
return err
}
go func() {
_, _ = io.Copy(cmd.OutOrStdout(), rawSSH)
}()
_, _ = io.Copy(rawSSH, cmd.InOrStdin())
return nil
}
sshClient, err := conn.SSHClient()
if err != nil {
return err
@@ -94,9 +120,17 @@ func ssh() *cobra.Command {
return err
}
err = sshSession.RequestPty("xterm-256color", 128, 128, gossh.TerminalModes{
gossh.OCRNL: 1,
})
if isatty.IsTerminal(os.Stdout.Fd()) {
state, err := terminal.MakeRaw(int(os.Stdin.Fd()))
if err != nil {
return err
}
defer func() {
_ = terminal.Restore(int(os.Stdin.Fd()), state)
}()
}
err = sshSession.RequestPty("xterm-256color", 128, 128, gossh.TerminalModes{})
if err != nil {
return err
}
@@ -115,6 +149,36 @@ func ssh() *cobra.Command {
return nil
},
}
cliflag.BoolVarP(cmd.Flags(), &stdio, "stdio", "", "CODER_SSH_STDIO", false, "Specifies whether to emit SSH output over stdin/stdout.")
return cmd
}
type stdioConn struct {
io.Reader
io.Writer
}
func (*stdioConn) Close() (err error) {
return nil
}
func (*stdioConn) LocalAddr() net.Addr {
return nil
}
func (*stdioConn) RemoteAddr() net.Addr {
return nil
}
func (*stdioConn) SetDeadline(_ time.Time) error {
return nil
}
func (*stdioConn) SetReadDeadline(_ time.Time) error {
return nil
}
func (*stdioConn) SetWriteDeadline(_ time.Time) error {
return nil
}