fix: use legacy server as default with additional warnings (#204377)

* ci: switch to glibc 2.17 remote server

* chore: signal user about unsupported connection

* chore: address review comments

* chore: update nodejs build

* chore: bump distro

* chore: lower the minimum requirements

* fix: glibc version check

* chore: remove explicit connection disposal
This commit is contained in:
Robo
2024-02-06 19:25:58 +09:00
committed by GitHub
parent 6a315f2d43
commit cf7ddbb51d
18 changed files with 298 additions and 162 deletions

View File

@@ -1 +1 @@
2024-01-29T19:26:27.993Z
2024-02-05T09:34:15.476Z

View File

@@ -34,11 +34,6 @@ if [ "$npm_config_arch" == "x64" ]; then
export CXX="$PWD/.build/CR_Clang/bin/clang++ --gcc-toolchain=$VSCODE_SYSROOT_DIR/x86_64-linux-gnu"
export CXXFLAGS="-nostdinc++ -D__NO_INLINE__ -I$PWD/.build/libcxx_headers -isystem$PWD/.build/libcxx_headers/include -isystem$PWD/.build/libcxxabi_headers/include -fPIC -flto=thin -fsplit-lto-unit -D_LIBCPP_ABI_NAMESPACE=Cr --sysroot=$VSCODE_SYSROOT_DIR/x86_64-linux-gnu/x86_64-linux-gnu/sysroot"
export LDFLAGS="-stdlib=libc++ --sysroot=$VSCODE_SYSROOT_DIR/x86_64-linux-gnu/x86_64-linux-gnu/sysroot -fuse-ld=lld -flto=thin -L$PWD/.build/libcxx-objects -lc++abi -L$VSCODE_SYSROOT_DIR/x86_64-linux-gnu/x86_64-linux-gnu/sysroot/usr/lib/x86_64-linux-gnu -L$VSCODE_SYSROOT_DIR/x86_64-linux-gnu/x86_64-linux-gnu/sysroot/lib/x86_64-linux-gnu -Wl,--lto-O0"
# Set compiler toolchain for remote server
export VSCODE_REMOTE_CC=$VSCODE_SYSROOT_DIR/x86_64-linux-gnu/bin/x86_64-linux-gnu-gcc
export VSCODE_REMOTE_CXX=$VSCODE_SYSROOT_DIR/x86_64-linux-gnu/bin/x86_64-linux-gnu-g++
export VSCODE_REMOTE_CXXFLAGS="--sysroot=$VSCODE_SYSROOT_DIR/x86_64-linux-gnu/x86_64-linux-gnu/sysroot"
export VSCODE_REMOTE_LDFLAGS="--sysroot=$VSCODE_SYSROOT_DIR/x86_64-linux-gnu/x86_64-linux-gnu/sysroot -L$VSCODE_SYSROOT_DIR/x86_64-linux-gnu/x86_64-linux-gnu/sysroot/usr/lib/x86_64-linux-gnu -L$VSCODE_SYSROOT_DIR/x86_64-linux-gnu/x86_64-linux-gnu/sysroot/lib/x86_64-linux-gnu"
elif [ "$npm_config_arch" == "arm64" ]; then
# Set compiler toolchain for client native modules and remote server
export CC=$VSCODE_SYSROOT_DIR/aarch64-linux-gnu/bin/aarch64-linux-gnu-gcc

View File

@@ -121,53 +121,23 @@ steps:
ELECTRON_SKIP_BINARY_DOWNLOAD: 1
PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: 1
GITHUB_TOKEN: "$(github-distro-mixin-password)"
VSCODE_HOST_MOUNT: "/mnt/vss/_work/1/s"
${{ if or(eq(parameters.VSCODE_ARCH, 'x64'), eq(parameters.VSCODE_ARCH, 'arm64')) }}:
VSCODE_REMOTE_DEPENDENCIES_CONTAINER_NAME: vscodehub.azurecr.io/vscode-linux-build-agent:centos7-devtoolset8-$(VSCODE_ARCH)
${{ if eq(parameters.VSCODE_ARCH, 'armhf') }}:
VSCODE_REMOTE_DEPENDENCIES_CONTAINER_NAME: vscodehub.azurecr.io/vscode-linux-build-agent:bionic-arm32v7
displayName: Install dependencies (non-OSS)
condition: and(succeeded(), ne(variables.NODE_MODULES_RESTORED, 'true'))
- script: |
set -e
- ${{ if or(eq(parameters.VSCODE_ARCH, 'x64'), eq(parameters.VSCODE_ARCH, 'arm64')) }}:
- script: |
set -e
TRIPLE="x86_64-linux-gnu"
if [ "$VSCODE_ARCH" == "arm64" ]; then
TRIPLE="aarch64-linux-gnu"
elif [ "$VSCODE_ARCH" == "armhf" ]; then
TRIPLE="arm-rpi-linux-gnueabihf"
fi
# Get all files with .node extension from remote/node_modules folder
files=$(find remote/node_modules -name "*.node")
# Check if any file has dependency of GLIBC > 2.28 or GLIBCXX > 3.4.25
for file in $files; do
glibc_version="2.28"
glibcxx_version="3.4.25"
while IFS= read -r line; do
if [[ $line == *"GLIBC_"* ]]; then
version=$(echo "$line" | awk '{print $5}' | tr -d '()')
version=${version#*_}
if [[ $(printf "%s\n%s" "$version" "$glibc_version" | sort -V | tail -n1) == "$version" ]]; then
glibc_version=$version
fi
elif [[ $line == *"GLIBCXX_"* ]]; then
version=$(echo "$line" | awk '{print $5}' | tr -d '()')
version=${version#*_}
if [[ $(printf "%s\n%s" "$version" "$glibcxx_version" | sort -V | tail -n1) == "$version" ]]; then
glibcxx_version=$version
fi
fi
done < <("$PWD/.build/sysroots/$TRIPLE/$TRIPLE/bin/objdump" -T "$file")
if [[ "$glibc_version" != "2.28" ]]; then
echo "Error: File $file has dependency on GLIBC > 2.28"
exit 1
fi
if [[ "$glibcxx_version" != "3.4.25" ]]; then
echo "Error: File $file has dependency on GLIBCXX > 3.4.25"
exit 1
fi
done
condition: and(succeeded(), ne(variables.NODE_MODULES_RESTORED, 'true'))
displayName: Check GLIBC and GLIBCXX dependencies in remote/node_modules
EXPECTED_GLIBC_VERSION="2.17" \
EXPECTED_GLIBCXX_VERSION="3.4.19" \
./build/azure-pipelines/linux/verify-glibc-requirements.sh
condition: and(succeeded(), ne(variables.NODE_MODULES_RESTORED, 'true'))
displayName: Check GLIBC and GLIBCXX dependencies in remote/node_modules
- script: node build/azure-pipelines/distro/mixin-npm
condition: and(succeeded(), ne(variables.NODE_MODULES_RESTORED, 'true'))
@@ -248,6 +218,7 @@ steps:
- script: |
set -e
export VSCODE_NODE_GLIBC='-glibc-2.17'
yarn gulp vscode-reh-linux-$(VSCODE_ARCH)-min-ci
mv ../vscode-reh-linux-$(VSCODE_ARCH) ../vscode-server-linux-$(VSCODE_ARCH) # TODO@joaomoreno
ARCHIVE_PATH=".build/linux/server/vscode-server-linux-$(VSCODE_ARCH).tar.gz"
@@ -260,6 +231,7 @@ steps:
- script: |
set -e
export VSCODE_NODE_GLIBC='-glibc-2.17'
yarn gulp vscode-reh-web-linux-$(VSCODE_ARCH)-min-ci
mv ../vscode-reh-web-linux-$(VSCODE_ARCH) ../vscode-server-linux-$(VSCODE_ARCH)-web # TODO@joaomoreno
ARCHIVE_PATH=".build/linux/web/vscode-server-linux-$(VSCODE_ARCH)-web.tar.gz"

View File

@@ -0,0 +1,44 @@
#!/usr/bin/env bash
set -e
TRIPLE="x86_64-linux-gnu"
if [ "$VSCODE_ARCH" == "arm64" ]; then
TRIPLE="aarch64-linux-gnu"
elif [ "$VSCODE_ARCH" == "armhf" ]; then
TRIPLE="arm-rpi-linux-gnueabihf"
fi
# Get all files with .node extension from remote/node_modules folder
files=$(find remote/node_modules -name "*.node" -not -path "*prebuilds*")
echo "Verifying requirements for files: $files"
for file in $files; do
glibc_version="$EXPECTED_GLIBC_VERSION"
glibcxx_version="$EXPECTED_GLIBCXX_VERSION"
while IFS= read -r line; do
if [[ $line == *"GLIBC_"* ]]; then
version=$(echo "$line" | awk '{print $5}' | tr -d '()')
version=${version#*_}
if [[ $(printf "%s\n%s" "$version" "$glibc_version" | sort -V | tail -n1) == "$version" ]]; then
glibc_version=$version
fi
elif [[ $line == *"GLIBCXX_"* ]]; then
version=$(echo "$line" | awk '{print $5}' | tr -d '()')
version=${version#*_}
if [[ $(printf "%s\n%s" "$version" "$glibcxx_version" | sort -V | tail -n1) == "$version" ]]; then
glibcxx_version=$version
fi
fi
done < <("$PWD/.build/sysroots/$TRIPLE/$TRIPLE/bin/objdump" -T "$file")
if [[ "$glibc_version" != "$EXPECTED_GLIBC_VERSION" ]]; then
echo "Error: File $file has dependency on GLIBC > $EXPECTED_GLIBC_VERSION"
exit 1
fi
if [[ "$glibcxx_version" != "$EXPECTED_GLIBCXX_VERSION" ]]; then
echo "Error: File $file has dependency on GLIBCXX > $EXPECTED_GLIBCXX_VERSION"
exit 1
fi
done

View File

@@ -125,7 +125,7 @@ function getNodeVersion() {
return { nodeVersion, internalNodeVersion };
}
function getNodeChecksum(nodeVersion, platform, arch) {
function getNodeChecksum(nodeVersion, platform, arch, glibcPrefix) {
let expectedName;
switch (platform) {
case 'win32':
@@ -135,7 +135,7 @@ function getNodeChecksum(nodeVersion, platform, arch) {
case 'darwin':
case 'alpine':
case 'linux':
expectedName = `node-v${nodeVersion}-${platform}-${arch}.tar.gz`;
expectedName = `node-v${nodeVersion}${glibcPrefix}-${platform}-${arch}.tar.gz`;
break;
}
@@ -193,7 +193,8 @@ function nodejs(platform, arch) {
log(`Downloading node.js ${nodeVersion} ${platform} ${arch} from ${product.nodejsRepository}...`);
const checksumSha256 = getNodeChecksum(nodeVersion, platform, arch);
const glibcPrefix = process.env['VSCODE_NODE_GLIBC'] ?? '';
const checksumSha256 = getNodeChecksum(nodeVersion, platform, arch, glibcPrefix);
if (checksumSha256) {
log(`Using SHA256 checksum for checking integrity: ${checksumSha256}`);
@@ -210,7 +211,7 @@ function nodejs(platform, arch) {
case 'darwin':
case 'linux':
return (product.nodejsRepository !== 'https://nodejs.org' ?
fetchGithub(product.nodejsRepository, { version: `${nodeVersion}-${internalNodeVersion}`, name: `node-v${nodeVersion}-${platform}-${arch}.tar.gz`, checksumSha256 }) :
fetchGithub(product.nodejsRepository, { version: `${nodeVersion}-${internalNodeVersion}`, name: `node-v${nodeVersion}${glibcPrefix}-${platform}-${arch}.tar.gz`, checksumSha256 }) :
fetchUrls(`/dist/v${nodeVersion}/node-v${nodeVersion}-${platform}-${arch}.tar.gz`, { base: 'https://nodejs.org', checksumSha256 })
).pipe(flatmap(stream => stream.pipe(gunzip()).pipe(untar())))
.pipe(filter('**/node'))

View File

@@ -53,10 +53,14 @@ function yarnInstall(dir, opts) {
console.log(`Installing dependencies in ${dir} inside container ${process.env['VSCODE_REMOTE_DEPENDENCIES_CONTAINER_NAME']}...`);
opts.cwd = root;
if (process.env['npm_config_arch'] === 'arm64') {
if (process.env['npm_config_arch'] === 'arm64' || process.env['npm_config_arch'] === 'arm') {
run('sudo', ['docker', 'run', '--rm', '--privileged', 'multiarch/qemu-user-static', '--reset', '-p', 'yes'], opts);
}
run('sudo', ['docker', 'run', '-e', 'GITHUB_TOKEN', '-e', 'npm_config_arch', '-v', `${process.env['VSCODE_HOST_MOUNT']}:/root/vscode`, '-v', `${process.env['VSCODE_HOST_MOUNT']}/.build/.netrc:/root/.netrc`, process.env['VSCODE_REMOTE_DEPENDENCIES_CONTAINER_NAME'], 'yarn', '--cwd', dir, ...args], opts);
if (process.env['npm_config_arch'] === 'arm') {
run('sudo', ['docker', 'run', '-e', 'GITHUB_TOKEN', '-e', 'npm_config_arch', '-v', `${process.env['VSCODE_HOST_MOUNT']}:/home/builduser`, '-v', `${process.env['VSCODE_HOST_MOUNT']}/.build/.netrc:/home/builduser/.netrc`, process.env['VSCODE_REMOTE_DEPENDENCIES_CONTAINER_NAME'], 'yarn', '--cwd', dir, ...args], opts);
} else {
run('sudo', ['docker', 'run', '-e', 'GITHUB_TOKEN', '-e', 'npm_config_arch', '-v', `${process.env['VSCODE_HOST_MOUNT']}:/root/vscode`, '-v', `${process.env['VSCODE_HOST_MOUNT']}/.build/.netrc:/root/.netrc`, process.env['VSCODE_REMOTE_DEPENDENCIES_CONTAINER_NAME'], 'yarn', '--cwd', dir, ...args], opts);
}
run('sudo', ['chown', '-R', `${userinfo.uid}:${userinfo.gid}`, `${dir}/node_modules`], opts);
} else {
console.log(`Installing dependencies in ${dir}...`);
@@ -102,8 +106,19 @@ for (let dir of dirs) {
if (/^(.build\/distro\/npm\/)?remote$/.test(dir)) {
// node modules used by vscode server
const env = { ...process.env };
if (process.env['VSCODE_REMOTE_CC']) { env['CC'] = process.env['VSCODE_REMOTE_CC']; }
if (process.env['VSCODE_REMOTE_CXX']) { env['CXX'] = process.env['VSCODE_REMOTE_CXX']; }
if (process.env['VSCODE_REMOTE_CC']) {
env['CC'] = process.env['VSCODE_REMOTE_CC'];
} else {
delete env['CC'];
}
if (process.env['VSCODE_REMOTE_CXX']) {
env['CXX'] = process.env['VSCODE_REMOTE_CXX'];
} else {
delete env['CXX'];
}
if (process.env['CXXFLAGS']) { delete env['CXXFLAGS']; }
if (process.env['CFLAGS']) { delete env['CFLAGS']; }
if (process.env['LDFLAGS']) { delete env['LDFLAGS']; }
if (process.env['VSCODE_REMOTE_CXXFLAGS']) { env['CXXFLAGS'] = process.env['VSCODE_REMOTE_CXXFLAGS']; }
if (process.env['VSCODE_REMOTE_LDFLAGS']) { env['LDFLAGS'] = process.env['VSCODE_REMOTE_LDFLAGS']; }
if (process.env['VSCODE_REMOTE_NODE_GYP']) { env['npm_config_node_gyp'] = process.env['VSCODE_REMOTE_NODE_GYP']; }

View File

@@ -20,8 +20,8 @@ lazy_static! {
static ref GENERIC_VERSION_RE: Regex = Regex::new(r"^([0-9]+)\.([0-9]+)$").unwrap();
static ref LIBSTD_CXX_VERSION_RE: BinRegex =
BinRegex::new(r"GLIBCXX_([0-9]+)\.([0-9]+)(?:\.([0-9]+))?").unwrap();
static ref MIN_CXX_VERSION: SimpleSemver = SimpleSemver::new(3, 4, 25);
static ref MIN_LDD_VERSION: SimpleSemver = SimpleSemver::new(2, 28, 0);
static ref MIN_CXX_VERSION: SimpleSemver = SimpleSemver::new(3, 4, 19);
static ref MIN_LDD_VERSION: SimpleSemver = SimpleSemver::new(2, 17, 0);
}
const NIXOS_TEST_PATH: &str = "/etc/NIXOS";

View File

@@ -1,7 +1,7 @@
{
"name": "code-oss-dev",
"version": "1.87.0",
"distro": "ddced7c51be56108bf9f4bc3e3481a8673aa5031",
"distro": "205c07946b7d7629f85cf332acfcfc8a76f23f6d",
"author": {
"name": "Microsoft Corporation"
},

View File

@@ -1,5 +1,5 @@
disturl "https://nodejs.org/dist"
target "18.17.1"
ms_build_id "252256"
ms_build_id "255375"
runtime "node"
build_from_source "true"

View File

@@ -126,5 +126,5 @@ fi
if [ "$found_required_glibc" = "0" ] || [ "$found_required_glibcxx" = "0" ]; then
echo "Error: Missing required dependencies. Please refer to our FAQ https://aka.ms/vscode-remote/faq/old-linux for additional information."
# Custom exit code based on https://tldp.org/LDP/abs/html/exitcodes.html
exit 99
#exit 99
fi

View File

@@ -27,6 +27,7 @@ export interface IRemoteAgentEnvironment {
all: IUserDataProfile[];
home: URI;
};
isUnsupportedGlibc: boolean;
}
export interface RemoteAgentConnectionContext {

View File

@@ -96,6 +96,15 @@ export class RemoteAgentEnvironmentChannel implements IServerChannel {
if (profile && !this._userDataProfilesService.profiles.some(p => p.id === profile)) {
await this._userDataProfilesService.createProfile(profile, profile);
}
type ProcessWithGlibc = NodeJS.Process & {
glibcVersion?: string;
};
let isUnsupportedGlibc = false;
if (process.platform === 'linux') {
const glibcVersion = (process as ProcessWithGlibc).glibcVersion;
const minorVersion = glibcVersion ? parseInt(glibcVersion.split('.')[1]) : 28;
isUnsupportedGlibc = (minorVersion <= 27);
}
return {
pid: process.pid,
connectionToken: (this._connectionToken.type !== ServerConnectionTokenType.None ? this._connectionToken.value : ''),
@@ -114,7 +123,8 @@ export class RemoteAgentEnvironmentChannel implements IServerChannel {
profiles: {
home: this._userDataProfilesService.profilesHome,
all: [...this._userDataProfilesService.profiles].map(profile => ({ ...profile }))
}
},
isUnsupportedGlibc
};
}

View File

@@ -222,11 +222,13 @@ export class BannerPart extends Part implements IBannerService {
}
// Action
const actionBarContainer = append(this.element, $('div.action-container'));
this.actionBar = this._register(new ActionBar(actionBarContainer));
const closeAction = this._register(new Action('banner.close', 'Close Banner', ThemeIcon.asClassName(widgetClose), true, () => this.close(item)));
this.actionBar.push(closeAction, { icon: true, label: false });
this.actionBar.setFocusable(false);
if (!item.disableCloseAction) {
const actionBarContainer = append(this.element, $('div.action-container'));
this.actionBar = this._register(new ActionBar(actionBarContainer));
const closeAction = this._register(new Action('banner.close', 'Close Banner', ThemeIcon.asClassName(widgetClose), true, () => this.close(item)));
this.actionBar.push(closeAction, { icon: true, label: false });
this.actionBar.setFocusable(false);
}
this.setVisibility(true);
this.item = item;

View File

@@ -11,6 +11,7 @@ import { TunnelFactoryContribution } from 'vs/workbench/contrib/remote/browser/t
import { RemoteAgentConnectionStatusListener, RemoteMarkers } from 'vs/workbench/contrib/remote/browser/remote';
import { RemoteStatusIndicator } from 'vs/workbench/contrib/remote/browser/remoteIndicator';
import { AutomaticPortForwarding, ForwardedPortsView, PortRestore } from 'vs/workbench/contrib/remote/browser/remoteExplorer';
import { InitialRemoteConnectionHealthContribution } from 'vs/workbench/contrib/remote/browser/remoteConnectionHealth';
const workbenchContributionsRegistry = Registry.as<IWorkbenchContributionsRegistry>(WorkbenchExtensions.Workbench);
registerWorkbenchContribution2(ShowCandidateContribution.ID, ShowCandidateContribution, WorkbenchPhase.BlockRestore);
@@ -21,3 +22,4 @@ workbenchContributionsRegistry.registerWorkbenchContribution(ForwardedPortsView,
workbenchContributionsRegistry.registerWorkbenchContribution(PortRestore, LifecyclePhase.Eventually);
workbenchContributionsRegistry.registerWorkbenchContribution(AutomaticPortForwarding, LifecyclePhase.Eventually);
workbenchContributionsRegistry.registerWorkbenchContribution(RemoteMarkers, LifecyclePhase.Eventually);
workbenchContributionsRegistry.registerWorkbenchContribution(InitialRemoteConnectionHealthContribution, LifecyclePhase.Restored);

View File

@@ -0,0 +1,183 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { IWorkbenchContribution } from 'vs/workbench/common/contributions';
import { IRemoteAgentService, remoteConnectionLatencyMeasurer } from 'vs/workbench/services/remote/common/remoteAgentService';
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
import { localize } from 'vs/nls';
import { isWeb } from 'vs/base/common/platform';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { getRemoteName } from 'vs/platform/remote/common/remoteHosts';
import { IBannerService } from 'vs/workbench/services/banner/browser/bannerService';
import { IOpenerService } from 'vs/platform/opener/common/opener';
import { IHostService } from 'vs/workbench/services/host/browser/host';
import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage';
import { IProductService } from 'vs/platform/product/common/productService';
import { IDialogService } from 'vs/platform/dialogs/common/dialogs';
import { Codicon } from 'vs/base/common/codicons';
import Severity from 'vs/base/common/severity';
const REMOTE_UNSUPPORTED_CONNECTION_CHOICE_KEY = 'remote.unsupportedConnectionChoice';
export class InitialRemoteConnectionHealthContribution implements IWorkbenchContribution {
constructor(
@IRemoteAgentService private readonly _remoteAgentService: IRemoteAgentService,
@IWorkbenchEnvironmentService private readonly _environmentService: IWorkbenchEnvironmentService,
@ITelemetryService private readonly _telemetryService: ITelemetryService,
@IBannerService private readonly bannerService: IBannerService,
@IDialogService private readonly dialogService: IDialogService,
@IOpenerService private readonly openerService: IOpenerService,
@IHostService private readonly hostService: IHostService,
@IStorageService private readonly storageService: IStorageService,
@IProductService private readonly productService: IProductService,
) {
if (this._environmentService.remoteAuthority) {
this._checkInitialRemoteConnectionHealth();
}
}
private async _confirmConnection(): Promise<boolean> {
const enum ConnectionChoice {
Allow = 1,
LearnMore = 2,
Cancel = 0
}
const { result, checkboxChecked } = await this.dialogService.prompt<ConnectionChoice>({
type: Severity.Warning,
message: localize('unsupportedGlibcWarning', "You are about to connect to an OS that is unsupported by {0}", this.productService.nameLong),
buttons: [
{
label: localize({ key: 'allow', comment: ['&& denotes a mnemonic'] }, "&&Allow"),
run: () => ConnectionChoice.Allow
},
{
label: localize({ key: 'learnMore', comment: ['&& denotes a mnemonic'] }, "&&Learn More"),
run: async () => { await this.openerService.open('https://aka.ms/vscode-remote/faq/old-linux'); return ConnectionChoice.LearnMore; }
}
],
cancelButton: {
run: () => ConnectionChoice.Cancel
},
checkbox: {
label: localize('remember', "Do not ask me again"),
}
});
if (result === ConnectionChoice.LearnMore) {
return await this._confirmConnection();
}
const allowed = result === ConnectionChoice.Allow;
if (checkboxChecked) {
this.storageService.store(REMOTE_UNSUPPORTED_CONNECTION_CHOICE_KEY, allowed, StorageScope.PROFILE, StorageTarget.MACHINE);
}
return allowed;
}
private async _checkInitialRemoteConnectionHealth(): Promise<void> {
try {
const environment = await this._remoteAgentService.getRawEnvironment();
if (environment && environment.isUnsupportedGlibc) {
let allowed = this.storageService.getBoolean(REMOTE_UNSUPPORTED_CONNECTION_CHOICE_KEY, StorageScope.PROFILE);
if (allowed === undefined) {
allowed = await this._confirmConnection();
}
if (allowed) {
const actions = [
{
label: localize('unsupportedGlibcBannerLearnMore', "Learn More"),
href: 'https://aka.ms/vscode-remote/faq/old-linux'
}
];
this.bannerService.show({
id: 'unsupportedGlibcWarning.banner',
message: localize('unsupportedGlibcWarning.banner', "You are connected to an OS that is unsupported by {0}.", this.productService.nameLong),
actions,
icon: Codicon.warning,
disableCloseAction: true
});
} else {
this.hostService.openWindow({ forceReuseWindow: true, remoteAuthority: null });
return;
}
}
type RemoteConnectionSuccessClassification = {
owner: 'alexdima';
comment: 'The initial connection succeeded';
web: { classification: 'SystemMetaData'; purpose: 'PerformanceAndHealth'; comment: 'Is web ui.' };
connectionTimeMs: { classification: 'SystemMetaData'; purpose: 'PerformanceAndHealth'; comment: 'Time, in ms, until connected'; isMeasurement: true };
remoteName: { classification: 'SystemMetaData'; purpose: 'PerformanceAndHealth'; comment: 'The name of the resolver.' };
};
type RemoteConnectionSuccessEvent = {
web: boolean;
connectionTimeMs: number | undefined;
remoteName: string | undefined;
};
this._telemetryService.publicLog2<RemoteConnectionSuccessEvent, RemoteConnectionSuccessClassification>('remoteConnectionSuccess', {
web: isWeb,
connectionTimeMs: await this._remoteAgentService.getConnection()?.getInitialConnectionTimeMs(),
remoteName: getRemoteName(this._environmentService.remoteAuthority)
});
await this._measureExtHostLatency();
} catch (err) {
type RemoteConnectionFailureClassification = {
owner: 'alexdima';
comment: 'The initial connection failed';
web: { classification: 'SystemMetaData'; purpose: 'PerformanceAndHealth'; comment: 'Is web ui.' };
remoteName: { classification: 'SystemMetaData'; purpose: 'PerformanceAndHealth'; comment: 'The name of the resolver.' };
connectionTimeMs: { classification: 'SystemMetaData'; purpose: 'PerformanceAndHealth'; comment: 'Time, in ms, until connection failure'; isMeasurement: true };
message: { classification: 'SystemMetaData'; purpose: 'PerformanceAndHealth'; comment: 'Error message' };
};
type RemoteConnectionFailureEvent = {
web: boolean;
remoteName: string | undefined;
connectionTimeMs: number | undefined;
message: string;
};
this._telemetryService.publicLog2<RemoteConnectionFailureEvent, RemoteConnectionFailureClassification>('remoteConnectionFailure', {
web: isWeb,
connectionTimeMs: await this._remoteAgentService.getConnection()?.getInitialConnectionTimeMs(),
remoteName: getRemoteName(this._environmentService.remoteAuthority),
message: err ? err.message : ''
});
}
}
private async _measureExtHostLatency() {
const measurement = await remoteConnectionLatencyMeasurer.measure(this._remoteAgentService);
if (measurement === undefined) {
return;
}
type RemoteConnectionLatencyClassification = {
owner: 'connor4312';
comment: 'The latency to the remote extension host';
web: { classification: 'SystemMetaData'; purpose: 'PerformanceAndHealth'; comment: 'Whether this is running on web' };
remoteName: { classification: 'SystemMetaData'; purpose: 'PerformanceAndHealth'; comment: 'Anonymized remote name' };
latencyMs: { classification: 'SystemMetaData'; purpose: 'PerformanceAndHealth'; comment: 'Latency to the remote, in milliseconds'; isMeasurement: true };
};
type RemoteConnectionLatencyEvent = {
web: boolean;
remoteName: string | undefined;
latencyMs: number;
};
this._telemetryService.publicLog2<RemoteConnectionLatencyEvent, RemoteConnectionLatencyClassification>('remoteConnectionLatency', {
web: isWeb,
remoteName: getRemoteName(this._environmentService.remoteAuthority),
latencyMs: measurement.current
});
}
}

View File

@@ -9,7 +9,7 @@ import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle
import { ILabelService, ResourceLabelFormatting } from 'vs/platform/label/common/label';
import { OperatingSystem, isWeb, OS } from 'vs/base/common/platform';
import { Schemas } from 'vs/base/common/network';
import { IRemoteAgentService, remoteConnectionLatencyMeasurer } from 'vs/workbench/services/remote/common/remoteAgentService';
import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService';
import { ILoggerService } from 'vs/platform/log/common/log';
import { localize, localize2 } from 'vs/nls';
import { Disposable } from 'vs/base/common/lifecycle';
@@ -24,8 +24,6 @@ import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation
import { Action2, registerAction2 } from 'vs/platform/actions/common/actions';
import { Categories } from 'vs/platform/action/common/actionCommonCategories';
import { PersistentConnection } from 'vs/platform/remote/common/remoteAgentConnection';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { getRemoteName } from 'vs/platform/remote/common/remoteHosts';
import { IDownloadService } from 'vs/platform/download/common/download';
import { DownloadServiceChannel } from 'vs/platform/download/common/downloadIpc';
import { RemoteLoggerChannelClient } from 'vs/platform/log/common/logIpc';
@@ -144,100 +142,10 @@ class RemoteInvalidWorkspaceDetector extends Disposable implements IWorkbenchCon
}
}
class InitialRemoteConnectionHealthContribution implements IWorkbenchContribution {
constructor(
@IRemoteAgentService private readonly _remoteAgentService: IRemoteAgentService,
@IWorkbenchEnvironmentService private readonly _environmentService: IWorkbenchEnvironmentService,
@ITelemetryService private readonly _telemetryService: ITelemetryService,
) {
if (this._environmentService.remoteAuthority) {
this._checkInitialRemoteConnectionHealth();
}
}
private async _checkInitialRemoteConnectionHealth(): Promise<void> {
try {
await this._remoteAgentService.getRawEnvironment();
type RemoteConnectionSuccessClassification = {
owner: 'alexdima';
comment: 'The initial connection succeeded';
web: { classification: 'SystemMetaData'; purpose: 'PerformanceAndHealth'; comment: 'Is web ui.' };
connectionTimeMs: { classification: 'SystemMetaData'; purpose: 'PerformanceAndHealth'; comment: 'Time, in ms, until connected'; isMeasurement: true };
remoteName: { classification: 'SystemMetaData'; purpose: 'PerformanceAndHealth'; comment: 'The name of the resolver.' };
};
type RemoteConnectionSuccessEvent = {
web: boolean;
connectionTimeMs: number | undefined;
remoteName: string | undefined;
};
this._telemetryService.publicLog2<RemoteConnectionSuccessEvent, RemoteConnectionSuccessClassification>('remoteConnectionSuccess', {
web: isWeb,
connectionTimeMs: await this._remoteAgentService.getConnection()?.getInitialConnectionTimeMs(),
remoteName: getRemoteName(this._environmentService.remoteAuthority)
});
await this._measureExtHostLatency();
} catch (err) {
type RemoteConnectionFailureClassification = {
owner: 'alexdima';
comment: 'The initial connection failed';
web: { classification: 'SystemMetaData'; purpose: 'PerformanceAndHealth'; comment: 'Is web ui.' };
remoteName: { classification: 'SystemMetaData'; purpose: 'PerformanceAndHealth'; comment: 'The name of the resolver.' };
connectionTimeMs: { classification: 'SystemMetaData'; purpose: 'PerformanceAndHealth'; comment: 'Time, in ms, until connection failure'; isMeasurement: true };
message: { classification: 'SystemMetaData'; purpose: 'PerformanceAndHealth'; comment: 'Error message' };
};
type RemoteConnectionFailureEvent = {
web: boolean;
remoteName: string | undefined;
connectionTimeMs: number | undefined;
message: string;
};
this._telemetryService.publicLog2<RemoteConnectionFailureEvent, RemoteConnectionFailureClassification>('remoteConnectionFailure', {
web: isWeb,
connectionTimeMs: await this._remoteAgentService.getConnection()?.getInitialConnectionTimeMs(),
remoteName: getRemoteName(this._environmentService.remoteAuthority),
message: err ? err.message : ''
});
}
}
private async _measureExtHostLatency() {
const measurement = await remoteConnectionLatencyMeasurer.measure(this._remoteAgentService);
if (measurement === undefined) {
return;
}
type RemoteConnectionLatencyClassification = {
owner: 'connor4312';
comment: 'The latency to the remote extension host';
web: { classification: 'SystemMetaData'; purpose: 'PerformanceAndHealth'; comment: 'Whether this is running on web' };
remoteName: { classification: 'SystemMetaData'; purpose: 'PerformanceAndHealth'; comment: 'Anonymized remote name' };
latencyMs: { classification: 'SystemMetaData'; purpose: 'PerformanceAndHealth'; comment: 'Latency to the remote, in milliseconds'; isMeasurement: true };
};
type RemoteConnectionLatencyEvent = {
web: boolean;
remoteName: string | undefined;
latencyMs: number;
};
this._telemetryService.publicLog2<RemoteConnectionLatencyEvent, RemoteConnectionLatencyClassification>('remoteConnectionLatency', {
web: isWeb,
remoteName: getRemoteName(this._environmentService.remoteAuthority),
latencyMs: measurement.current
});
}
}
const workbenchContributionsRegistry = Registry.as<IWorkbenchContributionsRegistry>(WorkbenchExtensions.Workbench);
registerWorkbenchContribution2(LabelContribution.ID, LabelContribution, WorkbenchPhase.BlockStartup);
workbenchContributionsRegistry.registerWorkbenchContribution(RemoteChannelsContribution, LifecyclePhase.Restored);
registerWorkbenchContribution2(RemoteInvalidWorkspaceDetector.ID, RemoteInvalidWorkspaceDetector, WorkbenchPhase.BlockStartup);
workbenchContributionsRegistry.registerWorkbenchContribution(InitialRemoteConnectionHealthContribution, LifecyclePhase.Restored);
const enableDiagnostics = true;

View File

@@ -16,6 +16,7 @@ export interface IBannerItem {
readonly actions?: ILinkDescriptor[];
readonly ariaLabel?: string;
readonly onClose?: () => void;
readonly disableCloseAction?: boolean;
}
export const IBannerService = createDecorator<IBannerService>('bannerService');

View File

@@ -43,6 +43,7 @@ export interface IRemoteAgentEnvironmentDTO {
all: UriDto<IUserDataProfile[]>;
home: UriComponents;
};
isUnsupportedGlibc: boolean;
}
export class RemoteExtensionEnvironmentChannelClient {
@@ -70,7 +71,8 @@ export class RemoteExtensionEnvironmentChannelClient {
arch: data.arch,
marks: data.marks,
useHostProxy: data.useHostProxy,
profiles: revive(data.profiles)
profiles: revive(data.profiles),
isUnsupportedGlibc: data.isUnsupportedGlibc
};
}