Skip to content

Commit 761303e

Browse files
committed
Inject header command into proxy command
1 parent 673a745 commit 761303e

File tree

5 files changed

+51
-20
lines changed

5 files changed

+51
-20
lines changed

src/main/kotlin/com/coder/gateway/CoderGatewayConnectionProvider.kt

+1-1
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ class CoderGatewayConnectionProvider : GatewayConnectionProvider {
100100
cli.login(client.token)
101101

102102
indicator.text = "Configuring Coder CLI..."
103-
cli.configSsh(workspaces.flatMap { it.toAgentModels() })
103+
cli.configSsh(workspaces.flatMap { it.toAgentModels() }, settings.headerCommand)
104104

105105
// TODO: Ask for these if missing. Maybe we can reuse the second
106106
// step of the wizard? Could also be nice if we automatically used

src/main/kotlin/com/coder/gateway/sdk/CoderCLIManager.kt

+24-4
Original file line numberDiff line numberDiff line change
@@ -179,8 +179,9 @@ class CoderCLIManager @JvmOverloads constructor(
179179
/**
180180
* Configure SSH to use this binary.
181181
*/
182-
fun configSsh(workspaces: List<WorkspaceAgentModel>) {
183-
writeSSHConfig(modifySSHConfig(readSSHConfig(), workspaces))
182+
@JvmOverloads
183+
fun configSsh(workspaces: List<WorkspaceAgentModel>, headerCommand: String? = null) {
184+
writeSSHConfig(modifySSHConfig(readSSHConfig(), workspaces, headerCommand))
184185
}
185186

186187
/**
@@ -194,16 +195,35 @@ class CoderCLIManager @JvmOverloads constructor(
194195
}
195196
}
196197

198+
/**
199+
* Escape a command argument by wrapping it in double quotes and escaping
200+
* any double quotes in the argument. For example, echo "test" becomes
201+
* "echo \"test\"".
202+
*/
203+
private fun escape(s: String): String {
204+
return "\"" + s.replace("\"", "\\\"") + "\""
205+
}
206+
197207
/**
198208
* Given an existing SSH config modify it to add or remove the config for
199209
* this deployment and return the modified config or null if it does not
200210
* need to be modified.
201211
*/
202-
private fun modifySSHConfig(contents: String?, workspaces: List<WorkspaceAgentModel>): String? {
212+
private fun modifySSHConfig(
213+
contents: String?,
214+
workspaces: List<WorkspaceAgentModel>,
215+
headerCommand: String?,
216+
): String? {
203217
val host = getSafeHost(deploymentURL)
204218
val startBlock = "# --- START CODER JETBRAINS $host"
205219
val endBlock = "# --- END CODER JETBRAINS $host"
206220
val isRemoving = workspaces.isEmpty()
221+
val proxyArgs = listOfNotNull(
222+
escape(localBinaryPath.toString()),
223+
"--global-config", escape(coderConfigPath.toString()),
224+
if (!headerCommand.isNullOrBlank()) "--header-command" else null,
225+
if (!headerCommand.isNullOrBlank()) escape(headerCommand) else null,
226+
"ssh", "--stdio")
207227
val blockContent = workspaces.joinToString(
208228
System.lineSeparator(),
209229
startBlock + System.lineSeparator(),
@@ -212,7 +232,7 @@ class CoderCLIManager @JvmOverloads constructor(
212232
"""
213233
Host ${getHostName(deploymentURL, it)}
214234
HostName coder.${it.name}
215-
ProxyCommand "$localBinaryPath" --global-config "$coderConfigPath" ssh --stdio ${it.name}
235+
ProxyCommand ${proxyArgs.joinToString(" ")} ${it.name}
216236
ConnectTimeout 0
217237
StrictHostKeyChecking no
218238
UserKnownHostsFile /dev/null

src/main/kotlin/com/coder/gateway/views/steps/CoderWorkspacesStepView.kt

+1-1
Original file line numberDiff line numberDiff line change
@@ -614,7 +614,7 @@ class CoderWorkspacesStepView(val setNextButtonEnabled: (Boolean) -> Unit) : Cod
614614
poller?.cancel()
615615

616616
logger.info("Configuring Coder CLI...")
617-
cli.configSsh(tableOfWorkspaces.items)
617+
cli.configSsh(tableOfWorkspaces.items, settings.headerCommand)
618618

619619
// The config directory can be used to pull the URL and token in
620620
// order to query this workspace's status in other flows, for
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
# --- START CODER JETBRAINS test.coder.invalid
2+
Host coder-jetbrains--header--test.coder.invalid
3+
HostName coder.header
4+
ProxyCommand "/tmp/coder-gateway/test.coder.invalid/coder-linux-amd64" --global-config "/tmp/coder-gateway/test.coder.invalid/config" --header-command "my-header-command \"test\"" ssh --stdio header
5+
ConnectTimeout 0
6+
StrictHostKeyChecking no
7+
UserKnownHostsFile /dev/null
8+
LogLevel ERROR
9+
SetEnv CODER_SSH_SESSION_TYPE=JetBrains
10+
# --- END CODER JETBRAINS test.coder.invalid

src/test/groovy/CoderCLIManagerTest.groovy

+15-14
Original file line numberDiff line numberDiff line change
@@ -398,7 +398,7 @@ class CoderCLIManagerTest extends Specification {
398398
.replace("/tmp/coder-gateway/test.coder.invalid/coder-linux-amd64", ccm.localBinaryPath.toString())
399399

400400
when:
401-
ccm.configSsh(workspaces.collect { DataGen.workspace(it) })
401+
ccm.configSsh(workspaces.collect { DataGen.workspace(it) }, headerCommand)
402402

403403
then:
404404
sshConfigPath.toFile().text == expectedConf
@@ -410,19 +410,20 @@ class CoderCLIManagerTest extends Specification {
410410
sshConfigPath.toFile().text == Path.of("src/test/fixtures/inputs").resolve(remove + ".conf").toFile().text
411411

412412
where:
413-
workspaces | input | output | remove
414-
["foo", "bar"] | null | "multiple-workspaces" | "blank"
415-
["foo-bar"] | "blank" | "append-blank" | "blank"
416-
["foo-bar"] | "blank-newlines" | "append-blank-newlines" | "blank"
417-
["foo-bar"] | "existing-end" | "replace-end" | "no-blocks"
418-
["foo-bar"] | "existing-end-no-newline" | "replace-end-no-newline" | "no-blocks"
419-
["foo-bar"] | "existing-middle" | "replace-middle" | "no-blocks"
420-
["foo-bar"] | "existing-middle-and-unrelated" | "replace-middle-ignore-unrelated" | "no-related-blocks"
421-
["foo-bar"] | "existing-only" | "replace-only" | "blank"
422-
["foo-bar"] | "existing-start" | "replace-start" | "no-blocks"
423-
["foo-bar"] | "no-blocks" | "append-no-blocks" | "no-blocks"
424-
["foo-bar"] | "no-related-blocks" | "append-no-related-blocks" | "no-related-blocks"
425-
["foo-bar"] | "no-newline" | "append-no-newline" | "no-blocks"
413+
workspaces | input | output | remove | headerCommand
414+
["foo", "bar"] | null | "multiple-workspaces" | "blank" | null
415+
["foo-bar"] | "blank" | "append-blank" | "blank" | null
416+
["foo-bar"] | "blank-newlines" | "append-blank-newlines" | "blank" | null
417+
["foo-bar"] | "existing-end" | "replace-end" | "no-blocks" | null
418+
["foo-bar"] | "existing-end-no-newline" | "replace-end-no-newline" | "no-blocks" | null
419+
["foo-bar"] | "existing-middle" | "replace-middle" | "no-blocks" | null
420+
["foo-bar"] | "existing-middle-and-unrelated" | "replace-middle-ignore-unrelated" | "no-related-blocks" | null
421+
["foo-bar"] | "existing-only" | "replace-only" | "blank" | null
422+
["foo-bar"] | "existing-start" | "replace-start" | "no-blocks" | null
423+
["foo-bar"] | "no-blocks" | "append-no-blocks" | "no-blocks" | null
424+
["foo-bar"] | "no-related-blocks" | "append-no-related-blocks" | "no-related-blocks" | null
425+
["foo-bar"] | "no-newline" | "append-no-newline" | "no-blocks" | null
426+
["header"] | null | "header-command" | "blank" | "my-header-command \"test\""
426427
}
427428

428429
def "fails if config is malformed"() {

0 commit comments

Comments
 (0)