Skip to content

Commit 7c4fcaf

Browse files
committed
Add the ability to disable autostart
On macOS this is checked by default for 2.5.0 and above.
1 parent 0a0fb52 commit 7c4fcaf

File tree

10 files changed

+100
-30
lines changed

10 files changed

+100
-30
lines changed

CHANGELOG.md

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

55
## Unreleased
66

7+
### Changed
8+
9+
- Disable autostarting workspaces by default on macOS to prevent an issue where
10+
it wakes periodically and keeps the workspace on. This can be toggled via the
11+
settings.
12+
713
## 2.9.3 - 2024-02-10
814

915
### Fixed

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

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
package com.coder.gateway
22

33
import com.coder.gateway.services.CoderSettingsService
4-
import com.coder.gateway.util.canCreateDirectory
54
import com.coder.gateway.services.CoderSettingsState
5+
import com.coder.gateway.util.canCreateDirectory
66
import com.intellij.openapi.components.service
77
import com.intellij.openapi.options.BoundConfigurable
88
import com.intellij.openapi.ui.DialogPanel
@@ -102,6 +102,13 @@ class CoderSettingsConfigurable : BoundConfigurable("Coder") {
102102
CoderGatewayBundle.message("gateway.connector.settings.tls-alt-name.comment")
103103
)
104104
}.layout(RowLayout.PARENT_GRID)
105+
row(CoderGatewayBundle.message("gateway.connector.settings.disable-autostart.heading")) {
106+
checkBox(CoderGatewayBundle.message("gateway.connector.settings.disable-autostart.title"))
107+
.bindSelected(state::disableAutostart)
108+
.comment(
109+
CoderGatewayBundle.message("gateway.connector.settings.disable-autostart.comment")
110+
)
111+
}.layout(RowLayout.PARENT_GRID)
105112
}
106113
}
107114

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

