Skip to content

Commit fc241b8

Browse files
committed
Add setting for --log-dir flag
1 parent dc93f2d commit fc241b8

File tree

9 files changed

+115
-25
lines changed

9 files changed

+115
-25
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@
1515
### Added
1616

1717
- Extra logging around the IDE spawn to help debugging.
18+
- Add setting to enable logging connection diagnostics from the Coder CLI for
19+
debugging connectivity issues.
1820

1921
## 2.13.0 - 2024-07-16
2022

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

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ import net.schmizz.sshj.common.SSHException
4545
import net.schmizz.sshj.connection.ConnectionException
4646
import org.zeroturnaround.exec.ProcessExecutor
4747
import java.net.URI
48+
import java.nio.file.Path
4849
import java.time.Duration
4950
import java.time.LocalDateTime
5051
import java.time.format.DateTimeFormatter
@@ -239,6 +240,11 @@ class CoderRemoteConnectionHandle {
239240
return
240241
}
241242

243+
// Makes sure the ssh log directory exists.
244+
if (settings.sshLogDirectory.isNotBlank()) {
245+
Path.of(settings.sshLogDirectory).toFile().mkdirs()
246+
}
247+
242248
// Make the initial connection.
243249
indicator.text = "Connecting ${workspace.ideName} client..."
244250
logger.info("Connecting ${workspace.ideName} client to coder@${workspace.hostname}:22")
@@ -286,7 +292,7 @@ class CoderRemoteConnectionHandle {
286292
}
287293
// Kill the lifetime if the client is closed by the user.
288294
handle.clientClosed.advise(lifetime) {
289-
logger.info("${workspace.ideName} client ${workspace.hostname} closed")
295+
logger.info("${workspace.ideName} client to ${workspace.hostname} closed")
290296
if (lifetime.status == LifetimeStatus.Alive) {
291297
if (continuation.isActive) {
292298
continuation.resumeWithException(Exception("${workspace.ideName} client was closed"))

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,11 @@ class CoderSettingsConfigurable : BoundConfigurable("Coder") {
139139
CoderGatewayBundle.message("gateway.connector.settings.default-url.comment"),
140140
)
141141
}.layout(RowLayout.PARENT_GRID)
142+
row(CoderGatewayBundle.message("gateway.connector.settings.ssh-log-directory.title")) {
143+
textField().resizableColumn().align(AlignX.FILL)
144+
.bindText(state::sshLogDirectory)
145+
.comment(CoderGatewayBundle.message("gateway.connector.settings.ssh-log-directory.comment"))
146+
}.layout(RowLayout.PARENT_GRID)
142147
}
143148
}
144149

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

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -262,7 +262,11 @@ class CoderCLIManager(
262262
"--stdio",
263263
if (settings.disableAutostart && feats.disableAutostart) "--disable-autostart" else null,
264264
)
265-
val proxyArgs = baseArgs + listOfNotNull(if (feats.reportWorkspaceUsage) "--usage-app=jetbrains" else null)
265+
val proxyArgs = baseArgs + listOfNotNull(
266+
if (settings.sshLogDirectory.isNotBlank()) "--log-dir" else null,
267+
if (settings.sshLogDirectory.isNotBlank()) escape(settings.sshLogDirectory) else null,
268+
if (feats.reportWorkspaceUsage) "--usage-app=jetbrains" else null,
269+
)
266270
val backgroundProxyArgs = baseArgs + listOfNotNull(if (feats.reportWorkspaceUsage) "--usage-app=disable" else null)
267271
val extraConfig =
268272
if (settings.sshConfigOptions.isNotBlank()) {
@@ -368,6 +372,10 @@ class CoderCLIManager(
368372
if (contents != null) {
369373
settings.sshConfigPath.parent.toFile().mkdirs()
370374
settings.sshConfigPath.toFile().writeText(contents)
375+
// The Coder cli will *not* create the log directory.
376+
if (settings.sshLogDirectory.isNotBlank()) {
377+
Path.of(settings.sshLogDirectory).toFile().mkdirs()
378+
}
371379
}
372380
}
373381

@@ -453,7 +461,6 @@ class CoderCLIManager(
453461
Features()
454462
} else {
455463
Features(
456-
// Autostart with SSH was added in 2.5.0.
457464
disableAutostart = version >= SemVer(2, 5, 0),
458465
reportWorkspaceUsage = version >= SemVer(2, 13, 0),
459466
)

src/main/kotlin/com/coder/gateway/settings/CoderSettings.kt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,8 @@ open class CoderSettingsState(
8282
open var ignoreSetupFailure: Boolean = false,
8383
// Default URL to show in the connection window.
8484
open var defaultURL: String = "",
85+
// Value for --log-dir.
86+
open var sshLogDirectory: String = "",
8587
)
8688

8789
/**
@@ -175,6 +177,9 @@ open class CoderSettings(
175177
return null
176178
}
177179

180+
val sshLogDirectory: String
181+
get() = state.sshLogDirectory
182+
178183
/**
179184
* Given a deployment URL, try to find a token for it if required.
180185
*/

src/main/resources/messages/CoderGatewayBundle.properties

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,3 +148,7 @@ gateway.connector.settings.default-url.comment=The default URL to set in the \
148148
URL field in the connection window when there is no last used URL. If this \
149149
is not set, it will try CODER_URL then the URL in the Coder CLI config \
150150
directory.
151+
gateway.connector.settings.ssh-log-directory.title=SSH log directory:
152+
gateway.connector.settings.ssh-log-directory.comment=If set, the Coder CLI will \
153+
output extra SSH information into this directory, which can be helpful for \
154+
debugging connectivity issues.
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
# --- START CODER JETBRAINS test.coder.invalid
2+
Host coder-jetbrains--foo--test.coder.invalid
3+
ProxyCommand /tmp/coder-gateway/test.coder.invalid/coder-linux-amd64 --global-config /tmp/coder-gateway/test.coder.invalid/config ssh --stdio --log-dir /tmp/coder-gateway/test.coder.invalid/logs --usage-app=jetbrains foo
4+
ConnectTimeout 0
5+
StrictHostKeyChecking no
6+
UserKnownHostsFile /dev/null
7+
LogLevel ERROR
8+
SetEnv CODER_SSH_SESSION_TYPE=JetBrains
9+
Host coder-jetbrains--foo--test.coder.invalid--bg
10+
ProxyCommand /tmp/coder-gateway/test.coder.invalid/coder-linux-amd64 --global-config /tmp/coder-gateway/test.coder.invalid/config ssh --stdio --usage-app=disable foo
11+
ConnectTimeout 0
12+
StrictHostKeyChecking no
13+
UserKnownHostsFile /dev/null
14+
LogLevel ERROR
15+
SetEnv CODER_SSH_SESSION_TYPE=JetBrains
16+
# --- END CODER JETBRAINS test.coder.invalid

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

Lines changed: 64 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -295,9 +295,14 @@ internal class CoderCLIManagerTest {
295295
val remove: String,
296296
val headerCommand: String = "",
297297
val disableAutostart: Boolean = false,
298-
val features: Features = Features(),
298+
// Default to the most common feature settings.
299+
val features: Features = Features(
300+
disableAutostart = false,
301+
reportWorkspaceUsage = true,
302+
),
299303
val extraConfig: String = "",
300304
val env: Environment = Environment(),
305+
val sshLogDirectory: Path? = null,
301306
)
302307

303308
@Test
@@ -309,27 +314,26 @@ internal class CoderCLIManagerTest {
309314
).joinToString(System.lineSeparator())
310315
val tests =
311316
listOf(
312-
SSHTest(listOf("foo", "bar"), null, "multiple-workspaces", "blank", features = Features(false, true)),
313-
SSHTest(listOf("foo", "bar"), null, "multiple-workspaces", "blank", features = Features(false, true)),
314-
SSHTest(listOf("foo-bar"), "blank", "append-blank", "blank", features = Features(false, true)),
315-
SSHTest(listOf("foo-bar"), "blank-newlines", "append-blank-newlines", "blank", features = Features(false, true)),
316-
SSHTest(listOf("foo-bar"), "existing-end", "replace-end", "no-blocks", features = Features(false, true)),
317-
SSHTest(listOf("foo-bar"), "existing-end-no-newline", "replace-end-no-newline", "no-blocks", features = Features(false, true)),
318-
SSHTest(listOf("foo-bar"), "existing-middle", "replace-middle", "no-blocks", features = Features(false, true)),
319-
SSHTest(listOf("foo-bar"), "existing-middle-and-unrelated", "replace-middle-ignore-unrelated", "no-related-blocks", features = Features(false, true)),
320-
SSHTest(listOf("foo-bar"), "existing-only", "replace-only", "blank", features = Features(false, true)),
321-
SSHTest(listOf("foo-bar"), "existing-start", "replace-start", "no-blocks", features = Features(false, true)),
322-
SSHTest(listOf("foo-bar"), "no-blocks", "append-no-blocks", "no-blocks", features = Features(false, true)),
323-
SSHTest(listOf("foo-bar"), "no-related-blocks", "append-no-related-blocks", "no-related-blocks", features = Features(false, true)),
324-
SSHTest(listOf("foo-bar"), "no-newline", "append-no-newline", "no-blocks", features = Features(false, true)),
317+
SSHTest(listOf("foo", "bar"), null, "multiple-workspaces", "blank"),
318+
SSHTest(listOf("foo", "bar"), null, "multiple-workspaces", "blank"),
319+
SSHTest(listOf("foo-bar"), "blank", "append-blank", "blank"),
320+
SSHTest(listOf("foo-bar"), "blank-newlines", "append-blank-newlines", "blank"),
321+
SSHTest(listOf("foo-bar"), "existing-end", "replace-end", "no-blocks"),
322+
SSHTest(listOf("foo-bar"), "existing-end-no-newline", "replace-end-no-newline", "no-blocks"),
323+
SSHTest(listOf("foo-bar"), "existing-middle", "replace-middle", "no-blocks"),
324+
SSHTest(listOf("foo-bar"), "existing-middle-and-unrelated", "replace-middle-ignore-unrelated", "no-related-blocks"),
325+
SSHTest(listOf("foo-bar"), "existing-only", "replace-only", "blank"),
326+
SSHTest(listOf("foo-bar"), "existing-start", "replace-start", "no-blocks"),
327+
SSHTest(listOf("foo-bar"), "no-blocks", "append-no-blocks", "no-blocks"),
328+
SSHTest(listOf("foo-bar"), "no-related-blocks", "append-no-related-blocks", "no-related-blocks"),
329+
SSHTest(listOf("foo-bar"), "no-newline", "append-no-newline", "no-blocks"),
325330
if (getOS() == OS.WINDOWS) {
326331
SSHTest(
327332
listOf("header"),
328333
null,
329334
"header-command-windows",
330335
"blank",
331336
""""C:\Program Files\My Header Command\HeaderCommand.exe" --url="%CODER_URL%" --test="foo bar"""",
332-
features = Features(false, true),
333337
)
334338
} else {
335339
SSHTest(
@@ -338,27 +342,53 @@ internal class CoderCLIManagerTest {
338342
"header-command",
339343
"blank",
340344
"my-header-command --url=\"\$CODER_URL\" --test=\"foo bar\" --literal='\$CODER_URL'",
341-
features = Features(false, true),
342345
)
343346
},
344-
SSHTest(listOf("foo"), null, "disable-autostart", "blank", "", true, Features(true, true)),
345-
SSHTest(listOf("foo"), null, "no-disable-autostart", "blank", "", true, Features(false, true)),
346-
SSHTest(listOf("foo"), null, "no-report-usage", "blank", "", true, Features(false, false)),
347+
SSHTest(
348+
listOf("foo"),
349+
null,
350+
"disable-autostart",
351+
"blank",
352+
"",
353+
true,
354+
Features(
355+
disableAutostart = true,
356+
reportWorkspaceUsage = true,
357+
),
358+
),
359+
SSHTest(listOf("foo"), null, "no-disable-autostart", "blank", ""),
360+
SSHTest(
361+
listOf("foo"),
362+
null,
363+
"no-report-usage",
364+
"blank",
365+
"",
366+
true,
367+
Features(
368+
disableAutostart = false,
369+
reportWorkspaceUsage = false,
370+
),
371+
),
347372
SSHTest(
348373
listOf("extra"),
349374
null,
350375
"extra-config",
351376
"blank",
352377
extraConfig = extraConfig,
353-
features = Features(false, true),
354378
),
355379
SSHTest(
356380
listOf("extra"),
357381
null,
358382
"extra-config",
359383
"blank",
360384
env = Environment(mapOf(CODER_SSH_CONFIG_OPTIONS to extraConfig)),
361-
features = Features(false, true),
385+
),
386+
SSHTest(
387+
listOf("foo"),
388+
null,
389+
"log-dir",
390+
"blank",
391+
sshLogDirectory = tmpdir.resolve("ssh-logs"),
362392
),
363393
)
364394

@@ -372,6 +402,7 @@ internal class CoderCLIManagerTest {
372402
dataDirectory = tmpdir.resolve("configure-ssh").toString(),
373403
headerCommand = it.headerCommand,
374404
sshConfigOptions = it.extraConfig,
405+
sshLogDirectory = it.sshLogDirectory?.toString() ?: "",
375406
),
376407
sshConfigPath = tmpdir.resolve(it.input + "_to_" + it.output + ".conf"),
377408
env = it.env,
@@ -395,12 +426,24 @@ internal class CoderCLIManagerTest {
395426
.replace(newlineRe, System.lineSeparator())
396427
.replace("/tmp/coder-gateway/test.coder.invalid/config", escape(coderConfigPath.toString()))
397428
.replace("/tmp/coder-gateway/test.coder.invalid/coder-linux-amd64", escape(ccm.localBinaryPath.toString()))
429+
.let { conf ->
430+
if (it.sshLogDirectory != null) {
431+
conf.replace("/tmp/coder-gateway/test.coder.invalid/logs", it.sshLogDirectory.toString())
432+
} else {
433+
conf
434+
}
435+
}
398436

399437
// Add workspaces.
400438
ccm.configSsh(it.workspaces.toSet(), it.features)
401439

402440
assertEquals(expectedConf, settings.sshConfigPath.toFile().readText())
403441

442+
// SSH log directory should have been created.
443+
if (it.sshLogDirectory != null) {
444+
assertTrue(it.sshLogDirectory.toFile().exists())
445+
}
446+
404447
// Remove configuration.
405448
ccm.configSsh(emptySet(), it.features)
406449

src/test/kotlin/com/coder/gateway/settings/CoderSettingsTest.kt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -302,7 +302,7 @@ internal class CoderSettingsTest {
302302
val tmp = Path.of(System.getProperty("java.io.tmpdir"))
303303
val url = URL("http://test.deployment.coder.com")
304304
val dir = tmp.resolve("coder-gateway-test/test-default-token")
305-
var env =
305+
val env =
306306
Environment(
307307
mapOf(
308308
"CODER_CONFIG_DIR" to dir.toString(),
@@ -386,6 +386,7 @@ internal class CoderSettingsTest {
386386
disableAutostart = getOS() != OS.MAC,
387387
setupCommand = "test setup",
388388
ignoreSetupFailure = true,
389+
sshLogDirectory = "test ssh log directory",
389390
),
390391
)
391392

@@ -399,5 +400,6 @@ internal class CoderSettingsTest {
399400
assertEquals(getOS() != OS.MAC, settings.disableAutostart)
400401
assertEquals("test setup", settings.setupCommand)
401402
assertEquals(true, settings.ignoreSetupFailure)
403+
assertEquals("test ssh log directory", settings.sshLogDirectory)
402404
}
403405
}

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy