Skip to content
This repository was archived by the owner on Aug 30, 2024. It is now read-only.

Commit 56f94b5

Browse files
committed
Add config-ssh command
1 parent f7d230b commit 56f94b5

File tree

3 files changed

+132
-5
lines changed

3 files changed

+132
-5
lines changed

cmd/coder/config_ssh.go

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
package main
2+
3+
import (
4+
"fmt"
5+
"io/ioutil"
6+
"os"
7+
"path/filepath"
8+
"strings"
9+
10+
"cdr.dev/coder-cli/internal/entclient"
11+
"github.com/spf13/pflag"
12+
13+
"go.coder.com/cli"
14+
"go.coder.com/flog"
15+
)
16+
17+
const (
18+
sshConfigStartToken = "# ------------START-CODER-ENTERPRISE-----------"
19+
sshConfigStartMessage = `# The following has been auto-generated by "coder config-ssh"
20+
# to make accessing your Coder Environments easier.
21+
#
22+
# To remove this blob, run:
23+
#
24+
# coder config-ssh --remove
25+
#
26+
# You should not hand-edit this section, unless you are deleting it.`
27+
sshConfigEndToken = "# ------------END-CODER-ENTERPRISE------------"
28+
)
29+
30+
type configSSHCmd struct {
31+
filepath string
32+
remove bool
33+
}
34+
35+
func (cmd *configSSHCmd) Spec() cli.CommandSpec {
36+
return cli.CommandSpec{
37+
Name: "config-ssh",
38+
Usage: "",
39+
Desc: "adds your Coder Enterprise environments to ~/.ssh/config",
40+
}
41+
}
42+
43+
func (cmd *configSSHCmd) RegisterFlags(fl *pflag.FlagSet) {
44+
fl.BoolVar(&cmd.remove, "remove", false, "remove the auto-generated Coder Enterprise ssh config")
45+
defaultPath := filepath.Join(os.Getenv("HOME"), ".ssh", "config")
46+
fl.StringVar(&cmd.filepath, "config-path", defaultPath, "overide the default path of your ssh config file")
47+
}
48+
49+
func (cmd *configSSHCmd) Run(fl *pflag.FlagSet) {
50+
currentConfiguration, err := readStr(cmd.filepath)
51+
if err != nil {
52+
flog.Fatal("failed to read ssh config file %q: %v", cmd.filepath, err)
53+
}
54+
55+
startIndex := strings.Index(currentConfiguration, sshConfigStartToken)
56+
endIndex := strings.Index(currentConfiguration, sshConfigEndToken)
57+
58+
if cmd.remove {
59+
if startIndex == -1 || endIndex == -1 {
60+
flog.Fatal("the Coder Enterprise ssh configuration section could not be safely deleted or does not exist.")
61+
}
62+
currentConfiguration = currentConfiguration[:startIndex-1] + currentConfiguration[endIndex+len(sshConfigEndToken)+1:]
63+
err = writeStr(cmd.filepath, currentConfiguration)
64+
if err != nil {
65+
flog.Fatal("failed to write to ssh config file %q: %v", cmd.filepath, err)
66+
}
67+
os.Exit(0)
68+
}
69+
70+
entClient := requireAuth()
71+
72+
me, err := entClient.Me()
73+
if err != nil {
74+
flog.Fatal("failed to fetch username: %v", err)
75+
}
76+
envs := getEnvs(entClient)
77+
78+
if startIndex == -1 || endIndex == -1 {
79+
newConfiguration := makeNewConfigs(me.Username, envs)
80+
err = writeStr(cmd.filepath, currentConfiguration+newConfiguration)
81+
if err != nil {
82+
flog.Fatal("failed to write new configurations to ssh config file %q: %v", cmd.filepath, err)
83+
}
84+
return
85+
}
86+
currentConfiguration = currentConfiguration[:startIndex-1] + currentConfiguration[endIndex+len(sshConfigEndToken)+1:]
87+
newConfiguration := makeNewConfigs(me.Username, envs)
88+
err = writeStr(cmd.filepath, currentConfiguration+newConfiguration)
89+
if err != nil {
90+
flog.Fatal("failed to write new configurations to ssh config file %q: %v", cmd.filepath, err)
91+
}
92+
}
93+
94+
func makeNewConfigs(userName string, envs []entclient.Environment) string {
95+
newConfiguration := fmt.Sprintf("\n%s\n%s\n\n", sshConfigStartToken, sshConfigStartMessage)
96+
for _, env := range envs {
97+
newConfiguration += makeConfig(userName, env.Name)
98+
}
99+
newConfiguration += fmt.Sprintf("\n%s\n", sshConfigEndToken)
100+
return newConfiguration
101+
}
102+
103+
func makeConfig(userName, envName string) string {
104+
return fmt.Sprintf(
105+
`Host coder:%s
106+
HostName %s
107+
User %s-%s
108+
Port 2222
109+
KeepAlive=yes
110+
ConnectTimeout=0
111+
`, envName, "MOCK-SSHPROXY-IP", userName, envName) // TODO: get real ssh proxy ip address
112+
}
113+
114+
func writeStr(filename, data string) error {
115+
return ioutil.WriteFile(filename, []byte(data), 0777)
116+
}
117+
118+
func readStr(filename string) (string, error) {
119+
contents, err := ioutil.ReadFile(filename)
120+
if err != nil {
121+
return "", err
122+
}
123+
return string(contents), nil
124+
}

cmd/coder/main.go

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
package main
22

33
import (
4-
"github.com/spf13/pflag"
5-
"go.coder.com/cli"
64
"log"
7-
"os"
85
"net/http"
96
_ "net/http/pprof"
7+
"os"
8+
9+
"github.com/spf13/pflag"
10+
"go.coder.com/cli"
1011
)
1112

1213
type rootCmd struct {
@@ -32,6 +33,7 @@ func (r *rootCmd) Subcommands() []cli.Command {
3233
&shellCmd{},
3334
&syncCmd{},
3435
&urlCmd{},
36+
&configSSHCmd{},
3537
}
3638
}
3739

internal/entclient/me.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
package entclient
22

33
type User struct {
4-
ID string `json:"id"`
5-
Email string `json:"email"`
4+
ID string `json:"id"`
5+
Email string `json:"email"`
6+
Username string `json:"username"`
67
}
78

89
func (c Client) Me() (*User, error) {

0 commit comments

Comments
 (0)