Lines changed: 22 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -199,9 +199,11 @@ class CoderCLIManager(
199199

200200
/**
201201
* Configure SSH to use this binary.
202+
*
203+
* This can take a version for testing purposes only.
202204
*/
203-
fun configSsh(workspaceNames: List<String>) {
204-
writeSSHConfig(modifySSHConfig(readSSHConfig(), workspaceNames))
205+
fun configSsh(workspaceNames: List<String>, version: SemVer? = tryVersion()) {
206+
writeSSHConfig(modifySSHConfig(readSSHConfig(), workspaceNames, version))
205207
}
206208

207209
/**
@@ -220,7 +222,7 @@ class CoderCLIManager(
220222
* this deployment and return the modified config or null if it does not
221223
* need to be modified.
222224
*/
223-
private fun modifySSHConfig(contents: String?, workspaceNames: List<String>): String? {
225+
private fun modifySSHConfig(contents: String?, workspaceNames: List<String>, version: SemVer?): String? {
224226
val host = deploymentURL.safeHost()
225227
val startBlock = "# --- START CODER JETBRAINS $host"
226228
val endBlock = "# --- END CODER JETBRAINS $host"
@@ -230,15 +232,17 @@ class CoderCLIManager(
230232
"--global-config", escape(coderConfigPath.toString()),
231233
if (settings.headerCommand.isNotBlank()) "--header-command" else null,
232234
if (settings.headerCommand.isNotBlank()) escapeSubcommand(settings.headerCommand) else null,
233-
"ssh", "--stdio")
235+
"ssh", "--stdio",
236+
// Autostart on SSH was added in 2.5.0.
237+
if (settings.disableAutostart && version != null && version >= SemVer(2, 5, 0)) "--disable-autostart" else null)
234238
val blockContent = workspaceNames.joinToString(
235239
System.lineSeparator(),
236240
startBlock + System.lineSeparator(),
237241
System.lineSeparator() + endBlock,
238242
transform = {
239243
"""
240244
Host ${getHostName(deploymentURL, it)}
241-
ProxyCommand ${proxyArgs.joinToString(" ")} ${it}
245+
ProxyCommand ${proxyArgs.joinToString(" ")} $it
242246
ConnectTimeout 0
243247
StrictHostKeyChecking no
244248
UserKnownHostsFile /dev/null
@@ -333,31 +337,36 @@ class CoderCLIManager(
333337
}
334338

335339
/**
336-
* Returns true if the CLI has the same major/minor/patch version as the
337-
* provided version, false if it does not match, or null if the CLI version
338-
* could not be determined because the binary could not be executed or the
339-
* version could not be parsed.
340+
* Like version(), but logs errors instead of throwing them.
340341
*/
341-
fun matchesVersion(rawBuildVersion: String): Boolean? {
342-
val cliVersion = try {
342+
private fun tryVersion(): SemVer? {
343+
return try {
343344
version()
344345
} catch (e: Exception) {
345346
when (e) {
346347
is JsonSyntaxException,
347348
is InvalidVersionException -> {
348349
logger.info("Got invalid version from $localBinaryPath: ${e.message}")
349-
return null
350350
}
351351
else -> {
352352
// An error here most likely means the CLI does not exist or
353353
// it executed successfully but output no version which
354354
// suggests it is not the right binary.
355355
logger.info("Unable to determine $localBinaryPath version: ${e.message}")
356-
return null
357356
}
358357
}
358+
null
359359
}
360+
}
360361

362+
/**
363+
* Returns true if the CLI has the same major/minor/patch version as the
364+
* provided version, false if it does not match, or null if the CLI version
365+
* could not be determined because the binary could not be executed or the
366+
* version could not be parsed.
367+
*/
368+
fun matchesVersion(rawBuildVersion: String): Boolean? {
369+
val cliVersion = tryVersion() ?: return null
361370
val buildVersion = try {
362371
SemVer.parse(rawBuildVersion)
363372
} catch (e: InvalidVersionException) {

src/main/kotlin/com/coder/gateway/services/CoderSettingsState.kt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package com.coder.gateway.services
22

3+
import com.coder.gateway.util.OS
4+
import com.coder.gateway.util.getOS
35
import com.intellij.openapi.components.PersistentStateComponent
46
import com.intellij.openapi.components.RoamingType
57
import com.intellij.openapi.components.Service
@@ -56,6 +58,10 @@ class CoderSettingsState(
5658
// connections. This is useful when the hostname used to connect to the
5759
// Coder service does not match the hostname in the TLS certificate.
5860
var tlsAlternateHostname: String = "",
61+
// Whether to add --disable-autostart to the proxy command. This works
62+
// around issues on macOS where it periodically wakes and Gateway
63+
// reconnects, keeping the workspace constantly up.
64+
var disableAutostart: Boolean = getOS() == OS.MAC,
5965
) : PersistentStateComponent<CoderSettingsState> {
6066
override fun getState(): CoderSettingsState {
6167
return this

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,9 @@ open class CoderSettings(
3535
val headerCommand: String
3636
get() = state.headerCommand
3737

38+
val disableAutostart: Boolean
39+
get() = state.disableAutostart
40+
3841
/**
3942
* Where the specified deployment should put its data.
4043
*/

src/main/resources/messages/CoderGatewayBundle.properties

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,3 +111,9 @@ gateway.connector.settings.tls-alt-name.comment=Optionally set this to \
111111
an alternate hostname used for verifying TLS connections. This is useful \
112112
when the hostname used to connect to the Coder service does not match the \
113113
hostname in the TLS certificate.
114+
gateway.connector.settings.disable-autostart.heading=Autostart:
115+
gateway.connector.settings.disable-autostart.title=Disable autostart
116+
gateway.connector.settings.disable-autostart.comment=Checking this box will \
117+
cause the plugin to configure the CLI with --disable-autostart. You must go \
118+
through the IDE selection again for the plugin to reconfigure the CLI with \
119+
this setting.
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
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 --disable-autostart foo
4+
ConnectTimeout 0
5+
StrictHostKeyChecking no
6+
UserKnownHostsFile /dev/null
7+
LogLevel ERROR
8+
SetEnv CODER_SSH_SESSION_TYPE=JetBrains
9+
# --- END CODER JETBRAINS test.coder.invalid
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
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 foo
4+
ConnectTimeout 0
5+
StrictHostKeyChecking no
6+
UserKnownHostsFile /dev/null
7+
LogLevel ERROR
8+
SetEnv CODER_SSH_SESSION_TYPE=JetBrains
9+
# --- END CODER JETBRAINS test.coder.invalid

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

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -234,7 +234,15 @@ internal class CoderCLIManagerTest {
234234
srv2.stop(0)
235235
}
236236

237-
data class SSHTest(val workspaces: List<String>, val input: String?, val output: String, val remove: String, val headerCommand: String?)
237+
data class SSHTest(
238+
val workspaces: List<String>,
239+
val input: String?,
240+
val output: String,
241+
val remove: String,
242+
val headerCommand: String?,
243+
val disableAutostart: Boolean = false,
244+
val version: SemVer? = null,
245+
)
238246

239247
@Test
240248
fun testConfigureSSH() {
@@ -256,13 +264,19 @@ internal class CoderCLIManagerTest {
256264
SSHTest(listOf("header"), null, "header-command-windows", "blank", """"C:\Program Files\My Header Command\HeaderCommand.exe" --url="%CODER_URL%" --test="foo bar"""")
257265
} else {
258266
SSHTest(listOf("header"), null, "header-command", "blank", "my-header-command --url=\"\$CODER_URL\" --test=\"foo bar\" --literal='\$CODER_URL'")
259-
}
267+
},
268+
SSHTest(listOf("foo"), null, "disable-autostart", "blank", null, true, SemVer(2, 5, 0)),
269+
SSHTest(listOf("foo"), null, "disable-autostart", "blank", null, true, SemVer(3, 5, 0)),
270+
SSHTest(listOf("foo"), null, "no-disable-autostart", "blank", null, true, SemVer(2, 4, 9)),
271+
SSHTest(listOf("foo"), null, "no-disable-autostart", "blank", null, true, SemVer(1, 0, 1)),
272+
SSHTest(listOf("foo"), null, "no-disable-autostart", "blank", null, true),
260273
)
261274

262275
val newlineRe = "\r?\n".toRegex()
263276

264277
tests.forEach {
265278
val settings = CoderSettings(CoderSettingsState(
279+
disableAutostart = it.disableAutostart,
266280
dataDirectory = tmpdir.resolve("configure-ssh").toString(),
267281
headerCommand = it.headerCommand ?: ""),
268282
sshConfigPath = tmpdir.resolve(it.input + "_to_" + it.output + ".conf"))
@@ -285,12 +299,12 @@ internal class CoderCLIManagerTest {
285299
.replace("/tmp/coder-gateway/test.coder.invalid/coder-linux-amd64", escape(ccm.localBinaryPath.toString()))
286300

287301
// Add workspaces.
288-
ccm.configSsh(it.workspaces)
302+
ccm.configSsh(it.workspaces, it.version)
289303

290304
assertEquals(expectedConf, settings.sshConfigPath.toFile().readText())
291305

292306
// Remove configuration.
293-
ccm.configSsh(emptyList())
307+
ccm.configSsh(emptyList(), it.version)
294308

295309
// Remove is the configuration we expect after removing.
296310
assertEquals(

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

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,14 @@
11
package com.coder.gateway.settings
22

33
import com.coder.gateway.services.CoderSettingsState
4-
import kotlin.test.Test
5-
import kotlin.test.assertContains
6-
import kotlin.test.assertEquals
7-
84
import com.coder.gateway.util.OS
95
import com.coder.gateway.util.getOS
106
import com.coder.gateway.util.withPath
117
import java.net.URL
128
import java.nio.file.Path
9+
import kotlin.test.Test
10+
import kotlin.test.assertContains
11+
import kotlin.test.assertEquals
1312

1413
internal class CoderSettingsTest {
1514
@Test
@@ -179,14 +178,15 @@ internal class CoderSettingsTest {
179178
// Make sure the remaining settings are being conveyed.
180179
val settings = CoderSettings(
181180
CoderSettingsState(
182-
enableDownloads = false,
183-
enableBinaryDirectoryFallback = true,
184-
headerCommand = "test header",
185-
tlsCertPath = "tls cert path",
186-
tlsKeyPath = "tls key path",
187-
tlsCAPath = "tls ca path",
188-
tlsAlternateHostname = "tls alt hostname",
189-
)
181+
enableDownloads = false,
182+
enableBinaryDirectoryFallback = true,
183+
headerCommand = "test header",
184+
tlsCertPath = "tls cert path",
185+
tlsKeyPath = "tls key path",
186+
tlsCAPath = "tls ca path",
187+
tlsAlternateHostname = "tls alt hostname",
188+
disableAutostart = true,
189+
)
190190
)
191191

192192
assertEquals(false, settings.enableDownloads)
@@ -196,5 +196,6 @@ internal class CoderSettingsTest {
196196
assertEquals("tls key path", settings.tls.keyPath)
197197
assertEquals("tls ca path", settings.tls.caPath)
198198
assertEquals("tls alt hostname", settings.tls.altHostname)
199+
assertEquals(true, settings.disableAutostart)
199200
}
200201
}

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