Skip to content

Commit b2d31b8

Browse files
authored
Override connect timeout (#194)
1 parent ae17065 commit b2d31b8

File tree

1 file changed

+67
-15
lines changed

1 file changed

+67
-15
lines changed

src/remote.ts

+67-15
Original file line numberDiff line numberDiff line change
@@ -265,29 +265,64 @@ export class Remote {
265265
agent = matchingAgents[0]
266266
}
267267

268-
let remotePlatforms = this.vscodeProposed.workspace
268+
const hostname = authorityParts[1]
269+
const remotePlatforms = this.vscodeProposed.workspace
269270
.getConfiguration()
270-
.get<Record<string, string>>("remote.SSH.remotePlatform")
271-
remotePlatforms = {
272-
...remotePlatforms,
273-
[`${authorityParts[1]}`]: agent.operating_system,
274-
}
271+
.get<Record<string, string>>("remote.SSH.remotePlatform", {})
272+
const connTimeout = this.vscodeProposed.workspace
273+
.getConfiguration()
274+
.get<number | undefined>("remote.SSH.connectTimeout")
275275

276+
// We have to directly munge the settings file with jsonc because trying to
277+
// update properly through the extension API hangs indefinitely. Possibly
278+
// VS Code is trying to update configuration on the remote, which cannot
279+
// connect until we finish here leading to a deadlock. We need to update it
280+
// locally, anyway, and it does not seem possible to force that via API.
276281
let settingsContent = "{}"
277282
try {
278283
settingsContent = await fs.readFile(this.storage.getUserSettingsPath(), "utf8")
279284
} catch (ex) {
280285
// Ignore! It's probably because the file doesn't exist.
281286
}
282-
const parsed = jsonc.parse(settingsContent)
283-
parsed["remote.SSH.remotePlatform"] = remotePlatforms
284-
const edits = jsonc.modify(settingsContent, ["remote.SSH.remotePlatform"], remotePlatforms, {})
285-
try {
286-
await fs.writeFile(this.storage.getUserSettingsPath(), jsonc.applyEdits(settingsContent, edits))
287-
} catch (ex) {
288-
// The user will just be prompted instead, which is fine!
289-
// If a user's settings.json is read-only, then we can't write to it.
290-
// This is the case when using home-manager on NixOS.
287+
288+
// Add the remote platform for this host to bypass a step where VS Code asks
289+
// the user for the platform.
290+
let mungedPlatforms = false
291+
if (!remotePlatforms[hostname] || remotePlatforms[hostname] !== agent.operating_system) {
292+
remotePlatforms[hostname] = agent.operating_system
293+
settingsContent = jsonc.applyEdits(
294+
settingsContent,
295+
jsonc.modify(settingsContent, ["remote.SSH.remotePlatform"], remotePlatforms, {}),
296+
)
297+
mungedPlatforms = true
298+
}
299+
300+
// VS Code ignores the connect timeout in the SSH config and uses a default
301+
// of 15 seconds, which can be too short in the case where we wait for
302+
// startup scripts. For now we hardcode a longer value. Because this is
303+
// potentially overwriting user configuration, it feels a bit sketchy. If
304+
// microsoft/vscode-remote-release#8519 is resolved we can remove this but
305+
// for now to mitigate the sketchiness we will reset it after connecting.
306+
const minConnTimeout = 1800
307+
let mungedConnTimeout = false
308+
if (!connTimeout || connTimeout < minConnTimeout) {
309+
settingsContent = jsonc.applyEdits(
310+
settingsContent,
311+
jsonc.modify(settingsContent, ["remote.SSH.connectTimeout"], minConnTimeout, {}),
312+
)
313+
mungedConnTimeout = true
314+
}
315+
316+
if (mungedPlatforms || mungedConnTimeout) {
317+
try {
318+
await fs.writeFile(this.storage.getUserSettingsPath(), settingsContent)
319+
} catch (ex) {
320+
// This could be because the user's settings.json is read-only. This is
321+
// the case when using home-manager on NixOS, for example. Failure to
322+
// write here is not necessarily catastrophic since the user will be
323+
// asked for the platform and the default timeout might be sufficient.
324+
mungedPlatforms = mungedConnTimeout = false
325+
}
291326
}
292327

293328
const workspaceUpdate = new vscode.EventEmitter<Workspace>()
@@ -431,6 +466,23 @@ export class Remote {
431466
await this.updateSSHConfig(authorityParts[1], hasCoderLogs)
432467

433468
this.findSSHProcessID().then((pid) => {
469+
// Once the SSH process has spawned we can reset the timeout.
470+
if (mungedConnTimeout) {
471+
// Re-read settings in case they changed.
472+
fs.readFile(this.storage.getUserSettingsPath(), "utf8").then(async (rawSettings) => {
473+
try {
474+
await fs.writeFile(
475+
this.storage.getUserSettingsPath(),
476+
jsonc.applyEdits(rawSettings, jsonc.modify(rawSettings, ["remote.SSH.connectTimeout"], connTimeout, {})),
477+
)
478+
} catch (error) {
479+
this.storage.writeToCoderOutputChannel(
480+
`Failed to reset remote.SSH.connectTimeout back to ${connTimeout}: ${error}`,
481+
)
482+
}
483+
})
484+
}
485+
434486
if (!pid) {
435487
// TODO: Show an error here!
436488
return

0 commit comments

Comments
 (0)