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

Commit 435677f

Browse files
committed
Add config-ssh command
1 parent f7d230b commit 435677f

File tree

3 files changed

+138
-5
lines changed

3 files changed

+138
-5
lines changed

cmd/coder/config_ssh.go

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
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 Enterprise 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+
64+
err = writeStr(cmd.filepath, currentConfiguration)
65+
if err != nil {
66+
flog.Fatal("failed to write to ssh config file %q: %v", cmd.filepath, err)
67+
}
68+
69+
return
70+
}
71+
72+
entClient := requireAuth()
73+
74+
me, err := entClient.Me()
75+
if err != nil {
76+
flog.Fatal("failed to fetch username: %v", err)
77+
}
78+
envs := getEnvs(entClient)
79+
80+
if startIndex == -1 || endIndex == -1 {
81+
newConfiguration := makeNewConfigs(me.Username, envs)
82+
83+
err = writeStr(cmd.filepath, currentConfiguration+newConfiguration)
84+
if err != nil {
85+
flog.Fatal("failed to write new configurations to ssh config file %q: %v", cmd.filepath, err)
86+
}
87+
88+
return
89+
}
90+
currentConfiguration = currentConfiguration[:startIndex-1] + currentConfiguration[endIndex+len(sshConfigEndToken)+1:]
91+
newConfiguration := makeNewConfigs(me.Username, envs)
92+
93+
err = writeStr(cmd.filepath, currentConfiguration+newConfiguration)
94+
if err != nil {
95+
flog.Fatal("failed to write new configurations to ssh config file %q: %v", cmd.filepath, err)
96+
}
97+
}
98+
99+
func makeNewConfigs(userName string, envs []entclient.Environment) string {
100+
newConfiguration := fmt.Sprintf("\n%s\n%s\n\n", sshConfigStartToken, sshConfigStartMessage)
101+
for _, env := range envs {
102+
newConfiguration += makeConfig(userName, env.Name)
103+
}
104+
newConfiguration += fmt.Sprintf("\n%s\n", sshConfigEndToken)
105+
106+
return newConfiguration
107+
}
108+
109+
func makeConfig(userName, envName string) string {
110+
return fmt.Sprintf(
111+
`Host coder:%s
112+
HostName %s
113+
User %s-%s
114+
Port 2222
115+
KeepAlive=yes
116+
ConnectTimeout=0
117+
`, envName, "MOCK-SSHPROXY-IP", userName, envName) // TODO: get real ssh proxy ip address
118+
}
119+
120+
func writeStr(filename, data string) error {
121+
return ioutil.WriteFile(filename, []byte(data), 0777)
122+
}
123+
124+
func readStr(filename string) (string, error) {
125+
contents, err := ioutil.ReadFile(filename)
126+
if err != nil {
127+
return "", err
128+
}
129+
return string(contents), nil
130+
}

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)