Skip to content

Commit

Permalink
feat: handle both hyperv and wsl machines (#8985)
Browse files Browse the repository at this point in the history
Signed-off-by: lstocchi <[email protected]>
  • Loading branch information
lstocchi authored Sep 25, 2024
1 parent da9535b commit e6cd9a7
Show file tree
Hide file tree
Showing 4 changed files with 363 additions and 30 deletions.
294 changes: 279 additions & 15 deletions extensions/podman/packages/extension/src/extension.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ import { PodmanConfiguration } from './podman-configuration';
import type { UpdateCheck } from './podman-install';
import { PodmanInstall } from './podman-install';
import { getAssetsFolder, isLinux, isMac, isWindows, LIBKRUN_LABEL, LoggerDelegator, VMTYPE } from './util';
import * as util from './util';

const config: Configuration = {
get: () => {
Expand Down Expand Up @@ -284,21 +285,6 @@ vi.mock('./podman-info-helper', async () => {
}),
};
});
vi.mock('./wsl-helper', async () => {
return {
WslHelper: vi.fn().mockImplementation(() => {
return {
getWSLVersionData: vi.fn().mockImplementation(() => {
return Promise.resolve({
wslVersion: '1.2.3',
kernelVersion: '1.2.3',
windowsVersion: '1.2.3',
});
}),
};
}),
};
});

