Skip to content

Commit 2a84a8e

Browse files
committed
Add extra help for certificate errors
1 parent bd7e426 commit 2a84a8e

File tree

2 files changed

+63
-26
lines changed

2 files changed

+63
-26
lines changed

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

+49-23
Original file line numberDiff line numberDiff line change
@@ -77,8 +77,10 @@ import java.net.ConnectException
7777
import java.net.SocketTimeoutException
7878
import java.net.URL
7979
import java.net.UnknownHostException
80+
import javax.net.ssl.SSLHandshakeException
8081
import javax.swing.Icon
8182
import javax.swing.JCheckBox
83+
import javax.swing.JLabel
8284
import javax.swing.JTable
8385
import javax.swing.JTextField
8486
import javax.swing.ListSelectionModel
@@ -101,6 +103,7 @@ class CoderWorkspacesStepView(val setNextButtonEnabled: (Boolean) -> Unit) : Cod
101103
private val appPropertiesService: PropertiesComponent = service()
102104

103105
private var tfUrl: JTextField? = null
106+
private var tfUrlComment: JLabel? = null
104107
private var cbExistingToken: JCheckBox? = null
105108

106109
private val notificationBanner = NotificationBanner()
@@ -116,7 +119,7 @@ class CoderWorkspacesStepView(val setNextButtonEnabled: (Boolean) -> Unit) : Cod
116119
minWidth = JBUI.scale(52)
117120
}
118121
rowHeight = 48
119-
setEmptyState("Disconnected")
122+
setEmptyState(CoderGatewayBundle.message("gateway.connector.view.coder.workspaces.connect.text.disconnected"))
120123
setSelectionMode(ListSelectionModel.SINGLE_SELECTION)
121124
selectionModel.addListSelectionListener {
122125
setNextButtonEnabled(selectedObject?.agentStatus?.ready() == true && selectedObject?.agentOS == OS.LINUX)
@@ -186,6 +189,16 @@ class CoderWorkspacesStepView(val setNextButtonEnabled: (Boolean) -> Unit) : Cod
186189
background = WelcomeScreenUIManager.getMainAssociatedComponentBackground()
187190
}
188191
}.layout(RowLayout.PARENT_GRID)
192+
row {
193+
cell() // Empty cells for alignment.
194+
tfUrlComment = cell(
195+
ComponentPanelBuilder.createCommentComponent(
196+
CoderGatewayBundle.message("gateway.connector.view.coder.workspaces.connect.text.comment",
197+
CoderGatewayBundle.message("gateway.connector.view.coder.workspaces.connect.text")),
198+
false, -1, true
199+
)
200+
).resizableColumn().align(AlignX.FILL).component
201+
}.layout(RowLayout.PARENT_GRID)
189202
row {
190203
cell() // Empty cell for alignment.
191204
cbExistingToken = checkBox(CoderGatewayBundle.message("gateway.connector.view.login.existing-token.label"))
@@ -410,7 +423,9 @@ class CoderWorkspacesStepView(val setNextButtonEnabled: (Boolean) -> Unit) : Cod
410423
// Clear out old deployment details.
411424
cliManager = null
412425
poller?.cancel()
413-
tableOfWorkspaces.setEmptyState("Connecting to $deploymentURL...")
426+
tfUrlComment?.foreground = UIUtil.getContextHelpForeground()
427+
tfUrlComment?.text = CoderGatewayBundle.message("gateway.connector.view.coder.workspaces.connect.text.connecting", deploymentURL.host)
428+
tableOfWorkspaces.setEmptyState(CoderGatewayBundle.message("gateway.connector.view.coder.workspaces.connect.text.connecting", deploymentURL.host))
414429
tableOfWorkspaces.listTableModel.items = emptyList()
415430

416431
// Authenticate and load in a background process with progress.
@@ -444,44 +459,55 @@ class CoderWorkspacesStepView(val setNextButtonEnabled: (Boolean) -> Unit) : Cod
444459
triggerWorkspacePolling(false)
445460

446461
cliManager = cli
447-
tableOfWorkspaces.setEmptyState("Connected to $deploymentURL")
462+
tableOfWorkspaces.setEmptyState(CoderGatewayBundle.message("gateway.connector.view.coder.workspaces.connect.text.connected", deploymentURL.host))
463+
tfUrlComment?.text = CoderGatewayBundle.message("gateway.connector.view.coder.workspaces.connect.text.connected", deploymentURL.host)
448464
} catch (e: Exception) {
449-
val errorSummary = when (e) {
450-
is java.nio.file.AccessDeniedException -> "Access denied to ${e.file}"
451-
is UnknownHostException -> "Unknown host ${e.message}"
452-
is InvalidExitValueException -> "CLI exited unexpectedly with ${e.exitValue}"
453-
else -> e.message ?: "No reason was provided"
454-
}
455-
var msg = CoderGatewayBundle.message(
456-
"gateway.connector.view.workspaces.connect.failed",
457-
deploymentURL,
458-
errorSummary,
459-
)
460-
when (e) {
465+
val reason = e.message ?: CoderGatewayBundle.message("gateway.connector.view.workspaces.connect.no-reason")
466+
val msg = when (e) {
467+
is java.nio.file.AccessDeniedException -> CoderGatewayBundle.message("gateway.connector.view.workspaces.connect.access-denied", e.file)
468+
is UnknownHostException -> CoderGatewayBundle.message("gateway.connector.view.workspaces.connect.unknown-host", e.message ?: deploymentURL.host)
469+
is InvalidExitValueException -> CoderGatewayBundle.message("gateway.connector.view.workspaces.connect.unexpected-exit", e.exitValue)
461470
is AuthenticationResponseException -> {
462-
msg = CoderGatewayBundle.message(
471+
CoderGatewayBundle.message(
463472
"gateway.connector.view.workspaces.connect.unauthorized",
464473
deploymentURL,
465474
)
466-
cs.launch { onAuthFailure?.invoke() }
467475
}
468-
469476
is SocketTimeoutException -> {
470-
msg = CoderGatewayBundle.message(
477+
CoderGatewayBundle.message(
471478
"gateway.connector.view.workspaces.connect.timeout",
472479
deploymentURL,
473480
)
474481
}
475-
476482
is ResponseException, is ConnectException -> {
477-
msg = CoderGatewayBundle.message(
483+
CoderGatewayBundle.message(
478484
"gateway.connector.view.workspaces.connect.download-failed",
479-
errorSummary,
485+
reason,
486+
)
487+
}
488+
is SSLHandshakeException -> {
489+
CoderGatewayBundle.message(
490+
"gateway.connector.view.workspaces.connect.ssl-error",
491+
deploymentURL.host,
492+
reason,
480493
)
481494
}
495+
else -> reason
482496
}
483-
tableOfWorkspaces.setEmptyState(msg)
497+
// It would be nice to place messages directly into the table
498+
// but it does not support wrapping or markup so place it in the
499+
// comment field of the URL input instead.
500+
tfUrlComment?.foreground = UIUtil.getErrorForeground()
501+
tfUrlComment?.text = msg
502+
tableOfWorkspaces.setEmptyState(CoderGatewayBundle.message(
503+
"gateway.connector.view.workspaces.connect.failed",
504+
deploymentURL.host,
505+
))
484506
logger.error(msg, e)
507+
508+
if (e is AuthenticationResponseException) {
509+
cs.launch { onAuthFailure?.invoke() }
510+
}
485511
}
486512
}
487513
}

src/main/resources/messages/CoderGatewayBundle.properties

+14-3
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,10 @@ gateway.connector.view.login.token.label=Session Token:
1010
gateway.connector.view.coder.workspaces.header.text=Coder Workspaces
1111
gateway.connector.view.coder.workspaces.comment=Self-hosted developer workspaces in the cloud or on-premises. Coder empowers developers with secure, consistent, and fast developer workspaces.
1212
gateway.connector.view.coder.workspaces.connect.text=Connect
13+
gateway.connector.view.coder.workspaces.connect.text.comment=Please enter your deployment URL and press "{0}".
14+
gateway.connector.view.coder.workspaces.connect.text.disconnected=Disconnected
15+
gateway.connector.view.coder.workspaces.connect.text.connected=Connected to {0}
16+
gateway.connector.view.coder.workspaces.connect.text.connecting=Connecting to {0}...
1317
gateway.connector.view.coder.workspaces.cli.downloader.dialog.title=Authenticate and setup Coder
1418
gateway.connector.view.coder.workspaces.next.text=Select IDE and project
1519
gateway.connector.view.coder.workspaces.dashboard.text=Open dashboard
@@ -19,12 +23,19 @@ gateway.connector.view.coder.workspaces.stop.text=Stop workspace
1923
gateway.connector.view.coder.workspaces.update.text=Update workspace template
2024
gateway.connector.view.coder.workspaces.create.text=Create workspace
2125
gateway.connector.view.coder.workspaces.unsupported.os.info=Gateway supports only Linux machines. Support for macOS and Windows is planned.
22-
gateway.connector.view.coder.workspaces.invalid.coder.version=Could not parse Coder version {0}. Coder Gateway plugin might not be compatible with this version. <a href='https://coder.com/docs/coder-oss/latest/ides/gateway#creating-a-new-jetbrains-gateway-connection'>Connect to a Coder workspace manually</a>
23-
gateway.connector.view.coder.workspaces.unsupported.coder.version=Coder version {0} might not be compatible with this plugin version. <a href='https://coder.com/docs/coder-oss/latest/ides/gateway#creating-a-new-jetbrains-gateway-connection'>Connect to a Coder workspace manually</a>
26+
gateway.connector.view.coder.workspaces.invalid.coder.version=Could not parse Coder version {0}. Coder Gateway plugin might not be compatible with this version. <a href='https://coder.com/docs/v2/latest/ides/gateway#creating-a-new-jetbrains-gateway-connection'>Connect to a Coder workspace manually</a>
27+
gateway.connector.view.coder.workspaces.unsupported.coder.version=Coder version {0} might not be compatible with this plugin version. <a href='https://coder.com/docs/v2/latest/ides/gateway#creating-a-new-jetbrains-gateway-connection'>Connect to a Coder workspace manually</a>
28+
gateway.connector.view.workspaces.connect.failed=Connection to {0} failed. See above for details.
29+
gateway.connector.view.workspaces.connect.no-reason=No reason was provided.
30+
gateway.connector.view.workspaces.connect.access-denied=Access denied to {0}.
31+
gateway.connector.view.workspaces.connect.unknown-host=Unknown host {0}.
32+
gateway.connector.view.workspaces.connect.unexpected-exit=CLI exited unexpectedly with {0}.
2433
gateway.connector.view.workspaces.connect.unauthorized=Token was rejected by {0}; has your token expired?
2534
gateway.connector.view.workspaces.connect.timeout=Unable to connect to {0}; is it up?
2635
gateway.connector.view.workspaces.connect.download-failed=Failed to download Coder CLI: {0}
27-
gateway.connector.view.workspaces.connect.failed=Failed to connect to {0}: {1}
36+
gateway.connector.view.workspaces.connect.ssl-error=Connection to {0} failed: {1}. See the \
37+
<a href='https://coder.com/docs/v2/latest/ides/gateway#configuring-the-gateway-plugin-to-use-internal-certificates'>documentation for TLS certificates</a> \
38+
for information on how to make your system trust certificates coming from your deployment.
2839
gateway.connector.view.workspaces.token.comment=The last used token is shown above.
2940
gateway.connector.view.workspaces.token.rejected=This token was rejected.
3041
gateway.connector.view.workspaces.token.injected=This token was pulled from your CLI config.

0 commit comments

Comments
 (0)