@@ -11,162 +11,165 @@ import (
11
11
"strings"
12
12
"time"
13
13
14
- "github.com/spf13/pflag"
15
- "go.coder.com/cli"
16
- "go.coder.com/flog"
17
-
18
14
"cdr.dev/coder-cli/internal/config"
19
15
"cdr.dev/coder-cli/internal/entclient"
20
- )
16
+ "github.com/urfave/cli"
21
17
22
- var (
23
- privateKeyFilepath = filepath .Join (os .Getenv ("HOME" ), ".ssh" , "coder_enterprise" )
18
+ "go.coder.com/flog"
24
19
)
25
20
26
- type configSSHCmd struct {
27
- filepath string
28
- remove bool
29
-
30
- startToken , startMessage , endToken string
31
- }
32
-
33
- func (cmd * configSSHCmd ) Spec () cli.CommandSpec {
34
- return cli.CommandSpec {
35
- Name : "config-ssh" ,
36
- Usage : "" ,
37
- Desc : "add your Coder Enterprise environments to ~/.ssh/config" ,
21
+ func makeConfigSSHCmd () cli.Command {
22
+ var (
23
+ configpath string
24
+ remove = false
25
+ )
26
+
27
+ return cli.Command {
28
+ Name : "config-ssh" ,
29
+ Usage : "Configure SSH to access Coder environments" ,
30
+ Description : "Inject the proper OpenSSH configuration into your local SSH config file." ,
31
+ Action : configSSH (& configpath , & remove ),
32
+ Flags : []cli.Flag {
33
+ cli.StringFlag {
34
+ Name : "filepath" ,
35
+ Usage : "overide the default path of your ssh config file" ,
36
+ Value : filepath .Join (os .Getenv ("HOME" ), ".ssh" , "config" ),
37
+ TakesFile : true ,
38
+ Destination : & configpath ,
39
+ },
40
+ cli.BoolFlag {
41
+ Name : "remove" ,
42
+ Usage : "remove the auto-generated Coder Enterprise ssh config" ,
43
+ Destination : & remove ,
44
+ },
45
+ },
38
46
}
39
47
}
40
48
41
- func (cmd * configSSHCmd ) RegisterFlags (fl * pflag.FlagSet ) {
42
- fl .BoolVar (& cmd .remove , "remove" , false , "remove the auto-generated Coder Enterprise ssh config" )
43
- home := os .Getenv ("HOME" )
44
- defaultPath := filepath .Join (home , ".ssh" , "config" )
45
- fl .StringVar (& cmd .filepath , "config-path" , defaultPath , "overide the default path of your ssh config file" )
46
-
47
- cmd .startToken = "# ------------START-CODER-ENTERPRISE-----------"
48
- cmd .startMessage = `# The following has been auto-generated by "coder config-ssh"
49
+ func configSSH (filepath * string , remove * bool ) func (c * cli.Context ) {
50
+ startToken := "# ------------START-CODER-ENTERPRISE-----------"
51
+ startMessage := `# The following has been auto-generated by "coder config-ssh"
49
52
# to make accessing your Coder Enterprise environments easier.
50
53
#
51
54
# To remove this blob, run:
52
55
#
53
56
# coder config-ssh --remove
54
57
#
55
58
# You should not hand-edit this section, unless you are deleting it.`
56
- cmd .endToken = "# ------------END-CODER-ENTERPRISE------------"
57
- }
58
-
59
- func (cmd * configSSHCmd ) Run (fl * pflag.FlagSet ) {
60
- ctx , cancel := context .WithCancel (context .Background ())
61
- defer cancel ()
59
+ endToken := "# ------------END-CODER-ENTERPRISE------------"
60
+
61
+ return func (c * cli.Context ) {
62
+ ctx , cancel := context .WithCancel (context .Background ())
63
+ defer cancel ()
64
+
65
+ currentConfig , err := readStr (* filepath )
66
+ if os .IsNotExist (err ) {
67
+ // SSH configs are not always already there.
68
+ currentConfig = ""
69
+ } else if err != nil {
70
+ flog .Fatal ("failed to read ssh config file %q: %v" , filepath , err )
71
+ }
62
72
63
- currentConfig , err := readStr (cmd .filepath )
64
- if os .IsNotExist (err ) {
65
- // SSH configs are not always already there.
66
- currentConfig = ""
67
- } else if err != nil {
68
- flog .Fatal ("failed to read ssh config file %q: %v" , cmd .filepath , err )
69
- }
73
+ startIndex := strings .Index (currentConfig , startToken )
74
+ endIndex := strings .Index (currentConfig , endToken )
70
75
71
- startIndex := strings .Index (currentConfig , cmd .startToken )
72
- endIndex := strings .Index (currentConfig , cmd .endToken )
76
+ if * remove {
77
+ if startIndex == - 1 || endIndex == - 1 {
78
+ flog .Fatal ("the Coder Enterprise ssh configuration section could not be safely deleted or does not exist" )
79
+ }
80
+ currentConfig = currentConfig [:startIndex - 1 ] + currentConfig [endIndex + len (endToken )+ 1 :]
73
81
74
- if cmd .remove {
75
- if startIndex == - 1 || endIndex == - 1 {
76
- flog .Fatal ("the Coder Enterprise ssh configuration section could not be safely deleted or does not exist" )
77
- }
78
- currentConfig = currentConfig [:startIndex - 1 ] + currentConfig [endIndex + len (cmd .endToken )+ 1 :]
82
+ err = writeStr (* filepath , currentConfig )
83
+ if err != nil {
84
+ flog .Fatal ("failed to write to ssh config file %q: %v" , * filepath , err )
85
+ }
79
86
80
- err = writeStr (cmd .filepath , currentConfig )
81
- if err != nil {
82
- flog .Fatal ("failed to write to ssh config file %q: %v" , cmd .filepath , err )
87
+ return
83
88
}
84
89
85
- return
86
- }
90
+ entClient := requireAuth ()
87
91
88
- entClient := requireAuth ()
92
+ sshAvailable := isSSHAvailable (ctx )
93
+ if ! sshAvailable {
94
+ flog .Fatal ("SSH is disabled or not available for your Coder Enterprise deployment." )
95
+ }
89
96
90
- sshAvailable := cmd . ensureSSHAvailable ( ctx )
91
- if ! sshAvailable {
92
- flog .Fatal ("SSH is disabled or not available for your Coder Enterprise deployment." )
93
- }
97
+ me , err := entClient . Me ( )
98
+ if err != nil {
99
+ flog .Fatal ("failed to fetch username: %v" , err )
100
+ }
94
101
95
- me , err := entClient .Me ()
96
- if err != nil {
97
- flog .Fatal ("failed to fetch username: %v" , err )
98
- }
102
+ envs := getEnvs (entClient )
103
+ if len (envs ) < 1 {
104
+ flog .Fatal ("no environments found" )
105
+ }
106
+ newConfig , err := makeNewConfigs (me .Username , envs , startToken , startMessage , endToken )
107
+ if err != nil {
108
+ flog .Fatal ("failed to make new ssh configurations: %v" , err )
109
+ }
99
110
100
- envs := getEnvs (entClient )
101
- if len (envs ) < 1 {
102
- flog .Fatal ("no environments found" )
103
- }
104
- newConfig , err := cmd .makeNewConfigs (me .Username , envs )
105
- if err != nil {
106
- flog .Fatal ("failed to make new ssh configurations: %v" , err )
107
- }
111
+ // if we find the old config, remove those chars from the string
112
+ if startIndex != - 1 && endIndex != - 1 {
113
+ currentConfig = currentConfig [:startIndex - 1 ] + currentConfig [endIndex + len (endToken )+ 1 :]
114
+ }
108
115
109
- // if we find the old config, remove those chars from the string
110
- if startIndex != - 1 && endIndex != - 1 {
111
- currentConfig = currentConfig [:startIndex - 1 ] + currentConfig [endIndex + len (cmd .endToken )+ 1 :]
112
- }
116
+ err = writeStr (* filepath , currentConfig + newConfig )
117
+ if err != nil {
118
+ flog .Fatal ("failed to write new configurations to ssh config file %q: %v" , filepath , err )
119
+ }
120
+ err = writeSSHKey (ctx , entClient )
121
+ if err != nil {
122
+ flog .Fatal ("failed to fetch and write ssh key: %v" , err )
123
+ }
113
124
114
- err = writeStr (cmd .filepath , currentConfig + newConfig )
115
- if err != nil {
116
- flog .Fatal ("failed to write new configurations to ssh config file %q: %v" , cmd .filepath , err )
117
- }
118
- err = writeSSHKey (ctx , entClient )
119
- if err != nil {
120
- flog .Fatal ("failed to fetch and write ssh key: %v" , err )
125
+ fmt .Printf ("An auto-generated ssh config was written to %q\n " , * filepath )
126
+ fmt .Printf ("Your private ssh key was written to %q\n " , privateKeyFilepath )
127
+ fmt .Println ("You should now be able to ssh into your environment" )
128
+ fmt .Printf ("For example, try running\n \n \t $ ssh coder.%s\n \n " , envs [0 ].Name )
121
129
}
122
-
123
- fmt .Printf ("An auto-generated ssh config was written to %q\n " , cmd .filepath )
124
- fmt .Printf ("Your private ssh key was written to %q\n " , privateKeyFilepath )
125
- fmt .Println ("You should now be able to ssh into your environment" )
126
- fmt .Printf ("For example, try running\n \n \t $ ssh coder.%s\n \n " , envs [0 ].Name )
127
130
}
128
131
132
+ var (
133
+ privateKeyFilepath = filepath .Join (os .Getenv ("HOME" ), ".ssh" , "coder_enterprise" )
134
+ )
135
+
129
136
func writeSSHKey (ctx context.Context , client * entclient.Client ) error {
130
137
key , err := client .SSHKey ()
131
138
if err != nil {
132
139
return err
133
140
}
134
- err = ioutil .WriteFile (privateKeyFilepath , []byte (key .PrivateKey ), 0400 )
135
- if err != nil {
136
- return err
137
- }
138
- return nil
141
+ return ioutil .WriteFile (privateKeyFilepath , []byte (key .PrivateKey ), 0400 )
139
142
}
140
143
141
- func ( cmd * configSSHCmd ) makeNewConfigs (userName string , envs []entclient.Environment ) (string , error ) {
144
+ func makeNewConfigs (userName string , envs []entclient.Environment , startToken , startMsg , endToken string ) (string , error ) {
142
145
hostname , err := configuredHostname ()
143
146
if err != nil {
144
147
return "" , nil
145
148
}
146
149
147
- newConfig := fmt .Sprintf ("\n %s\n %s\n \n " , cmd . startToken , cmd . startMessage )
150
+ newConfig := fmt .Sprintf ("\n %s\n %s\n \n " , startToken , startMsg )
148
151
for _ , env := range envs {
149
- newConfig += cmd . makeConfig (hostname , userName , env .Name )
152
+ newConfig += makeSSHConfig (hostname , userName , env .Name )
150
153
}
151
- newConfig += fmt .Sprintf ("\n %s\n " , cmd . endToken )
154
+ newConfig += fmt .Sprintf ("\n %s\n " , endToken )
152
155
153
156
return newConfig , nil
154
157
}
155
158
156
- func ( cmd * configSSHCmd ) makeConfig (host , userName , envName string ) string {
159
+ func makeSSHConfig (host , userName , envName string ) string {
157
160
return fmt .Sprintf (
158
161
`Host coder.%s
159
- HostName %s
160
- User %s-%s
161
- StrictHostKeyChecking no
162
- ConnectTimeout=0
163
- IdentityFile=%s
164
- ServerAliveInterval 60
165
- ServerAliveCountMax 3
162
+ HostName %s
163
+ User %s-%s
164
+ StrictHostKeyChecking no
165
+ ConnectTimeout=0
166
+ IdentityFile=%s
167
+ ServerAliveInterval 60
168
+ ServerAliveCountMax 3
166
169
` , envName , host , userName , envName , privateKeyFilepath )
167
170
}
168
171
169
- func ( cmd * configSSHCmd ) ensureSSHAvailable (ctx context.Context ) bool {
172
+ func isSSHAvailable (ctx context.Context ) bool {
170
173
ctx , cancel := context .WithTimeout (ctx , 3 * time .Second )
171
174
defer cancel ()
172
175
0 commit comments