vi.mock('./util', async () => {
// eslint-disable-next-line @typescript-eslint/consistent-type-imports
Expand Down Expand Up @@ -1947,6 +1933,36 @@ describe('registerOnboardingRemoveUnsupportedMachinesCommand', () => {
stdout: 'podman version 5.0.0',
} as unknown as extensionApi.RunResult);

vi.mocked(extensionApi.process.exec).mockResolvedValueOnce({
stdout: 'unknown message: 1.2.5.0',
stderr: '',
command: 'command',
});

vi.mocked(extensionApi.process.exec).mockResolvedValueOnce({
stdout: 'True',
stderr: '',
command: 'command',
});

vi.mocked(extensionApi.process.exec).mockResolvedValueOnce({
stdout: 'True',
stderr: '',
command: 'command',
});

vi.mocked(extensionApi.process.exec).mockResolvedValueOnce({
stdout: 'True',
stderr: '',
command: 'command',
});

vi.mocked(extensionApi.process.exec).mockResolvedValueOnce({
stdout: 'Running',
stderr: '',
command: 'command',
});

// two times false (no qemu folders)
vi.mocked(fs.existsSync).mockReturnValueOnce(false);
vi.mocked(fs.existsSync).mockReturnValueOnce(false);
Expand Down Expand Up @@ -2337,3 +2353,251 @@ test('activate function returns an api implementation', async () => {
expect(api).toBeDefined();
expect(typeof api.exec).toBe('function');
});

test('isHypervEnabled should return false if it is not windows', async () => {
vi.mocked(isWindows).mockReturnValue(false);
const hypervEnabled = await extension.isHyperVEnabled();
expect(hypervEnabled).toBeFalsy();
});

test('isHypervEnabled should return false if hyperv is not enabled', async () => {
vi.mocked(isWindows).mockReturnValue(true);
const hypervEnabled = await extension.isHyperVEnabled();
expect(hypervEnabled).toBeFalsy();
});

test('isHypervEnabled should return true if hyperv is enabled', async () => {
vi.mocked(isWindows).mockReturnValue(true);
vi.spyOn(extensionApi.process, 'exec').mockImplementation((command, args) => {
return new Promise<extensionApi.RunResult>(resolve => {
if (command === 'powershell.exe') {
resolve({
stdout: args?.[0] === '@(Get-Service vmms).Status' ? 'Running' : 'True',
stderr: '',
command: 'command',
});
}
});
});
const wslHypervEnabled = await extension.isHyperVEnabled();
expect(wslHypervEnabled).toBeTruthy();
});

test('isWSLEnabled should return false if it is not windows', async () => {
vi.mocked(isWindows).mockReturnValue(false);
const wslEnabled = await extension.isWSLEnabled();
expect(wslEnabled).toBeFalsy();
});

test('isWSLEnabled should return false if wsl is not enabled', async () => {
vi.mocked(isWindows).mockReturnValue(true);
vi.spyOn(extensionApi.process, 'exec').mockResolvedValue({
stdout: 'unknown message: 1.2.5.0',
stderr: '',
command: 'command',
});
const wslEnabled = await extension.isWSLEnabled();
expect(wslEnabled).toBeFalsy();
});

test('isWSLEnabled should return true if wsl is enabled', async () => {
vi.mocked(isWindows).mockReturnValue(true);
vi.spyOn(extensionApi.process, 'exec').mockImplementation(command => {
return new Promise<extensionApi.RunResult>(resolve => {
if (command === 'wsl') {
resolve({
stdout:
'WSL version: 2.2.5.0\nKernel version: 5.15.90.1\nWSLg version: 1.0.51\nMSRDC version: 1.2.3770\nDirect3D version: 1.608.2-61064218\nDXCore version: 10.0.25131.1002-220531-1700.rs-onecore-base2-hyp\nWindows version: 10.0.22621.2134',
stderr: '',
command: 'command',
});
}
if (command === 'powershell.exe') {
resolve({
stdout: 'True',
stderr: '',
command: 'command',
});
}
});
});
const wslEnabled = await extension.isWSLEnabled();
expect(wslEnabled).toBeTruthy();
});

test('getJSONMachineList should only get machines from wsl if hyperv is not enabled', async () => {
vi.mocked(isWindows).mockReturnValue(true);
vi.mocked(isMac).mockReturnValue(false);
vi.spyOn(config, 'get').mockReturnValue('');
vi.spyOn(extensionApi.process, 'exec').mockImplementation((command, args) => {
return new Promise<extensionApi.RunResult>(resolve => {
if (command !== 'wsl' && args?.[0] === '--version') {
resolve({
stdout: 'podman version 5.1.1',
} as extensionApi.RunResult);
}
if (command === 'wsl') {
resolve({
stdout:
'WSL version: 2.2.5.0\nKernel version: 5.15.90.1\nWSLg version: 1.0.51\nMSRDC version: 1.2.3770\nDirect3D version: 1.608.2-61064218\nDXCore version: 10.0.25131.1002-220531-1700.rs-onecore-base2-hyp\nWindows version: 10.0.22621.2134',
stderr: '',
command: 'command',
});
}
if (command === 'powershell.exe') {
resolve({
stdout: 'True',
stderr: '',
command: 'command',
});
}
});
});
const fakeJSON: extension.MachineJSON[] = [
{
Name: 'podman-machine-default',
CPUs: 2,
Memory: '1048000000',
DiskSize: '250000000000',
Running: true,
Starting: false,
Default: true,
VMType: VMTYPE.LIBKRUN,
},
{
Name: 'podman-machine-1',
CPUs: 2,
Memory: '1048000000',
DiskSize: '250000000000',
Running: false,
Starting: false,
Default: false,
VMType: VMTYPE.LIBKRUN,
},
];
const execPodmanSpy = vi.spyOn(util, 'execPodman').mockResolvedValue({
stdout: JSON.stringify(fakeJSON),
stderr: '',
command: '',
});
await extension.getJSONMachineList();
expect(execPodmanSpy).toBeCalledWith(['machine', 'list', '--format', 'json'], 'wsl');
});

test('getJSONMachineList should only get machines from hyperv if wsl is not enabled', async () => {
vi.mocked(isWindows).mockReturnValue(true);
vi.mocked(isMac).mockReturnValue(false);
vi.spyOn(config, 'get').mockReturnValue('');
vi.spyOn(extensionApi.process, 'exec').mockImplementation((command, args) => {
return new Promise<extensionApi.RunResult>(resolve => {
if (command !== 'wsl' && args?.[0] === '--version') {
resolve({
stdout: 'podman version 5.1.1',
} as extensionApi.RunResult);
}
if (command === 'wsl') {
resolve({
stdout: 'WSL version: invalid',
stderr: '',
command: 'command',
});
}
if (command === 'powershell.exe') {
resolve({
stdout: args?.[0] === '@(Get-Service vmms).Status' ? 'Running' : 'True',
stderr: '',
command: 'command',
});
}
});
});
const fakeJSON: extension.MachineJSON[] = [
{
Name: 'podman-machine-default',
CPUs: 2,
Memory: '1048000000',
DiskSize: '250000000000',
Running: true,
Starting: false,
Default: true,
VMType: VMTYPE.LIBKRUN,
},
{
Name: 'podman-machine-1',
CPUs: 2,
Memory: '1048000000',
DiskSize: '250000000000',
Running: false,
Starting: false,
Default: false,
VMType: VMTYPE.LIBKRUN,
},
];
const execPodmanSpy = vi.spyOn(util, 'execPodman').mockResolvedValue({
stdout: JSON.stringify(fakeJSON),
stderr: '',
command: '',
});
await extension.getJSONMachineList();
expect(execPodmanSpy).toBeCalledWith(['machine', 'list', '--format', 'json'], 'hyperv');
});

test('getJSONMachineList should get machines from hyperv and wsl if both are enabled', async () => {
vi.mocked(isWindows).mockReturnValue(true);
vi.mocked(isMac).mockReturnValue(false);
vi.spyOn(config, 'get').mockReturnValue('');
vi.spyOn(extensionApi.process, 'exec').mockImplementation((command, args) => {
return new Promise<extensionApi.RunResult>(resolve => {
if (command !== 'wsl' && args?.[0] === '--version') {
resolve({
stdout: 'podman version 5.1.1',
} as extensionApi.RunResult);
}
if (command === 'wsl') {
resolve({
stdout:
'WSL version: 2.2.5.0\nKernel version: 5.15.90.1\nWSLg version: 1.0.51\nMSRDC version: 1.2.3770\nDirect3D version: 1.608.2-61064218\nDXCore version: 10.0.25131.1002-220531-1700.rs-onecore-base2-hyp\nWindows version: 10.0.22621.2134',
stderr: '',
command: 'command',
});
}
if (command === 'powershell.exe') {
resolve({
stdout: args?.[0] === '@(Get-Service vmms).Status' ? 'Running' : 'True',
stderr: '',
command: 'command',
});
}
});
});
const fakeJSON: extension.MachineJSON[] = [
{
Name: 'podman-machine-default',
CPUs: 2,
Memory: '1048000000',
DiskSize: '250000000000',
Running: true,
Starting: false,
Default: true,
VMType: VMTYPE.LIBKRUN,
},
{
Name: 'podman-machine-1',
CPUs: 2,
Memory: '1048000000',
DiskSize: '250000000000',
Running: false,
Starting: false,
Default: false,
VMType: VMTYPE.LIBKRUN,
},
];
const execPodmanSpy = vi.spyOn(util, 'execPodman').mockResolvedValue({
stdout: JSON.stringify(fakeJSON),
stderr: '',
command: '',
});
await extension.getJSONMachineList();
expect(execPodmanSpy).toHaveBeenNthCalledWith(1, ['machine', 'list', '--format', 'json'], 'wsl');
expect(execPodmanSpy).toHaveBeenNthCalledWith(2, ['machine', 'list', '--format', 'json'], 'hyperv');
});
32 changes: 30 additions & 2 deletions extensions/podman/packages/extension/src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import * as extensionApi from '@podman-desktop/api';
import { compareVersions } from 'compare-versions';

import type { PodmanExtensionApi, PodmanRunOptions } from '../../api/src/podman-extension-api';
import { SequenceCheck } from './base-check';
import { getSocketCompatibility } from './compatibility-mode';
import { getDetectionChecks } from './detection-checks';
import { KrunkitHelper } from './krunkit-helper';
Expand All @@ -37,7 +38,7 @@ import type { InstalledPodman } from './podman-cli';
import { getPodmanCli, getPodmanInstallation } from './podman-cli';
import { PodmanConfiguration } from './podman-configuration';
import { PodmanInfoHelper } from './podman-info-helper';
import { PodmanInstall } from './podman-install';
import { HyperVCheck, PodmanInstall, WSL2Check, WSLVersionCheck } from './podman-install';
import { PodmanRemoteConnections } from './podman-remote-connections';
import { QemuHelper } from './qemu-helper';
import { RegistrySetup } from './registry-setup';
Expand Down Expand Up @@ -1742,7 +1743,16 @@ export async function getJSONMachineList(): Promise<MachineJSONListOutput> {
// if libkrun is supported we want to show both applehv and libkrun machines
if (installedPodman && isLibkrunSupported(installedPodman.version)) {
containerMachineProviders.push(...['applehv', 'libkrun']);
} else {
}

if (await isWSLEnabled()) {
containerMachineProviders.push('wsl');
}
if (await isHyperVEnabled()) {
containerMachineProviders.push('hyperv');
}

if (containerMachineProviders.length === 0) {
// in all other cases we set undefined so that it executes normally by using the default container provider
containerMachineProviders.push(undefined);
}
Expand Down Expand Up @@ -1807,6 +1817,24 @@ export function isLibkrunSupported(podmanVersion: string): boolean {
return isMac() && compareVersions(podmanVersion, PODMAN_MINIMUM_VERSION_FOR_LIBKRUN_SUPPORT) >= 0;
}

export async function isWSLEnabled(): Promise<boolean> {
if (!isWindows()) {
return false;
}
const wslCheck = new SequenceCheck('WSL platform', [new WSLVersionCheck(), new WSL2Check()]);
const wslCheckResult = await wslCheck.execute();
return wslCheckResult.successful;
}

export async function isHyperVEnabled(): Promise<boolean> {
if (!isWindows()) {
return false;
}
const hyperVCheck = new HyperVCheck();
const hyperVCheckResult = await hyperVCheck.execute();
return hyperVCheckResult.successful;
}

export function sendTelemetryRecords(
eventName: string,
telemetryRecords: Record<string, unknown>,
Expand Down
Loading

0 comments on commit e6cd9a7

Please sign in to comment.