Skip to content

Commit df37364

Browse files
authored
impl: ssh config improvements (#41)
- different header blocks to differentiate between Gateway and Toolbox ssh config - different key for Toolbox for workspace usage - honors the `disable autostart` setting in the URI handling - plugin's display name was changed from Coder Toolbox to Coder - adds support for ssh wildcard config, configurable from plugin's Settings page. - the settings models were reworked because there was too much confusion around CoderSettings, CoderSettingsState, CoderSettingsService. Lots of properties and some models were introduced only to inject data during tests. - among other issues, test related properties were leaking in the business code and there was no clear definition between the readable and writable interface. With this PR we have CoderSettingsStore exposing read+write, CoderSettings exposing reads + PluginSettingsStore the underlying persistable store. - the tests control data via an instance of PluginSettingsStore just like the business code - some of the options that did not make sense to be configurable and were only used in the tests were removed: - setupCommand - ignoreSetupFailures - two settings are now also exposed and configurable in the UI (previously only from tests): - ssh log dir - ssh extra arguments to the proxy command - resolves #40 - resolves #46
1 parent 427e6f1 commit df37364

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

56 files changed

+1188
-972
lines changed

build.gradle.kts

+3-2
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ val extension = ExtensionJson(
7171

7272
version = properties("version"),
7373
meta = ExtensionJsonMeta(
74-
name = "Coder Toolbox",
74+
name = "Coder",
7575
description = "Connects your JetBrains IDE to Coder workspaces",
7676
vendor = "Coder",
7777
url = "https://github.com/coder/coder-jetbrains-toolbox-plugin",
@@ -145,7 +145,8 @@ fun CopySpec.fromCompileDependencies() {
145145
"core-api",
146146
"ui-api",
147147
"annotations",
148-
"localization-api"
148+
"localization-api",
149+
"slf4j-api"
149150
).any { file.name.contains(it) }
150151
}
151152
},

src/main/kotlin/com/coder/toolbox/CoderRemoteEnvironment.kt

+6-1
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,12 @@ class CoderRemoteEnvironment(
123123
* have to do is provide it a host name.
124124
*/
125125
override suspend
126-
fun getContentsView(): EnvironmentContentsView = EnvironmentView(client.url, workspace, agent)
126+
fun getContentsView(): EnvironmentContentsView = EnvironmentView(
127+
context.settingsStore.readOnly(),
128+
client.url,
129+
workspace,
130+
agent
131+
)
127132

128133
override val connectionRequest: MutableStateFlow<Boolean>? = MutableStateFlow(false)
129134

src/main/kotlin/com/coder/toolbox/CoderRemoteProvider.kt

+21-26
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,7 @@ package com.coder.toolbox
33
import com.coder.toolbox.cli.CoderCLIManager
44
import com.coder.toolbox.sdk.CoderRestClient
55
import com.coder.toolbox.sdk.v2.models.WorkspaceStatus
6-
import com.coder.toolbox.services.CoderSecretsService
7-
import com.coder.toolbox.services.CoderSettingsService
8-
import com.coder.toolbox.settings.CoderSettings
9-
import com.coder.toolbox.settings.Source
6+
import com.coder.toolbox.settings.SettingSource
107
import com.coder.toolbox.util.CoderProtocolHandler
118
import com.coder.toolbox.util.DialogUi
129
import com.coder.toolbox.views.Action
@@ -46,12 +43,11 @@ class CoderRemoteProvider(
4643
private var pollJob: Job? = null
4744
private var lastEnvironments: Set<CoderRemoteEnvironment>? = null
4845

46+
private val cSettings = context.settingsStore.readOnly()
47+
4948
// Create our services from the Toolbox ones.
50-
private val settingsService = CoderSettingsService(context.settingsStore)
51-
private val settings: CoderSettings = CoderSettings(settingsService, context.logger)
52-
private val secrets: CoderSecretsService = CoderSecretsService(context.secretsStore)
53-
private val settingsPage: CoderSettingsPage = CoderSettingsPage(context, settingsService)
54-
private val dialogUi = DialogUi(context, settings)
49+
private val settingsPage: CoderSettingsPage = CoderSettingsPage(context)
50+
private val dialogUi = DialogUi(context)
5551

5652
// The REST client, if we are signed in
5753
private var client: CoderRestClient? = null
@@ -65,7 +61,7 @@ class CoderRemoteProvider(
6561
private var firstRun = true
6662
private val isInitialized: MutableStateFlow<Boolean> = MutableStateFlow(false)
6763
private var coderHeaderPage = NewEnvironmentPage(context, context.i18n.pnotr(getDeploymentURL()?.first ?: ""))
68-
private val linkHandler = CoderProtocolHandler(context, settings, httpClient, dialogUi, isInitialized)
64+
private val linkHandler = CoderProtocolHandler(context, httpClient, dialogUi, isInitialized)
6965
override val environments: MutableStateFlow<LoadableState<List<RemoteProviderEnvironment>>> = MutableStateFlow(
7066
LoadableState.Value(emptyList())
7167
)
@@ -151,7 +147,7 @@ class CoderRemoteProvider(
151147
private fun logout() {
152148
// Keep the URL and token to make it easy to log back in, but set
153149
// rememberMe to false so we do not try to automatically log in.
154-
secrets.rememberMe = "false"
150+
context.secrets.rememberMe = "false"
155151
close()
156152
}
157153

@@ -272,9 +268,9 @@ class CoderRemoteProvider(
272268
// When coming back to the application, authenticate immediately.
273269
val autologin = shouldDoAutoLogin()
274270
var autologinEx: Exception? = null
275-
secrets.lastToken.let { lastToken ->
276-
secrets.lastDeploymentURL.let { lastDeploymentURL ->
277-
if (autologin && lastDeploymentURL.isNotBlank() && (lastToken.isNotBlank() || !settings.requireTokenAuth)) {
271+
context.secrets.lastToken.let { lastToken ->
272+
context.secrets.lastDeploymentURL.let { lastDeploymentURL ->
273+
if (autologin && lastDeploymentURL.isNotBlank() && (lastToken.isNotBlank() || !cSettings.requireTokenAuth)) {
278274
try {
279275
return createConnectPage(URL(lastDeploymentURL), lastToken)
280276
} catch (ex: Exception) {
@@ -309,7 +305,7 @@ class CoderRemoteProvider(
309305
return null
310306
}
311307

312-
private fun shouldDoAutoLogin(): Boolean = firstRun && secrets.rememberMe == "true"
308+
private fun shouldDoAutoLogin(): Boolean = firstRun && context.secrets.rememberMe == "true"
313309

314310
/**
315311
* Create a connect page that starts polling and resets the UI on success.
@@ -318,15 +314,14 @@ class CoderRemoteProvider(
318314
context,
319315
deploymentURL,
320316
token,
321-
settings,
322317
httpClient,
323318
::goToEnvironmentsPage,
324319
) { client, cli ->
325320
// Store the URL and token for use next time.
326-
secrets.lastDeploymentURL = client.url.toString()
327-
secrets.lastToken = client.token ?: ""
321+
context.secrets.lastDeploymentURL = client.url.toString()
322+
context.secrets.lastToken = client.token ?: ""
328323
// Currently we always remember, but this could be made an option.
329-
secrets.rememberMe = "true"
324+
context.secrets.rememberMe = "true"
330325
this.client = client
331326
pollError = null
332327
pollJob?.cancel()
@@ -343,11 +338,11 @@ class CoderRemoteProvider(
343338
* 2. Token on disk for this deployment.
344339
* 3. Global token for Coder, if it matches the deployment.
345340
*/
346-
private fun getToken(deploymentURL: URL): Pair<String, Source>? = secrets.lastToken.let {
347-
if (it.isNotBlank() && secrets.lastDeploymentURL == deploymentURL.toString()) {
348-
it to Source.LAST_USED
341+
private fun getToken(deploymentURL: URL): Pair<String, SettingSource>? = context.secrets.lastToken.let {
342+
if (it.isNotBlank() && context.secrets.lastDeploymentURL == deploymentURL.toString()) {
343+
it to SettingSource.LAST_USED
349344
} else {
350-
settings.token(deploymentURL)
345+
cSettings.token(deploymentURL)
351346
}
352347
}
353348

@@ -361,11 +356,11 @@ class CoderRemoteProvider(
361356
* 3. CODER_URL.
362357
* 4. URL in global cli config.
363358
*/
364-
private fun getDeploymentURL(): Pair<String, Source>? = secrets.lastDeploymentURL.let {
359+
private fun getDeploymentURL(): Pair<String, SettingSource>? = context.secrets.lastDeploymentURL.let {
365360
if (it.isNotBlank()) {
366-
it to Source.LAST_USED
361+
it to SettingSource.LAST_USED
367362
} else {
368-
settings.defaultURL()
363+
context.settingsStore.defaultURL()
369364
}
370365
}
371366
}

src/main/kotlin/com/coder/toolbox/CoderToolboxContext.kt

+4-4
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
package com.coder.toolbox
22

3-
import com.jetbrains.toolbox.api.core.PluginSecretStore
4-
import com.jetbrains.toolbox.api.core.PluginSettingsStore
3+
import com.coder.toolbox.store.CoderSecretsStore
4+
import com.coder.toolbox.store.CoderSettingsStore
55
import com.jetbrains.toolbox.api.core.diagnostics.Logger
66
import com.jetbrains.toolbox.api.localization.LocalizableStringFactory
77
import com.jetbrains.toolbox.api.remoteDev.connection.ClientHelper
@@ -18,6 +18,6 @@ data class CoderToolboxContext(
1818
val cs: CoroutineScope,
1919
val logger: Logger,
2020
val i18n: LocalizableStringFactory,
21-
val settingsStore: PluginSettingsStore,
22-
val secretsStore: PluginSecretStore
21+
val settingsStore: CoderSettingsStore,
22+
val secrets: CoderSecretsStore
2323
)

src/main/kotlin/com/coder/toolbox/CoderToolboxExtension.kt

+6-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
package com.coder.toolbox
22

3+
import com.coder.toolbox.settings.Environment
4+
import com.coder.toolbox.store.CoderSecretsStore
5+
import com.coder.toolbox.store.CoderSettingsStore
36
import com.jetbrains.toolbox.api.core.PluginSecretStore
47
import com.jetbrains.toolbox.api.core.PluginSettingsStore
58
import com.jetbrains.toolbox.api.core.ServiceLocator
@@ -20,6 +23,7 @@ import okhttp3.OkHttpClient
2023
class CoderToolboxExtension : RemoteDevExtension {
2124
// All services must be passed in here and threaded as necessary.
2225
override fun createRemoteProviderPluginInstance(serviceLocator: ServiceLocator): RemoteProvider {
26+
val logger = serviceLocator.getService(Logger::class.java)
2327
return CoderRemoteProvider(
2428
CoderToolboxContext(
2529
serviceLocator.getService(ToolboxUi::class.java),
@@ -29,8 +33,8 @@ class CoderToolboxExtension : RemoteDevExtension {
2933
serviceLocator.getService(CoroutineScope::class.java),
3034
serviceLocator.getService(Logger::class.java),
3135
serviceLocator.getService(LocalizableStringFactory::class.java),
32-
serviceLocator.getService(PluginSettingsStore::class.java),
33-
serviceLocator.getService(PluginSecretStore::class.java),
36+
CoderSettingsStore(serviceLocator.getService(PluginSettingsStore::class.java), Environment(), logger),
37+
CoderSecretsStore(serviceLocator.getService(PluginSecretStore::class.java)),
3438
),
3539
OkHttpClient(),
3640
)

0 commit comments

Comments
 (0)