Skip to content

Commit 1708944

Browse files
committed
impl: rework the settings models
- too much confusion around CoderSettings, CoderSettingsState, CoderSettingsService - lots of properties and some models were introduced only to inject data during tests - test related properties were leaking in the business code and there was no clear definition between the readable and writable interface - with this commit 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 - 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 - some of the options that did not make sense to be configurable and were only used in the tests were removed: - setupCommand - ignoreSetupFailures - these two are Gateway specific
1 parent 2929b0e commit 1708944

22 files changed

+919
-791
lines changed

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

+12-13
Original file line numberDiff line numberDiff line change
@@ -3,8 +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.settings.CoderSettings
7-
import com.coder.toolbox.settings.Source
6+
import com.coder.toolbox.settings.SettingSource
87
import com.coder.toolbox.util.CoderProtocolHandler
98
import com.coder.toolbox.util.DialogUi
109
import com.coder.toolbox.views.Action
@@ -44,10 +43,11 @@ class CoderRemoteProvider(
4443
private var pollJob: Job? = null
4544
private var lastEnvironments: Set<CoderRemoteEnvironment>? = null
4645

46+
private val cSettings = context.settingsStore.readOnly()
47+
4748
// Create our services from the Toolbox ones.
48-
private val settings: CoderSettings = CoderSettings(context.settings, context.logger)
4949
private val settingsPage: CoderSettingsPage = CoderSettingsPage(context)
50-
private val dialogUi = DialogUi(context, settings)
50+
private val dialogUi = DialogUi(context)
5151

5252
// The REST client, if we are signed in
5353
private var client: CoderRestClient? = null
@@ -61,7 +61,7 @@ class CoderRemoteProvider(
6161
private var firstRun = true
6262
private val isInitialized: MutableStateFlow<Boolean> = MutableStateFlow(false)
6363
private var coderHeaderPage = NewEnvironmentPage(context, context.i18n.pnotr(getDeploymentURL()?.first ?: ""))
64-
private val linkHandler = CoderProtocolHandler(context, settings, httpClient, dialogUi, isInitialized)
64+
private val linkHandler = CoderProtocolHandler(context, httpClient, dialogUi, isInitialized)
6565
override val environments: MutableStateFlow<LoadableState<List<RemoteProviderEnvironment>>> = MutableStateFlow(
6666
LoadableState.Value(emptyList())
6767
)
@@ -270,7 +270,7 @@ class CoderRemoteProvider(
270270
var autologinEx: Exception? = null
271271
context.secrets.lastToken.let { lastToken ->
272272
context.secrets.lastDeploymentURL.let { lastDeploymentURL ->
273-
if (autologin && lastDeploymentURL.isNotBlank() && (lastToken.isNotBlank() || !settings.requireTokenAuth)) {
273+
if (autologin && lastDeploymentURL.isNotBlank() && (lastToken.isNotBlank() || !cSettings.requireTokenAuth)) {
274274
try {
275275
return createConnectPage(URL(lastDeploymentURL), lastToken)
276276
} catch (ex: Exception) {
@@ -314,7 +314,6 @@ class CoderRemoteProvider(
314314
context,
315315
deploymentURL,
316316
token,
317-
settings,
318317
httpClient,
319318
::goToEnvironmentsPage,
320319
) { client, cli ->
@@ -339,11 +338,11 @@ class CoderRemoteProvider(
339338
* 2. Token on disk for this deployment.
340339
* 3. Global token for Coder, if it matches the deployment.
341340
*/
342-
private fun getToken(deploymentURL: URL): Pair<String, Source>? = context.secrets.lastToken.let {
341+
private fun getToken(deploymentURL: URL): Pair<String, SettingSource>? = context.secrets.lastToken.let {
343342
if (it.isNotBlank() && context.secrets.lastDeploymentURL == deploymentURL.toString()) {
344-
it to Source.LAST_USED
343+
it to SettingSource.LAST_USED
345344
} else {
346-
settings.token(deploymentURL)
345+
cSettings.token(deploymentURL)
347346
}
348347
}
349348

@@ -357,11 +356,11 @@ class CoderRemoteProvider(
357356
* 3. CODER_URL.
358357
* 4. URL in global cli config.
359358
*/
360-
private fun getDeploymentURL(): Pair<String, Source>? = context.secrets.lastDeploymentURL.let {
359+
private fun getDeploymentURL(): Pair<String, SettingSource>? = context.secrets.lastDeploymentURL.let {
361360
if (it.isNotBlank()) {
362-
it to Source.LAST_USED
361+
it to SettingSource.LAST_USED
363362
} else {
364-
settings.defaultURL()
363+
context.settingsStore.defaultURL()
365364
}
366365
}
367366
}

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.coder.toolbox.services.CoderSecretsService
4-
import com.coder.toolbox.services.CoderSettingsService
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 settings: CoderSettingsService,
22-
val secrets: CoderSecretsService
21+
val settingsStore: CoderSettingsStore,
22+
val secrets: CoderSecretsStore
2323
)

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

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

3-
import com.coder.toolbox.services.CoderSecretsService
4-
import com.coder.toolbox.services.CoderSettingsService
3+
import com.coder.toolbox.settings.Environment
4+
import com.coder.toolbox.store.CoderSecretsStore
5+
import com.coder.toolbox.store.CoderSettingsStore
56
import com.jetbrains.toolbox.api.core.PluginSecretStore
67
import com.jetbrains.toolbox.api.core.PluginSettingsStore
78
import com.jetbrains.toolbox.api.core.ServiceLocator
@@ -22,6 +23,7 @@ import okhttp3.OkHttpClient
2223
class CoderToolboxExtension : RemoteDevExtension {
2324
// All services must be passed in here and threaded as necessary.
2425
override fun createRemoteProviderPluginInstance(serviceLocator: ServiceLocator): RemoteProvider {
26+
val logger = serviceLocator.getService(Logger::class.java)
2527
return CoderRemoteProvider(
2628
CoderToolboxContext(
2729
serviceLocator.getService(ToolboxUi::class.java),
@@ -31,8 +33,8 @@ class CoderToolboxExtension : RemoteDevExtension {
3133
serviceLocator.getService(CoroutineScope::class.java),
3234
serviceLocator.getService(Logger::class.java),
3335
serviceLocator.getService(LocalizableStringFactory::class.java),
34-
CoderSettingsService(serviceLocator.getService(PluginSettingsStore::class.java)),
35-
CoderSecretsService(serviceLocator.getService(PluginSecretStore::class.java)),
36+
CoderSettingsStore(serviceLocator.getService(PluginSettingsStore::class.java), Environment(), logger),
37+
CoderSecretsStore(serviceLocator.getService(PluginSecretStore::class.java)),
3638
),
3739
OkHttpClient(),
3840
)

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

+15-13
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ import com.coder.toolbox.cli.ex.MissingVersionException
55
import com.coder.toolbox.cli.ex.ResponseException
66
import com.coder.toolbox.cli.ex.SSHConfigFormatException
77
import com.coder.toolbox.settings.CoderSettings
8-
import com.coder.toolbox.settings.CoderSettingsState
98
import com.coder.toolbox.util.CoderHostnameVerifier
109
import com.coder.toolbox.util.InvalidVersionException
1110
import com.coder.toolbox.util.OS
@@ -59,8 +58,8 @@ fun ensureCLI(
5958
context: CoderToolboxContext,
6059
deploymentURL: URL,
6160
buildVersion: String,
62-
settings: CoderSettings,
6361
): CoderCLIManager {
62+
val settings = context.settingsStore.readOnly()
6463
val cli = CoderCLIManager(deploymentURL, context.logger, settings)
6564

6665
// Short-circuit if we already have the expected version. This
@@ -123,7 +122,7 @@ class CoderCLIManager(
123122
private val deploymentURL: URL,
124123
private val logger: Logger,
125124
// Plugin configuration.
126-
private val settings: CoderSettings = CoderSettings(CoderSettingsState(), logger),
125+
private val settings: CoderSettings,
127126
// If the binary directory is not writable, this can be used to force the
128127
// manager to download to the data directory instead.
129128
forceDownloadToData: Boolean = false,
@@ -138,7 +137,7 @@ class CoderCLIManager(
138137
fun download(): Boolean {
139138
val eTag = getBinaryETag()
140139
val conn = remoteBinaryURL.openConnection() as HttpURLConnection
141-
if (settings.headerCommand.isNotBlank()) {
140+
if (!settings.headerCommand.isNullOrBlank()) {
142141
val headersFromHeaderCommand = getHeaders(deploymentURL, settings.headerCommand)
143142
for ((key, value) in headersFromHeaderCommand) {
144143
conn.setRequestProperty(key, value)
@@ -232,7 +231,7 @@ class CoderCLIManager(
232231
* Return the contents of the SSH config or null if it does not exist.
233232
*/
234233
private fun readSSHConfig(): String? = try {
235-
settings.sshConfigPath.toFile().readText()
234+
Path.of(settings.sshConfigPath).toFile().readText()
236235
} catch (e: FileNotFoundException) {
237236
null
238237
}
@@ -264,21 +263,21 @@ class CoderCLIManager(
264263
// always use the correct URL.
265264
"--url",
266265
escape(deploymentURL.toString()),
267-
if (settings.headerCommand.isNotBlank()) "--header-command" else null,
268-
if (settings.headerCommand.isNotBlank()) escapeSubcommand(settings.headerCommand) else null,
266+
if (!settings.headerCommand.isNullOrBlank()) "--header-command" else null,
267+
if (!settings.headerCommand.isNullOrBlank()) escapeSubcommand(settings.headerCommand) else null,
269268
"ssh",
270269
"--stdio",
271270
if (settings.disableAutostart && feats.disableAutostart) "--disable-autostart" else null,
272271
)
273272
val proxyArgs = baseArgs + listOfNotNull(
274-
if (settings.sshLogDirectory.isNotBlank()) "--log-dir" else null,
275-
if (settings.sshLogDirectory.isNotBlank()) escape(settings.sshLogDirectory) else null,
273+
if (!settings.sshLogDirectory.isNullOrBlank()) "--log-dir" else null,
274+
if (!settings.sshLogDirectory.isNullOrBlank()) escape(settings.sshLogDirectory) else null,
276275
if (feats.reportWorkspaceUsage) "--usage-app=toolbox" else null,
277276
)
278277
val backgroundProxyArgs =
279278
baseArgs + listOfNotNull(if (feats.reportWorkspaceUsage) "--usage-app=disable" else null)
280279
val extraConfig =
281-
if (settings.sshConfigOptions.isNotBlank()) {
280+
if (!settings.sshConfigOptions.isNullOrBlank()) {
282281
"\n" + settings.sshConfigOptions.prependIndent(" ")
283282
} else {
284283
""
@@ -379,10 +378,13 @@ class CoderCLIManager(
379378
*/
380379
private fun writeSSHConfig(contents: String?) {
381380
if (contents != null) {
382-
settings.sshConfigPath.parent.toFile().mkdirs()
383-
settings.sshConfigPath.toFile().writeText(contents)
381+
if (!settings.sshConfigPath.isNullOrBlank()) {
382+
val sshConfPath = Path.of(settings.sshConfigPath)
383+
sshConfPath.parent.toFile().mkdirs()
384+
sshConfPath.toFile().writeText(contents)
385+
}
384386
// The Coder cli will *not* create the log directory.
385-
if (settings.sshLogDirectory.isNotBlank()) {
387+
if (!settings.sshLogDirectory.isNullOrBlank()) {
386388
Path.of(settings.sshLogDirectory).toFile().mkdirs()
387389
}
388390
}

src/main/kotlin/com/coder/toolbox/sdk/CoderRestClient.kt

+1-3
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,6 @@ import com.coder.toolbox.sdk.v2.models.Workspace
1515
import com.coder.toolbox.sdk.v2.models.WorkspaceBuild
1616
import com.coder.toolbox.sdk.v2.models.WorkspaceResource
1717
import com.coder.toolbox.sdk.v2.models.WorkspaceTransition
18-
import com.coder.toolbox.settings.CoderSettings
19-
import com.coder.toolbox.settings.CoderSettingsState
2018
import com.coder.toolbox.util.CoderHostnameVerifier
2119
import com.coder.toolbox.util.coderSocketFactory
2220
import com.coder.toolbox.util.coderTrustManagers
@@ -53,11 +51,11 @@ open class CoderRestClient(
5351
context: CoderToolboxContext,
5452
val url: URL,
5553
val token: String?,
56-
private val settings: CoderSettings = CoderSettings(CoderSettingsState(), context.logger),
5754
private val proxyValues: ProxyValues? = null,
5855
private val pluginVersion: String = "development",
5956
existingHttpClient: OkHttpClient? = null,
6057
) {
58+
private val settings = context.settingsStore.readOnly()
6159
private val httpClient: OkHttpClient
6260
private val retroRestClient: CoderV2RestFacade
6361

src/main/kotlin/com/coder/toolbox/services/CoderSettingsService.kt

-60
This file was deleted.

0 commit comments

Comments
 (0)