/
ssh.go
121 lines (107 loc) · 2.95 KB
/
ssh.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
package cmd
import (
"fmt"
"os"
"os/exec"
"os/user"
"path/filepath"
"github.com/spf13/cobra"
"golang.org/x/term"
"golang.org/x/xerrors"
"cdr.dev/coder-cli/coder-sdk"
"cdr.dev/coder-cli/pkg/clog"
)
var (
showInteractiveOutput = term.IsTerminal(int(os.Stdout.Fd()))
)
func sshCmd() *cobra.Command {
cmd := cobra.Command{
Use: "ssh [workspace_name] [<command [args...]>]",
Short: "Enter a shell of execute a command over SSH into a Coder workspace",
Args: shValidArgs,
Example: `coder ssh my-dev
coder ssh my-dev pwd`,
Aliases: []string{"sh"},
DisableFlagParsing: true,
DisableFlagsInUseLine: true,
RunE: shell,
}
return &cmd
}
func shell(cmd *cobra.Command, args []string) error {
ctx := cmd.Context()
client, err := newClient(ctx, true)
if err != nil {
return err
}
workspace, err := findWorkspace(ctx, client, args[0], coder.Me)
if err != nil {
return err
}
if workspace.LatestStat.ContainerStatus != coder.WorkspaceOn {
return clog.Error("workspace not available",
fmt.Sprintf("current status: \"%s\"", workspace.LatestStat.ContainerStatus),
clog.BlankLine,
clog.Tipf("use \"coder workspaces rebuild %s\" to rebuild this workspace", workspace.Name),
)
}
wp, err := client.WorkspaceProviderByID(ctx, workspace.ResourcePoolID)
if err != nil {
return err
}
if !wp.SSHEnabled {
return clog.Error("SSH is disabled on this Workspace")
}
usr, err := user.Current()
if err != nil {
return xerrors.Errorf("get user home directory: %w", err)
}
privateKeyFilepath := filepath.Join(usr.HomeDir, ".ssh", "coder_enterprise")
err = writeSSHKey(ctx, client, privateKeyFilepath)
if err != nil {
return err
}
binPath, err := binPath()
if err != nil {
return xerrors.Errorf("Failed to get executable path: %w", err)
}
ssh := exec.CommandContext(ctx,
"ssh", "-i"+privateKeyFilepath,
"-o"+fmt.Sprintf("ProxyCommand=%s", proxyCommand(binPath, workspace.Name, false)),
workspace.Name,
)
if len(args) > 1 {
ssh.Args = append(ssh.Args, args[1:]...)
}
ssh.Stderr = os.Stderr
ssh.Stdout = os.Stdout
ssh.Stdin = os.Stdin
err = ssh.Run()
var exitErr *exec.ExitError
if xerrors.As(err, &exitErr) {
os.Exit(exitErr.ExitCode())
return xerrors.New("unreachable")
}
return err
}
// special handling for the common case of "coder sh" input without a positional argument.
func shValidArgs(cmd *cobra.Command, args []string) error {
ctx := cmd.Context()
err := cobra.MinimumNArgs(1)(cmd, args)
if err != nil {
client, err := newClient(ctx, true)
if err != nil {
return clog.Error("missing [workspace_name] argument")
}
_, haystack, err := searchForWorkspace(ctx, client, "", coder.Me)
if err != nil {
return clog.Error("missing [workspace_name] argument",
fmt.Sprintf("specify one of %q", haystack),
clog.BlankLine,
clog.Tipf("run \"coder workspaces ls\" to view your workspaces"),
)
}
return clog.Error("missing [workspace_name] argument")
}
return nil
}