Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commitf39e1f4

Browse files
Use wildcard SSH config Host entries (#521)
* Use wildcard SSH config Host entriesThis simplifies the written SSH config and avoids the need to make anAPI request for every workspace the filter returns. This can removeminutes from the "Configuring Coder CLI..." step when the user hasaccess to many workspaces (for example, an admin who wants the option ofconnecting to anyone's workspace on a large deployment).Depends oncoder/coder#16088* changelog update---------Co-authored-by: Benjamin Peinhardt <61021968+bcpeinhardt@users.noreply.github.com>Co-authored-by: Benjamin <benjaminpeinhardt@gmail.com>
1 parent9705045 commitf39e1f4

File tree

6 files changed

+128
-44
lines changed

6 files changed

+128
-44
lines changed

‎CHANGELOG.md‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,10 @@
44

55
##Unreleased
66

7+
###Changed
8+
9+
- Simplifies the written SSH config and avoids the need to make an API request for every workspace the filter returns.
10+
711
##2.17.0 - 2025-01-27
812

913
###Added

‎src/main/kotlin/com/coder/gateway/cli/CoderCLIManager.kt‎

Lines changed: 84 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,7 @@ fun ensureCLI(
115115
data classFeatures(
116116
valdisableAutostart:Boolean =false,
117117
valreportWorkspaceUsage:Boolean =false,
118+
valwildcardSSH:Boolean =false,
118119
)
119120

120121
/**
@@ -285,37 +286,57 @@ class CoderCLIManager(
285286
}else {
286287
""
287288
}
289+
val sshOpts="""
290+
ConnectTimeout 0
291+
StrictHostKeyChecking no
292+
UserKnownHostsFile /dev/null
293+
LogLevel ERROR
294+
SetEnv CODER_SSH_SESSION_TYPE=JetBrains
295+
""".trimIndent()
288296
val blockContent=
297+
if (feats.wildcardSSH) {
298+
startBlock+System.lineSeparator()+
299+
"""
300+
Host${getHostPrefix()}--*
301+
ProxyCommand${proxyArgs.joinToString("")} --ssh-host-prefix${getHostPrefix()}-- %h
302+
""".trimIndent()
303+
.plus("\n"+ sshOpts.prependIndent(""))
304+
.plus(extraConfig)
305+
.plus("\n\n")
306+
.plus(
307+
"""
308+
Host${getHostPrefix()}-bg--*
309+
ProxyCommand${backgroundProxyArgs.joinToString("")} --ssh-host-prefix${getHostPrefix()}-bg-- %h
310+
""".trimIndent()
311+
.plus("\n"+ sshOpts.prependIndent(""))
312+
.plus(extraConfig),
313+
).replace("\n",System.lineSeparator())+
314+
System.lineSeparator()+ endBlock
315+
316+
}else {
289317
workspaceNames.joinToString(
290318
System.lineSeparator(),
291319
startBlock+System.lineSeparator(),
292320
System.lineSeparator()+ endBlock,
293321
transform= {
294322
"""
295-
Host${getHostName(deploymentURL,it.first, currentUser, it.second)}
323+
Host${getHostName(it.first, currentUser, it.second)}
296324
ProxyCommand${proxyArgs.joinToString("")}${getWorkspaceParts(it.first, it.second)}
297-
ConnectTimeout 0
298-
StrictHostKeyChecking no
299-
UserKnownHostsFile /dev/null
300-
LogLevel ERROR
301-
SetEnv CODER_SSH_SESSION_TYPE=JetBrains
302325
""".trimIndent()
326+
.plus("\n"+ sshOpts.prependIndent(""))
303327
.plus(extraConfig)
304328
.plus("\n")
305329
.plus(
306330
"""
307-
Host${getBackgroundHostName(deploymentURL,it.first, currentUser, it.second)}
331+
Host${getBackgroundHostName(it.first, currentUser, it.second)}
308332
ProxyCommand${backgroundProxyArgs.joinToString("")}${getWorkspaceParts(it.first, it.second)}
309-
ConnectTimeout 0
310-
StrictHostKeyChecking no
311-
UserKnownHostsFile /dev/null
312-
LogLevel ERROR
313-
SetEnv CODER_SSH_SESSION_TYPE=JetBrains
314333
""".trimIndent()
334+
.plus("\n"+ sshOpts.prependIndent(""))
315335
.plus(extraConfig),
316336
).replace("\n",System.lineSeparator())
317337
},
318338
)
339+
}
319340

320341
if (contents==null) {
321342
logger.info("No existing SSH config to modify")
@@ -489,40 +510,53 @@ class CoderCLIManager(
489510
Features(
490511
disableAutostart= version>=SemVer(2,5,0),
491512
reportWorkspaceUsage= version>=SemVer(2,13,0),
513+
wildcardSSH= version>=SemVer(2,19,0),
492514
)
493515
}
494516
}
495517

518+
/*
519+
* This function returns the ssh-host-prefix used for Host entries.
520+
*/
521+
fungetHostPrefix():String=
522+
"coder-jetbrains-${deploymentURL.safeHost()}"
523+
524+
/**
525+
* This function returns the ssh host name generated for connecting to the workspace.
526+
*/
527+
fungetHostName(
528+
workspace:Workspace,
529+
currentUser:User,
530+
agent:WorkspaceAgent,
531+
):String=
532+
if (features.wildcardSSH) {
533+
"${getHostPrefix()}--${workspace.ownerName}--${workspace.name}.${agent.name}"
534+
}else {
535+
// For a user's own workspace, we use the old syntax without a username for backwards compatibility,
536+
// since the user might have recent connections that still use the old syntax.
537+
if (currentUser.username== workspace.ownerName) {
538+
"coder-jetbrains--${workspace.name}.${agent.name}--${deploymentURL.safeHost()}"
539+
}else {
540+
"coder-jetbrains--${workspace.ownerName}--${workspace.name}.${agent.name}--${deploymentURL.safeHost()}"
541+
}
542+
}
543+
544+
fungetBackgroundHostName(
545+
workspace:Workspace,
546+
currentUser:User,
547+
agent:WorkspaceAgent,
548+
):String=
549+
if (features.wildcardSSH) {
550+
"${getHostPrefix()}-bg--${workspace.ownerName}--${workspace.name}.${agent.name}"
551+
}else {
552+
getHostName(workspace, currentUser, agent)+"--bg"
553+
}
554+
496555
companionobject {
497556
val logger=Logger.getInstance(CoderCLIManager::class.java.simpleName)
498557

499558
privateval tokenRegex="--token [^ ]+".toRegex()
500559

501-
/**
502-
* This function returns the ssh host name generated for connecting to the workspace.
503-
*/
504-
@JvmStatic
505-
fungetHostName(
506-
url:URL,
507-
workspace:Workspace,
508-
currentUser:User,
509-
agent:WorkspaceAgent,
510-
):String=
511-
// For a user's own workspace, we use the old syntax without a username for backwards compatibility,
512-
// since the user might have recent connections that still use the old syntax.
513-
if (currentUser.username== workspace.ownerName) {
514-
"coder-jetbrains--${workspace.name}.${agent.name}--${url.safeHost()}"
515-
}else {
516-
"coder-jetbrains--${workspace.ownerName}--${workspace.name}.${agent.name}--${url.safeHost()}"
517-
}
518-
519-
fungetBackgroundHostName(
520-
url:URL,
521-
workspace:Workspace,
522-
currentUser:User,
523-
agent:WorkspaceAgent,
524-
):String= getHostName(url, workspace, currentUser, agent)+"--bg"
525-
526560
/**
527561
* This function returns the identifier for the workspace to pass to the
528562
* coder ssh proxy command.
@@ -536,6 +570,18 @@ class CoderCLIManager(
536570
@JvmStatic
537571
fungetBackgroundHostName(
538572
hostname:String,
539-
):String= hostname+"--bg"
573+
):String {
574+
val parts= hostname.split("--").toMutableList()
575+
if (parts.size<2) {
576+
throwSSHConfigFormatException("Invalid hostname:$hostname")
577+
}
578+
// non-wildcard case
579+
if (parts[0]=="coder-jetbrains") {
580+
return hostname+"--bg"
581+
}
582+
// wildcard case
583+
parts[0]+="-bg"
584+
return parts.joinToString("--")
585+
}
540586
}
541587
}

‎src/main/kotlin/com/coder/gateway/util/LinkHandler.kt‎

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,11 @@ open class LinkHandler(
111111
}
112112

113113
indicator?.invoke("Configuring Coder CLI...")
114-
cli.configSsh(workspacesAndAgents= client.withAgents(workspaces), currentUser= client.me)
114+
if (cli.features.wildcardSSH) {
115+
cli.configSsh(workspacesAndAgents= emptySet(), currentUser= client.me)
116+
}else {
117+
cli.configSsh(workspacesAndAgents= client.withAgents(workspaces), currentUser= client.me)
118+
}
115119

116120
val openDialog=
117121
parameters.ideProductCode().isNullOrBlank()||
@@ -127,7 +131,7 @@ open class LinkHandler(
127131
verifyDownloadLink(parameters)
128132
WorkspaceProjectIDE.fromInputs(
129133
name=CoderCLIManager.getWorkspaceParts(workspace, agent),
130-
hostname=CoderCLIManager.getHostName(deploymentURL.toURL(), workspace, client.me, agent),
134+
hostname=CoderCLIManager(deploymentURL.toURL(),settings).getHostName(workspace, client.me, agent),
131135
projectPath= parameters.folder(),
132136
ideProductCode= parameters.ideProductCode(),
133137
ideBuildNumber= parameters.ideBuildNumber(),

‎src/main/kotlin/com/coder/gateway/views/steps/CoderWorkspaceProjectIDEStepView.kt‎

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -208,7 +208,11 @@ class CoderWorkspaceProjectIDEStepView(
208208
logger.info("Configuring Coder CLI...")
209209
cbIDE.renderer=IDECellRenderer("Configuring Coder CLI...")
210210
withContext(Dispatchers.IO) {
211-
data.cliManager.configSsh(data.client.withAgents(data.workspaces), data.client.me)
211+
if (data.cliManager.features.wildcardSSH) {
212+
data.cliManager.configSsh(emptySet(), data.client.me)
213+
}else {
214+
data.cliManager.configSsh(data.client.withAgents(data.workspaces), data.client.me)
215+
}
212216
}
213217

214218
val ides=
@@ -223,7 +227,7 @@ class CoderWorkspaceProjectIDEStepView(
223227
}else {
224228
IDECellRenderer(CoderGatewayBundle.message("gateway.connector.view.coder.connect-ssh"))
225229
}
226-
val executor= createRemoteExecutor(CoderCLIManager.getBackgroundHostName(data.client.url,data.workspace, data.client.me, data.agent))
230+
val executor= createRemoteExecutor(CoderCLIManager(data.client.url).getBackgroundHostName(data.workspace, data.client.me, data.agent))
227231

228232
if (ComponentValidator.getInstance(tfProject).isEmpty) {
229233
logger.info("Installing remote path validator...")
@@ -428,7 +432,7 @@ class CoderWorkspaceProjectIDEStepView(
428432
overridefundata():WorkspaceProjectIDE= withoutNull(cbIDE.selectedItem, state) { selectedIDE, state->
429433
selectedIDE.withWorkspaceProject(
430434
name=CoderCLIManager.getWorkspaceParts(state.workspace, state.agent),
431-
hostname=CoderCLIManager.getHostName(state.client.url,state.workspace, state.client.me, state.agent),
435+
hostname=CoderCLIManager(state.client.url).getHostName(state.workspace, state.client.me, state.agent),
432436
projectPath= tfProject.text,
433437
deploymentURL= state.client.url,
434438
)
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
# --- START CODER JETBRAINS test.coder.invalid
2+
Host coder-jetbrains-test.coder.invalid--*
3+
ProxyCommand /tmp/coder-gateway/test.coder.invalid/coder-linux-amd64 --global-config /tmp/coder-gateway/test.coder.invalid/config --url https://test.coder.invalid ssh --stdio --ssh-host-prefix coder-jetbrains-test.coder.invalid-- %h
4+
ConnectTimeout 0
5+
StrictHostKeyChecking no
6+
UserKnownHostsFile /dev/null
7+
LogLevel ERROR
8+
SetEnv CODER_SSH_SESSION_TYPE=JetBrains
9+
10+
Host coder-jetbrains-test.coder.invalid-bg--*
11+
ProxyCommand /tmp/coder-gateway/test.coder.invalid/coder-linux-amd64 --global-config /tmp/coder-gateway/test.coder.invalid/config --url https://test.coder.invalid ssh --stdio --ssh-host-prefix coder-jetbrains-test.coder.invalid-bg-- %h
12+
ConnectTimeout 0
13+
StrictHostKeyChecking no
14+
UserKnownHostsFile /dev/null
15+
LogLevel ERROR
16+
SetEnv CODER_SSH_SESSION_TYPE=JetBrains
17+
# --- END CODER JETBRAINS test.coder.invalid

‎src/test/kotlin/com/coder/gateway/cli/CoderCLIManagerTest.kt‎

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -419,6 +419,15 @@ internal class CoderCLIManagerTest {
419419
output="multiple-agents",
420420
remove="blank",
421421
),
422+
SSHTest(
423+
listOf(workspace),
424+
input=null,
425+
output="wildcard",
426+
remove="blank",
427+
features=Features(
428+
wildcardSSH=true,
429+
),
430+
),
422431
)
423432

424433
val newlineRe="\r?\n".toRegex()
@@ -804,7 +813,7 @@ internal class CoderCLIManagerTest {
804813
listOf(
805814
Pair("2.5.0",Features(true)),
806815
Pair("2.13.0",Features(true,true)),
807-
Pair("4.9.0",Features(true,true)),
816+
Pair("4.9.0",Features(true,true,true)),
808817
Pair("2.4.9",Features(false)),
809818
Pair("1.0.1",Features(false)),
810819
)

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp