From 1f3fc708e725cdce47ed39f6913cc8ff273a8e64 Mon Sep 17 00:00:00 2001 From: Mark Yen Date: Tue, 28 Nov 2023 15:31:49 -0800 Subject: [PATCH 1/2] WSL: Use resources helper to get wsl-helper path This makes us (mostly) use the existing helper to get the executable paths, so that it would be easier to change the name. Signed-off-by: Mark Yen --- pkg/rancher-desktop/backend/wsl.ts | 3 +- .../integrations/windowsIntegrationManager.ts | 32 +++++++++---------- pkg/rancher-desktop/utils/resources.ts | 30 ++++++++++++++--- pkg/rancher-desktop/utils/wslVersion.ts | 8 ++--- 4 files changed, 47 insertions(+), 26 deletions(-) diff --git a/pkg/rancher-desktop/backend/wsl.ts b/pkg/rancher-desktop/backend/wsl.ts index 55af6aa1fcc..a63759ab21d 100644 --- a/pkg/rancher-desktop/backend/wsl.ts +++ b/pkg/rancher-desktop/backend/wsl.ts @@ -54,6 +54,7 @@ import clone from '@pkg/utils/clone'; import Logging from '@pkg/utils/logging'; import { wslHostIPv4Address } from '@pkg/utils/networks'; import paths from '@pkg/utils/paths'; +import { executable } from '@pkg/utils/resources'; import { jsonStringifyWithWhiteSpace } from '@pkg/utils/stringify'; import { defined, RecursivePartial } from '@pkg/utils/typeUtils'; @@ -1745,7 +1746,7 @@ export default class WSLBackend extends events.EventEmitter implements VMBackend // We need to get the Linux path to our helper executable; it is easier to // just get WSL to do the transformation for us. - return this.wslify(path.join(paths.resources, 'linux', 'wsl-helper'), distro); + return this.wslify(executable('wsl-helper-linux'), distro); } /** diff --git a/pkg/rancher-desktop/integrations/windowsIntegrationManager.ts b/pkg/rancher-desktop/integrations/windowsIntegrationManager.ts index ca1df821591..5354dcac412 100644 --- a/pkg/rancher-desktop/integrations/windowsIntegrationManager.ts +++ b/pkg/rancher-desktop/integrations/windowsIntegrationManager.ts @@ -92,7 +92,7 @@ export default class WindowsIntegrationManager implements IntegrationManager { console.debug('Spawning Windows docker proxy'); return spawn( - path.join(paths.resources, 'win32', 'wsl-helper.exe'), + executable('wsl-helper'), ['docker-proxy', 'serve', ...this.wslHelperDebugArgs], { stdio: ['ignore', stream, stream], windowsHide: true, @@ -238,15 +238,14 @@ export default class WindowsIntegrationManager implements IntegrationManager { /** * Return the Linux path to the WSL helper executable. */ - protected async getLinuxToolPath(distro: string, ...tool: string[]): Promise { + protected async getLinuxToolPath(distro: string, tool: string): Promise { // We need to get the Linux path to our helper executable; it is easier to // just get WSL to do the transformation for us. const logStream = Logging[`wsl-helper.${ distro }`]; const { stdout } = await spawnFile( await this.wslExe, - ['--distribution', distro, '--exec', '/bin/wslpath', '-a', '-u', - path.join(paths.resources, 'linux', ...tool)], + ['--distribution', distro, '--exec', '/bin/wslpath', '-a', '-u', tool], { stdio: ['ignore', 'pipe', logStream] }, ); @@ -291,7 +290,7 @@ export default class WindowsIntegrationManager implements IntegrationManager { console.debug(`Syncing ${ distro } socket proxy: ${ shouldRun ? 'should' : 'should not' } run.`); if (shouldRun) { - const executable = await this.getLinuxToolPath(distro, 'wsl-helper'); + const linuxExecutable = await this.getLinuxToolPath(distro, executable('wsl-helper-linux')); const logStream = Logging[`wsl-helper.${ distro }`]; this.distroSocketProxyProcesses[distro] ??= new BackgroundProcess( @@ -299,7 +298,7 @@ export default class WindowsIntegrationManager implements IntegrationManager { { spawn: async() => { return spawn(await this.wslExe, - ['--distribution', distro, '--user', 'root', '--exec', executable, + ['--distribution', distro, '--user', 'root', '--exec', linuxExecutable, 'docker-proxy', 'serve', ...this.wslHelperDebugArgs], { stdio: ['ignore', await logStream.fdStream, await logStream.fdStream], @@ -312,7 +311,7 @@ export default class WindowsIntegrationManager implements IntegrationManager { // Ensure we kill the WSL-side process; sometimes things can get out // of sync. await this.execCommand({ distro, root: true }, - executable, 'docker-proxy', 'kill', ...this.wslHelperDebugArgs); + linuxExecutable, 'docker-proxy', 'kill', ...this.wslHelperDebugArgs); }, }); this.distroSocketProxyProcesses[distro].start(); @@ -358,7 +357,7 @@ export default class WindowsIntegrationManager implements IntegrationManager { throw new Error("Can't find home directory"); } const cliDir = path.join(homeDir, '.docker', 'cli-plugins'); - const srcPath = executable(pluginName); + const srcPath = executable(pluginName as any); // It's an executable in `bin` const cliPath = path.join(cliDir, path.basename(srcPath)); console.debug(`Syncing host ${ pluginName }: ${ srcPath } -> ${ cliPath }`); @@ -389,7 +388,8 @@ export default class WindowsIntegrationManager implements IntegrationManager { */ protected async syncDistroDockerPlugin(distro: string, pluginName: string, state: boolean) { try { - const srcPath = await this.getLinuxToolPath(distro, 'bin', pluginName); + const srcPath = await this.getLinuxToolPath(distro, + path.join(paths.resources, 'linux', 'bin', pluginName)); const destDir = '$HOME/.docker/cli-plugins'; const destPath = `${ destDir }/${ pluginName }`; @@ -433,14 +433,14 @@ export default class WindowsIntegrationManager implements IntegrationManager { if (this.settings.experimental?.virtualMachine?.networkingTunnel) { await this.execCommand( { distro, root: true }, - await this.getLinuxToolPath(distro, 'wsl-helper'), + await this.getLinuxToolPath(distro, executable('wsl-helper-linux')), 'update-host', `--entries=${ entry }`, ); } else { await this.execCommand( { distro, root: true }, - await this.getLinuxToolPath(distro, 'wsl-helper'), + await this.getLinuxToolPath(distro, executable('wsl-helper-linux')), 'update-host', `--remove`, ); @@ -464,7 +464,7 @@ export default class WindowsIntegrationManager implements IntegrationManager { WSLENV: `${ process.env.WSLENV }:KUBECONFIG/up`, }, }, - await this.getLinuxToolPath(distro, 'wsl-helper'), + await this.getLinuxToolPath(distro, executable('wsl-helper-linux')), 'kubeconfig', `--enable=${ state && this.settings.kubernetes?.enabled }`, `--rd-networking=${ rdNetworking }`, @@ -518,10 +518,10 @@ export default class WindowsIntegrationManager implements IntegrationManager { protected async markIntegration(distro: string, state: boolean): Promise { try { - const executable = await this.getLinuxToolPath(distro, 'wsl-helper'); + const exe = await this.getLinuxToolPath(distro, executable('wsl-helper-linux')); const mode = state ? 'set' : 'delete'; - await this.execCommand({ distro, root: true }, executable, 'wsl', 'integration-state', `--mode=${ mode }`); + await this.execCommand({ distro, root: true }, exe, 'wsl', 'integration-state', `--mode=${ mode }`); } catch (ex) { console.error(`Failed to mark integration for ${ distro }:`, ex); } @@ -548,10 +548,10 @@ export default class WindowsIntegrationManager implements IntegrationManager { return `Rancher Desktop can only integrate with v2 WSL distributions (this is v${ distro.version }).`; } try { - const executable = await this.getLinuxToolPath(distro.name, 'wsl-helper'); + const exe = await this.getLinuxToolPath(distro.name, executable('wsl-helper-linux')); const stdout = await this.captureCommand( { distro: distro.name }, - executable, 'wsl', 'integration-state', '--mode=show'); + exe, 'wsl', 'integration-state', '--mode=show'); console.debug(`WSL distro "${ distro.name }": wsl-helper output: "${ stdout.trim() }"`); if (['true', 'false'].includes(stdout.trim())) { diff --git a/pkg/rancher-desktop/utils/resources.ts b/pkg/rancher-desktop/utils/resources.ts index 832091f93a5..c21a205a8be 100644 --- a/pkg/rancher-desktop/utils/resources.ts +++ b/pkg/rancher-desktop/utils/resources.ts @@ -1,19 +1,41 @@ -import os from 'os'; import path from 'path'; import memoize from 'lodash/memoize'; import paths from '@pkg/utils/paths'; +/** + * executableMap is a mapping of valid executable names and their path. + * If the value is `undefined`, then it's assumed to be an executable in the + * user-accessible `bin` directory. + * Otherwise, it's an array containing the path to the executable. + */ +const executableMap: Record = { + docker: undefined, + kubectl: undefined, + nerdctl: undefined, + rdctl: undefined, + 'wsl-helper': [paths.resources, process.platform, platformBinary('wsl-helper')], + 'wsl-helper-linux': [paths.resources, 'linux', 'wsl-helper'], +}; + +function platformBinary(name: string): string { + return process.platform === 'win32' ? `${ name }.exe` : name; +} + /** * Gets the absolute path to an executable. Adds ".exe" to the end * if running on Windows. * @param name The name of the binary, without file extension. */ -function _executable(name: string) { - const osSpecificName = os.platform().startsWith('win') ? `${ name }.exe` : name; +function _executable(name: keyof typeof executableMap): string { + const parts = executableMap[name]; + + if (parts === undefined) { + return path.join(paths.resources, process.platform, 'bin', platformBinary(name)); + } - return path.join(paths.resources, os.platform(), 'bin', osSpecificName); + return path.join(...parts); } export const executable = memoize(_executable); diff --git a/pkg/rancher-desktop/utils/wslVersion.ts b/pkg/rancher-desktop/utils/wslVersion.ts index ebe0de61dc5..8898f129616 100644 --- a/pkg/rancher-desktop/utils/wslVersion.ts +++ b/pkg/rancher-desktop/utils/wslVersion.ts @@ -3,11 +3,9 @@ * version. */ -import path from 'path'; - import { spawnFile } from '@pkg/utils/childProcess'; import logging from '@pkg/utils/logging'; -import paths from '@pkg/utils/paths'; +import { executable } from '@pkg/utils/resources'; type WSLVersionInfo = { installed: boolean; @@ -28,8 +26,8 @@ const console = logging['wsl-version']; * Get information about the currently installed WSL version. */ export default async function getWSLVersion(): Promise { - const wslHelper = path.join(paths.resources, 'win32', 'wsl-helper.exe'); - const { stdout } = await spawnFile(wslHelper, ['wsl', 'info'], { stdio: ['ignore', 'pipe', console] }); + const { stdout } = await spawnFile(executable('wsl-helper'), + ['wsl', 'info'], { stdio: ['ignore', 'pipe', console] }); return JSON.parse(stdout); } From 4168dfa3b54016cb81e0218d9f283a5d2f9825db Mon Sep 17 00:00:00 2001 From: Mark Yen Date: Tue, 28 Nov 2023 16:37:52 -0800 Subject: [PATCH 2/2] WSL-Helper: Use a temporary name We have issues with wsl-helper (the Linux executable) not being stopped correctly on uninstall; this means that when the new executable runs on an upgrade/downgrade, we get confusing errors (segfaults and sigill). Avoid this temporarily by renaming the executable until we have a more permanent fix. Signed-off-by: Mark Yen --- e2e/wsl-integrations.e2e.spec.ts | 18 +++++++++--------- pkg/rancher-desktop/utils/resources.ts | 4 ++-- scripts/lib/build-utils.ts | 3 ++- scripts/lib/sign-win32.ts | 2 +- 4 files changed, 14 insertions(+), 13 deletions(-) diff --git a/e2e/wsl-integrations.e2e.spec.ts b/e2e/wsl-integrations.e2e.spec.ts index 566a60746ef..dbc29eee1e8 100644 --- a/e2e/wsl-integrations.e2e.spec.ts +++ b/e2e/wsl-integrations.e2e.spec.ts @@ -76,21 +76,21 @@ test.describe('WSL Integrations', () => { utf16le: true, }, ...['alpha', 'beta', 'gamma'].flatMap(distro => [ - ...[['bin', 'docker-compose'], ['wsl-helper']].flatMap(tool => ([ + ...[['bin', 'docker-compose'], ['wsl-helper-1.11.1']].flatMap(tool => ([ { args: ['--distribution', distro, '--exec', '/bin/wslpath', '-a', '-u', path.join(process.cwd(), 'resources', 'linux', ...tool)], mode: 'repeated', stdout: `/${ distro }/${ tool.join('/') }`, }])), - ...[['bin', 'docker-buildx'], ['wsl-helper']].flatMap(tool => ([ + ...[['bin', 'docker-buildx'], ['wsl-helper-1.11.1']].flatMap(tool => ([ { args: ['--distribution', distro, '--exec', '/bin/wslpath', '-a', '-u', path.join(process.cwd(), 'resources', 'linux', ...tool)], mode: 'repeated', stdout: `/${ distro }/${ tool.join('/') }`, }])), ...[ - [`/${ distro }/wsl-helper`, 'kubeconfig', '--enable=false'], - [`/${ distro }/wsl-helper`, 'kubeconfig', '--enable=true'], + [`/${ distro }/wsl-helper-1.11.1`, 'kubeconfig', '--enable=false'], + [`/${ distro }/wsl-helper-1.11.1`, 'kubeconfig', '--enable=true'], ['/bin/sh', '-c', 'mkdir -p "$HOME/.docker/cli-plugins"'], ['/bin/sh', '-c', `if [ ! -e "$HOME/.docker/cli-plugins/docker-compose" -a ! -L "$HOME/.docker/cli-plugins/docker-compose" ] ; then @@ -112,28 +112,28 @@ test.describe('WSL Integrations', () => { stdout: '/dev/null', }, { - args: ['--distribution', distro, '--user', 'root', '--exec', `/${ distro }/wsl-helper`, 'docker-proxy', 'serve', '--verbose'], + args: ['--distribution', distro, '--user', 'root', '--exec', `/${ distro }/wsl-helper-1.11.1`, 'docker-proxy', 'serve', '--verbose'], mode: 'repeated', stdout: '/dev/null', }, { - args: ['--distribution', distro, '--user', 'root', '--exec', `/${ distro }/wsl-helper`, 'docker-proxy', 'kill', '--verbose'], + args: ['--distribution', distro, '--user', 'root', '--exec', `/${ distro }/wsl-helper-1.11.1`, 'docker-proxy', 'kill', '--verbose'], mode: 'repeated', stdout: '/dev/null', }, ]), { - args: ['--distribution', 'alpha', '--exec', '/alpha/wsl-helper', 'kubeconfig', '--show'], + args: ['--distribution', 'alpha', '--exec', '/alpha/wsl-helper-1.11.1', 'kubeconfig', '--show'], mode: 'repeated', stdout: (opts?.alpha ?? false).toString(), }, { - args: ['--distribution', 'beta', '--exec', '/beta/wsl-helper', 'kubeconfig', '--show'], + args: ['--distribution', 'beta', '--exec', '/beta/wsl-helper-1.11.1', 'kubeconfig', '--show'], mode: 'repeated', stdout: (opts?.beta ?? true).toString(), }, { - args: ['--distribution', 'gamma', '--exec', '/gamma/wsl-helper', 'kubeconfig', '--show'], + args: ['--distribution', 'gamma', '--exec', '/gamma/wsl-helper-1.11.1', 'kubeconfig', '--show'], mode: 'repeated', stdout: (opts?.gamma ?? 'some error').toString(), }, diff --git a/pkg/rancher-desktop/utils/resources.ts b/pkg/rancher-desktop/utils/resources.ts index c21a205a8be..ae42bc8c449 100644 --- a/pkg/rancher-desktop/utils/resources.ts +++ b/pkg/rancher-desktop/utils/resources.ts @@ -15,8 +15,8 @@ const executableMap: Record = { kubectl: undefined, nerdctl: undefined, rdctl: undefined, - 'wsl-helper': [paths.resources, process.platform, platformBinary('wsl-helper')], - 'wsl-helper-linux': [paths.resources, 'linux', 'wsl-helper'], + 'wsl-helper': [paths.resources, process.platform, platformBinary('wsl-helper-1.11.1')], + 'wsl-helper-linux': [paths.resources, 'linux', 'wsl-helper-1.11.1'], }; function platformBinary(name: string): string { diff --git a/scripts/lib/build-utils.ts b/scripts/lib/build-utils.ts index 08916c270e1..b01632731ed 100644 --- a/scripts/lib/build-utils.ts +++ b/scripts/lib/build-utils.ts @@ -269,7 +269,8 @@ export default { * @param platform The platform to build for. */ const buildPlatform = async(platform: 'linux' | 'win32') => { - const exeName = platform === 'win32' ? 'wsl-helper.exe' : 'wsl-helper'; + const exeRoot = 'wsl-helper-1.11.1'; + const exeName = `${ exeRoot }${ platform === 'win32' ? '.exe' : '' }`; const outFile = path.join(this.rootDir, 'resources', platform, exeName); await this.spawn('go', 'build', '-ldflags', '-s -w', '-o', outFile, '.', { diff --git a/scripts/lib/sign-win32.ts b/scripts/lib/sign-win32.ts index 0ec67c7fa23..8b080654e11 100644 --- a/scripts/lib/sign-win32.ts +++ b/scripts/lib/sign-win32.ts @@ -64,7 +64,7 @@ export async function sign(workDir: string) { const binDir = path.join(resourcesRootDir, 'bin'); const whiteList: Record> = { '.': ['Rancher Desktop.exe'], - [resourcesRootDir]: ['wsl-helper.exe'], + [resourcesRootDir]: ['wsl-helper-1.11.1.exe'], [internalDir]: ['host-resolver.exe', 'host-switch.exe', 'privileged-service.exe', 'steve.exe', 'vtunnel.exe'], [binDir]: ['docker.exe', 'docker-credential-none.exe', 'nerdctl.exe', 'rdctl.exe'], };