@@ -24,7 +24,11 @@ import javax.xml.bind.annotation.adapters.HexBinaryAdapter
24
24
/* *
25
25
* Manage the CLI for a single deployment.
26
26
*/
27
- class CoderCLIManager @JvmOverloads constructor(private val deploymentURL : URL , destinationDir : Path = getDataDir()) {
27
+ class CoderCLIManager @JvmOverloads constructor(
28
+ private val deploymentURL : URL ,
29
+ destinationDir : Path = getDataDir(),
30
+ private val sshConfigPath : Path = Path .of(System .getProperty("user.home")).resolve(".ssh/config"),
31
+ ) {
28
32
private var remoteBinaryUrl: URL
29
33
var localBinaryPath: Path
30
34
private var coderConfigPath: Path
@@ -163,10 +167,27 @@ class CoderCLIManager @JvmOverloads constructor(private val deploymentURL: URL,
163
167
/* *
164
168
* Configure SSH to use this binary.
165
169
*/
166
- fun configSsh (
167
- workspaces : List <WorkspaceAgentModel >,
168
- sshConfigPath : Path = Path .of(System .getProperty("user.home")).resolve(".ssh/config"),
169
- ) {
170
+ fun configSsh (workspaces : List <WorkspaceAgentModel >) {
171
+ writeSSHConfig(modifySSHConfig(readSSHConfig(), workspaces))
172
+ }
173
+
174
+ /* *
175
+ * Return the contents of the SSH config or null if it does not exist.
176
+ */
177
+ private fun readSSHConfig (): String? {
178
+ return try {
179
+ sshConfigPath.toFile().readText()
180
+ } catch (e: FileNotFoundException ) {
181
+ null
182
+ }
183
+ }
184
+
185
+ /* *
186
+ * Given an existing SSH config modify it to add or remove the config for
187
+ * this deployment and return the modified config or null if it does not
188
+ * need to be modified.
189
+ */
190
+ private fun modifySSHConfig (contents : String? , workspaces : List <WorkspaceAgentModel >): String? {
170
191
val host = getSafeHost(deploymentURL)
171
192
val startBlock = " # --- START CODER JETBRAINS $host "
172
193
val endBlock = " # --- END CODER JETBRAINS $host "
@@ -187,53 +208,68 @@ class CoderCLIManager @JvmOverloads constructor(private val deploymentURL: URL,
187
208
SetEnv CODER_SSH_SESSION_TYPE=JetBrains
188
209
""" .trimIndent().replace(" \n " , System .lineSeparator())
189
210
})
190
- Files .createDirectories(sshConfigPath.parent)
191
- try {
192
- val contents = sshConfigPath.toFile().readText()
193
- val start = " (\\ s*)$startBlock " .toRegex().find(contents)
194
- val end = " $endBlock (\\ s*)" .toRegex().find(contents)
195
- if (start == null && end == null && isRemoving) {
196
- logger.info(" Leaving $sshConfigPath alone since there are no workspaces and no config to remove" )
197
- } else if (start == null && end == null ) {
198
- logger.info(" Appending config to $sshConfigPath " )
199
- val toAppend = if (contents.isEmpty()) blockContent else listOf (
200
- contents,
201
- blockContent
202
- ).joinToString(System .lineSeparator())
203
- sshConfigPath.toFile().writeText(toAppend + System .lineSeparator())
204
- } else if (start == null ) {
205
- throw SSHConfigFormatException (" End block exists but no start block" )
206
- } else if (end == null ) {
207
- throw SSHConfigFormatException (" Start block exists but no end block" )
208
- } else if (start.range.first > end.range.first) {
209
- throw SSHConfigFormatException (" Start block found after end block" )
210
- } else if (isRemoving) {
211
- logger.info(" Removing config from $sshConfigPath " )
212
- sshConfigPath.toFile().writeText(
213
- listOf (
214
- contents.substring(0 , start.range.first),
215
- // Need to keep the trailing newline(s) if we are not at
216
- // the front of the file otherwise the before and after
217
- // lines would get joined.
218
- if (start.range.first > 0 ) end.groupValues[1 ] else " " ,
219
- contents.substring(end.range.last + 1 )
220
- ).joinToString(" " )
221
- )
222
- } else {
223
- logger.info(" Replacing config in $sshConfigPath " )
224
- sshConfigPath.toFile().writeText(
225
- listOf (
226
- contents.substring(0 , start.range.first),
227
- start.groupValues[1 ], // Leading newline(s).
228
- blockContent,
229
- end.groupValues[1 ], // Trailing newline(s).
230
- contents.substring(end.range.last + 1 )
231
- ).joinToString(" " )
232
- )
233
- }
234
- } catch (e: FileNotFoundException ) {
235
- logger.info(" Writing config to $sshConfigPath " )
236
- sshConfigPath.toFile().writeText(blockContent + System .lineSeparator())
211
+
212
+ if (contents == null ) {
213
+ logger.info(" No existing SSH config to modify" )
214
+ return blockContent + System .lineSeparator()
215
+ }
216
+
217
+ val start = " (\\ s*)$startBlock " .toRegex().find(contents)
218
+ val end = " $endBlock (\\ s*)" .toRegex().find(contents)
219
+
220
+ if (start == null && end == null && isRemoving) {
221
+ logger.info(" No workspaces and no existing config blocks to remove" )
222
+ return null
223
+ }
224
+
225
+ if (start == null && end == null ) {
226
+ logger.info(" Appending config block" )
227
+ val toAppend = if (contents.isEmpty()) blockContent else listOf (
228
+ contents,
229
+ blockContent
230
+ ).joinToString(System .lineSeparator())
231
+ return toAppend + System .lineSeparator()
232
+ }
233
+
234
+ if (start == null ) {
235
+ throw SSHConfigFormatException (" End block exists but no start block" )
236
+ }
237
+ if (end == null ) {
238
+ throw SSHConfigFormatException (" Start block exists but no end block" )
239
+ }
240
+ if (start.range.first > end.range.first) {
241
+ throw SSHConfigFormatException (" Start block found after end block" )
242
+ }
243
+
244
+ if (isRemoving) {
245
+ logger.info(" No workspaces; removing config block" )
246
+ return listOf (
247
+ contents.substring(0 , start.range.first),
248
+ // Need to keep the trailing newline(s) if we are not at the
249
+ // front of the file otherwise the before and after lines would
250
+ // get joined.
251
+ if (start.range.first > 0 ) end.groupValues[1 ] else " " ,
252
+ contents.substring(end.range.last + 1 )
253
+ ).joinToString(" " )
254
+ }
255
+
256
+ logger.info(" Replacing existing config block" )
257
+ return listOf (
258
+ contents.substring(0 , start.range.first),
259
+ start.groupValues[1 ], // Leading newline(s).
260
+ blockContent,
261
+ end.groupValues[1 ], // Trailing newline(s).
262
+ contents.substring(end.range.last + 1 )
263
+ ).joinToString(" " )
264
+ }
265
+
266
+ /* *
267
+ * Write the provided SSH config or do nothing if null.
268
+ */
269
+ private fun writeSSHConfig (contents : String? ) {
270
+ if (contents != null ) {
271
+ Files .createDirectories(sshConfigPath.parent)
272
+ sshConfigPath.toFile().writeText(contents)
237
273
}
238
274
}
239
275
0 commit comments