From b1e966082363dac6ace8e44d1a6ae045568d3725 Mon Sep 17 00:00:00 2001 From: Kyle Carberry Date: Sat, 4 Sep 2021 16:13:52 -0500 Subject: [PATCH 1/6] Resolve remote authority to initialize tunnel API --- .../extensions/browser/extensionService.ts | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/lib/vscode/src/vs/workbench/services/extensions/browser/extensionService.ts b/lib/vscode/src/vs/workbench/services/extensions/browser/extensionService.ts index 1ab766411c38..3f41d09d20b0 100644 --- a/lib/vscode/src/vs/workbench/services/extensions/browser/extensionService.ts +++ b/lib/vscode/src/vs/workbench/services/extensions/browser/extensionService.ts @@ -9,7 +9,7 @@ import { IWorkbenchExtensionEnablementService, IWebExtensionsScannerService } fr import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; -import { IExtensionService, IExtensionHost } from 'vs/workbench/services/extensions/common/extensions'; +import { IExtensionService, IExtensionHost, ExtensionHostKind, } from 'vs/workbench/services/extensions/common/extensions'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { IFileService } from 'vs/platform/files/common/files'; import { IProductService } from 'vs/platform/product/common/productService'; @@ -28,6 +28,7 @@ import { IExtensionManagementService } from 'vs/platform/extensionManagement/com import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; import { IExtensionManifestPropertiesService } from 'vs/workbench/services/extensions/common/extensionManifestPropertiesService'; import { IUserDataInitializationService } from 'vs/workbench/services/userData/browser/userDataInit'; +import { IRemoteExplorerService } from '../../remote/common/remoteExplorerService'; export class ExtensionService extends AbstractExtensionService implements IExtensionService { @@ -51,6 +52,7 @@ export class ExtensionService extends AbstractExtensionService implements IExten @ILifecycleService private readonly _lifecycleService: ILifecycleService, @IExtensionManifestPropertiesService extensionManifestPropertiesService: IExtensionManifestPropertiesService, @IUserDataInitializationService private readonly _userDataInitializationService: IUserDataInitializationService, + @IRemoteExplorerService private readonly _remoteExplorerService: IRemoteExplorerService ) { super( new ExtensionRunningLocationClassifier( @@ -75,7 +77,17 @@ export class ExtensionService extends AbstractExtensionService implements IExten // Initialize installed extensions first and do it only after workbench is ready this._lifecycleService.when(LifecyclePhase.Ready).then(async () => { await this._userDataInitializationService.initializeInstalledExtensions(this._instantiationService); - this._initialize(); + this._initialize().then(async () => { + try { + const extHost = this._getExtensionHostManager(ExtensionHostKind.Remote) + const resolved = await extHost?.resolveAuthority("coder-link+main") + if (resolved) { + this._remoteExplorerService.setTunnelInformation(resolved.tunnelInformation); + } + } catch (ex) { + this._logOrShowMessage(Severity.Error, nls.localize('link', "Failed to initialize remote Link authority: {0}", ex)) + } + }) }); this._initFetchFileSystem(); From 404315358304dbbe535ce52e9efa1eb4c77697cb Mon Sep 17 00:00:00 2001 From: Kyle Carberry Date: Wed, 8 Sep 2021 11:37:54 -0500 Subject: [PATCH 2/6] Fix linting --- .../services/extensions/browser/extensionService.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/vscode/src/vs/workbench/services/extensions/browser/extensionService.ts b/lib/vscode/src/vs/workbench/services/extensions/browser/extensionService.ts index 3f41d09d20b0..f56ced3dab5c 100644 --- a/lib/vscode/src/vs/workbench/services/extensions/browser/extensionService.ts +++ b/lib/vscode/src/vs/workbench/services/extensions/browser/extensionService.ts @@ -79,15 +79,15 @@ export class ExtensionService extends AbstractExtensionService implements IExten await this._userDataInitializationService.initializeInstalledExtensions(this._instantiationService); this._initialize().then(async () => { try { - const extHost = this._getExtensionHostManager(ExtensionHostKind.Remote) - const resolved = await extHost?.resolveAuthority("coder-link+main") + const extHost = this._getExtensionHostManager(ExtensionHostKind.Remote); + const resolved = await extHost?.resolveAuthority('coder-link+main'); if (resolved) { this._remoteExplorerService.setTunnelInformation(resolved.tunnelInformation); } } catch (ex) { - this._logOrShowMessage(Severity.Error, nls.localize('link', "Failed to initialize remote Link authority: {0}", ex)) + this._logOrShowMessage(Severity.Error, nls.localize('link', "Failed to initialize remote Link authority: {0}", ex)); } - }) + }); }); this._initFetchFileSystem(); From c7a5263b38c970d4c95e0bf8226d1b94f3032148 Mon Sep 17 00:00:00 2001 From: Kyle Carberry Date: Wed, 8 Sep 2021 11:43:47 -0500 Subject: [PATCH 3/6] Add comment --- .../services/extensions/browser/extensionService.ts | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/lib/vscode/src/vs/workbench/services/extensions/browser/extensionService.ts b/lib/vscode/src/vs/workbench/services/extensions/browser/extensionService.ts index f56ced3dab5c..dead7008488c 100644 --- a/lib/vscode/src/vs/workbench/services/extensions/browser/extensionService.ts +++ b/lib/vscode/src/vs/workbench/services/extensions/browser/extensionService.ts @@ -79,8 +79,15 @@ export class ExtensionService extends AbstractExtensionService implements IExten await this._userDataInitializationService.initializeInstalledExtensions(this._instantiationService); this._initialize().then(async () => { try { + // This enables the `vscode.workspace.registerRemoteAuthorityResolver` API to be executed. + // + // It's specifically scoped to the "coder-link" scheme at the moment to reduce external + // dependency on forking VS Code functionality. + // + // The remote host doesn't resolve to an extension host like the API expects, but instead + // we only utilize the tunnel functionality. const extHost = this._getExtensionHostManager(ExtensionHostKind.Remote); - const resolved = await extHost?.resolveAuthority('coder-link+main'); + const resolved = await extHost?.resolveAuthority('coder-link+web'); if (resolved) { this._remoteExplorerService.setTunnelInformation(resolved.tunnelInformation); } From d39a141d42429a5aff22b918d90f1543d3527673 Mon Sep 17 00:00:00 2001 From: Kyle Carberry Date: Thu, 9 Sep 2021 16:24:04 -0500 Subject: [PATCH 4/6] Add linkup command to enable linking The linkup command is the successor to coder-cloud. It enables port-forwarding via URL, uses WebRTC internally, and will enable greater linking functionality longer-term. A Link extension will be published once this is merged. We are not removing the old link functionality yet... we eventually will, but want to ensure a smooth transition. --- .gitignore | 1 + ci/build/build-code-server.sh | 14 ++++++++++++++ ci/build/build-release.sh | 1 + ci/build/npm-postinstall.sh | 6 ++++++ src/node/link.ts | 23 +++++++++++++++++++++++ src/node/main.ts | 10 ++++++++++ 6 files changed, 55 insertions(+) create mode 100644 src/node/link.ts diff --git a/.gitignore b/.gitignore index 8ac5641036d5..5c922b9d6817 100644 --- a/.gitignore +++ b/.gitignore @@ -12,6 +12,7 @@ node_modules node-* /plugins /lib/coder-cloud-agent +/lib/linkup .home coverage **/.DS_Store diff --git a/ci/build/build-code-server.sh b/ci/build/build-code-server.sh index c17725393435..61932f88a704 100755 --- a/ci/build/build-code-server.sh +++ b/ci/build/build-code-server.sh @@ -29,6 +29,20 @@ main() { set -e fi + if ! [ -f ./lib/linkup ]; then + echo "Downloading Link agent..." + + # for arch; we do not use OS from lib.sh and get our own. + # lib.sh normalizes macos to darwin - but cloud-agent's binaries do not + source ./ci/lib.sh + OS="$(uname | tr '[:upper:]' '[:lower:]')" + + set +e + curl -fsSL "https://storage.googleapis.com/coder-link-releases/latest/linkup-$OS-$ARCH" -o ./lib/linkup + chmod +x ./lib/linkup + set -e + fi + yarn browserify out/browser/register.js -o out/browser/register.browserified.js yarn browserify out/browser/pages/login.js -o out/browser/pages/login.browserified.js yarn browserify out/browser/pages/vscode.js -o out/browser/pages/vscode.browserified.js diff --git a/ci/build/build-release.sh b/ci/build/build-release.sh index fe788d7c886d..2f61c9f9bed3 100755 --- a/ci/build/build-release.sh +++ b/ci/build/build-release.sh @@ -60,6 +60,7 @@ EOF rsync node_modules/ "$RELEASE_PATH/node_modules" mkdir -p "$RELEASE_PATH/lib" rsync ./lib/coder-cloud-agent "$RELEASE_PATH/lib" + rsync ./lib/linkup "$RELEASE_PATH/lib" fi } diff --git a/ci/build/npm-postinstall.sh b/ci/build/npm-postinstall.sh index a1110ccd354b..469f2ee5f168 100755 --- a/ci/build/npm-postinstall.sh +++ b/ci/build/npm-postinstall.sh @@ -63,6 +63,12 @@ main() { echo "Failed to download cloud agent; --link will not work" fi + if curl -fsSL "https://storage.googleapis.com/coder-link-releases/latest/linkup-$OS-$ARCH" -o ./lib/linkup; then + chmod +x ./lib/linkup + else + echo "Failed to download Link agent; the Link extension will not work" + fi + if ! vscode_yarn; then echo "You may not have the required dependencies to build the native modules." echo "Please see https://github.com/cdr/code-server/blob/master/docs/npm.md" diff --git a/src/node/link.ts b/src/node/link.ts new file mode 100644 index 000000000000..633c193ed1f0 --- /dev/null +++ b/src/node/link.ts @@ -0,0 +1,23 @@ +import { logger } from "@coder/logger" +import { spawn } from "child_process" +import path from "path" + +export function startLink(port: number): Promise { + logger.debug(`running link targetting ${port}`) + + const agent = spawn(path.resolve(__dirname, "../../lib/linkup"), ["--devurl", `code:${port}:code-server`], { + stdio: "ignore", + shell: false, + }) + return new Promise((res, rej) => { + agent.on("error", rej) + agent.on("close", (code) => { + if (code !== 0) { + return rej({ + message: `Link exited with ${code}`, + }) + } + res() + }) + }) +} diff --git a/src/node/main.ts b/src/node/main.ts index e00364135f50..e22496e1e3d1 100644 --- a/src/node/main.ts +++ b/src/node/main.ts @@ -8,6 +8,7 @@ import { createApp, ensureAddress } from "./app" import { AuthType, DefaultedArgs, Feature } from "./cli" import { coderCloudBind } from "./coder_cloud" import { commit, version } from "./constants" +import { startLink } from "./link" import { register } from "./routes" import { humanPath, isFile, open } from "./util" @@ -128,6 +129,15 @@ export const runCodeServer = async (args: DefaultedArgs): Promise = await coderCloudBind(serverAddress.replace(/^https?:\/\//, ""), args.link.value) logger.info(" - Connected to cloud agent") } + + try { + const port = parseInt(serverAddress.split(":").pop() as string, 10) + startLink(port).catch((ex) => { + logger.debug("Link daemon exited!", field("error", ex)) + }) + } catch (ex) { + logger.debug("Failed to start link daemon!", ex) + } if (args.enable && args.enable.length > 0) { logger.info("Enabling the following experimental features:") From bece429508fdd25f45c41a37ce42b337dcef08a9 Mon Sep 17 00:00:00 2001 From: Kyle Carberry Date: Thu, 9 Sep 2021 16:26:05 -0500 Subject: [PATCH 5/6] fmt --- src/node/main.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/node/main.ts b/src/node/main.ts index e22496e1e3d1..b9ed82dae90e 100644 --- a/src/node/main.ts +++ b/src/node/main.ts @@ -129,7 +129,7 @@ export const runCodeServer = async (args: DefaultedArgs): Promise = await coderCloudBind(serverAddress.replace(/^https?:\/\//, ""), args.link.value) logger.info(" - Connected to cloud agent") } - + try { const port = parseInt(serverAddress.split(":").pop() as string, 10) startLink(port).catch((ex) => { From 5551751122443ec38453cdfcc700d5adbce2b74d Mon Sep 17 00:00:00 2001 From: Kyle Carberry Date: Fri, 10 Sep 2021 12:50:57 -0500 Subject: [PATCH 6/6] Fix proposed extension --- lib/vscode/product.json | 3 ++- src/node/link.ts | 1 - 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/vscode/product.json b/lib/vscode/product.json index 787f4f9c44c3..1bccecd53e3d 100644 --- a/lib/vscode/product.json +++ b/lib/vscode/product.json @@ -28,7 +28,8 @@ "ms-vscode.remotehub", "ms-vscode.remotehub-insiders", "GitHub.remotehub", - "GitHub.remotehub-insiders" + "GitHub.remotehub-insiders", + "coder.vscode-link" ], "builtInExtensions": [ { diff --git a/src/node/link.ts b/src/node/link.ts index 633c193ed1f0..5dfe795228da 100644 --- a/src/node/link.ts +++ b/src/node/link.ts @@ -6,7 +6,6 @@ export function startLink(port: number): Promise { logger.debug(`running link targetting ${port}`) const agent = spawn(path.resolve(__dirname, "../../lib/linkup"), ["--devurl", `code:${port}:code-server`], { - stdio: "ignore", shell: false, }) return new Promise((res, rej) => {