Skip to content

Commit

Permalink
feat: allow users to pick between WSL and HyperV if both enabled (pod…
Browse files Browse the repository at this point in the history
…man-desktop#8864) (podman-desktop#9105)

* feat: allow users to pick between WSL and HyperV if both enabled (podman-desktop#8864)

Signed-off-by: lstocchi <[email protected]>

* fix: add tests

Signed-off-by: lstocchi <[email protected]>

* fix: retrieve selected provider when creating machine

Signed-off-by: lstocchi <[email protected]>

* fix: round memory to a multiple of 2Mb to be complaint to hyperv requirementes

Signed-off-by: lstocchi <[email protected]>

* fix: disable usermode for hyperv

Signed-off-by: lstocchi <[email protected]>

---------

Signed-off-by: lstocchi <[email protected]>
  • Loading branch information
lstocchi authored Sep 30, 2024
1 parent 4c09e30 commit 1982569
Show file tree
Hide file tree
Showing 6 changed files with 243 additions and 73 deletions.
19 changes: 15 additions & 4 deletions extensions/podman/packages/extension/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@
"maximum": "HOST_TOTAL_CPU",
"scope": "ContainerProviderConnectionFactory",
"description": "CPU(s)",
"when": "podman.podmanMachineCpuSupported"
"when": "podman.podmanMachineCpuSupported && !podman.isCreateWSLOptionSelected"
},
"podman.factory.machine.memory": {
"type": "number",
Expand All @@ -130,7 +130,7 @@
"scope": "ContainerProviderConnectionFactory",
"step": 500000000,
"description": "Memory",
"when": "podman.podmanMachineMemorySupported"
"when": "podman.podmanMachineMemorySupported && !podman.isCreateWSLOptionSelected"
},
"podman.factory.machine.diskSize": {
"type": "number",
Expand All @@ -141,7 +141,7 @@
"step": 500000000,
"scope": "ContainerProviderConnectionFactory",
"description": "Disk size",
"when": "podman.podmanMachineDiskSupported"
"when": "podman.podmanMachineDiskSupported && !podman.isCreateWSLOptionSelected"
},
"podman.factory.machine.image-path": {
"type": "string",
Expand All @@ -161,7 +161,7 @@
"default": false,
"scope": "ContainerProviderConnectionFactory",
"markdownDescription": "User mode networking (traffic relayed by a user process). See [documentation](https://docs.podman.io/en/latest/markdown/podman-machine-init.1.html#user-mode-networking).",
"when": "podman.isUserModeNetworkingSupported == true"
"when": "podman.isUserModeNetworkingSupported == true && podman.isCreateWSLOptionSelected"
},
"podman.factory.machine.provider": {
"type": "string",
Expand All @@ -174,6 +174,17 @@
"description": "Provider Type",
"when": "podman.isLibkrunSupported"
},
"podman.factory.machine.win.provider": {
"type": "string",
"default": "wsl",
"enum": [
"wsl",
"hyperv"
],
"scope": "ContainerProviderConnectionFactory",
"description": "Provider Type",
"when": "podman.wslHypervEnabled"
},
"podman.factory.machine.now": {
"type": "boolean",
"default": true,
Expand Down
48 changes: 32 additions & 16 deletions extensions/podman/packages/extension/src/extension.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -329,7 +329,7 @@ test('verify create command called with correct values', async () => {
});
expect(spyExecPromise).toBeCalledWith(
podmanCli.getPodmanCli(),
['machine', 'init', '--cpus', '2', '--memory', '999', '--disk-size', '232', '--image-path', 'path', '--rootful'],
['machine', 'init', '--cpus', '2', '--memory', '1000', '--disk-size', '232', '--image-path', 'path', '--rootful'],
{
logger: undefined,
token: undefined,
Expand Down Expand Up @@ -372,7 +372,7 @@ test('verify create command called with correct values with user mode networking
'--cpus',
'2',
'--memory',
'999',
'1000',
'--disk-size',
'232',
'--image-path',
Expand Down Expand Up @@ -418,7 +418,7 @@ test('verify create command called with now flag if start machine after creation
'--cpus',
'2',
'--memory',
'999',
'1000',
'--disk-size',
'232',
'--image-path',
Expand Down Expand Up @@ -2027,24 +2027,25 @@ describe('calcPodmanMachineSetting', () => {

test('setValue to true if OS is MacOS', async () => {
vi.mocked(isWindows).mockReturnValue(false);
await extension.calcPodmanMachineSetting(podmanConfiguration);
await extension.calcPodmanMachineSetting();
expect(extensionApi.context.setValue).toBeCalledWith(extension.PODMAN_MACHINE_CPU_SUPPORTED_KEY, true);
expect(extensionApi.context.setValue).toBeCalledWith(extension.PODMAN_MACHINE_MEMORY_SUPPORTED_KEY, true);
expect(extensionApi.context.setValue).toBeCalledWith(extension.PODMAN_MACHINE_DISK_SUPPORTED_KEY, true);
});
test('setValue to true if OS is Windows and uses HyperV - set env variable', async () => {
vi.mocked(isWindows).mockReturnValue(true);
process.env.CONTAINERS_MACHINE_PROVIDER = 'hyperv';
vi.spyOn(podmanConfiguration, 'matchRegexpInContainersConfig').mockResolvedValue(false);
await extension.calcPodmanMachineSetting(podmanConfiguration);
expect(extensionApi.context.setValue).toBeCalledWith(extension.PODMAN_MACHINE_CPU_SUPPORTED_KEY, true);
expect(extensionApi.context.setValue).toBeCalledWith(extension.PODMAN_MACHINE_MEMORY_SUPPORTED_KEY, true);
expect(extensionApi.context.setValue).toBeCalledWith(extension.PODMAN_MACHINE_DISK_SUPPORTED_KEY, true);
});
test('setValue to true if OS is Windows and uses HyperV - set by config file', async () => {
vi.mocked(isWindows).mockReturnValue(true);
vi.spyOn(podmanConfiguration, 'matchRegexpInContainersConfig').mockResolvedValue(true);
await extension.calcPodmanMachineSetting(podmanConfiguration);
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',
});
}
});
});
await extension.calcPodmanMachineSetting();
expect(extensionApi.context.setValue).toBeCalledWith(extension.PODMAN_MACHINE_CPU_SUPPORTED_KEY, true);
expect(extensionApi.context.setValue).toBeCalledWith(extension.PODMAN_MACHINE_MEMORY_SUPPORTED_KEY, true);
expect(extensionApi.context.setValue).toBeCalledWith(extension.PODMAN_MACHINE_DISK_SUPPORTED_KEY, true);
Expand All @@ -2053,7 +2054,7 @@ describe('calcPodmanMachineSetting', () => {
vi.mocked(isWindows).mockReturnValue(true);
process.env.CONTAINERS_MACHINE_PROVIDER = 'wsl';
vi.spyOn(podmanConfiguration, 'matchRegexpInContainersConfig').mockResolvedValue(false);
await extension.calcPodmanMachineSetting(podmanConfiguration);
await extension.calcPodmanMachineSetting();
expect(extensionApi.context.setValue).toBeCalledWith(extension.PODMAN_MACHINE_CPU_SUPPORTED_KEY, false);
expect(extensionApi.context.setValue).toBeCalledWith(extension.PODMAN_MACHINE_MEMORY_SUPPORTED_KEY, false);
expect(extensionApi.context.setValue).toBeCalledWith(extension.PODMAN_MACHINE_DISK_SUPPORTED_KEY, false);
Expand Down Expand Up @@ -2619,3 +2620,18 @@ test('getJSONMachineList should get machines from hyperv and wsl if both are ena
expect(execPodmanSpy).toHaveBeenNthCalledWith(1, ['machine', 'list', '--format', 'json'], 'wsl');
expect(execPodmanSpy).toHaveBeenNthCalledWith(2, ['machine', 'list', '--format', 'json'], 'hyperv');
});

describe('updateWSLHyperVEnabledValue', () => {
beforeEach(() => {
extension.updateWSLHyperVEnabledValue(true);
vi.resetAllMocks();
});
test('setValue should be called if new value is different than wslAndHypervEnabled', async () => {
extension.updateWSLHyperVEnabledValue(false);
expect(extensionApi.context.setValue).toBeCalledWith(extension.WSL_HYPERV_ENABLED_KEY, false);
});
test('setValue should not be called if new value is equal to wslAndHypervEnabled', async () => {
extension.updateWSLHyperVEnabledValue(true);
expect(extensionApi.context.setValue).not.toBeCalled();
});
});
71 changes: 58 additions & 13 deletions extensions/podman/packages/extension/src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,9 @@ const krunkitHelper = new KrunkitHelper();
const podmanBinaryHelper = new PodmanBinaryLocationHelper();
const podmanInfoHelper = new PodmanInfoHelper();

let createWSLMachineOptionSelected = false;
let wslAndHypervEnabled = false;

let shouldNotifySetup = true;
const setupPodmanNotification: extensionApi.NotificationOptions = {
title: 'Podman needs to be set up',
Expand Down Expand Up @@ -1023,6 +1026,8 @@ export const PODMAN_MACHINE_CPU_SUPPORTED_KEY = 'podman.podmanMachineCpuSupporte
export const PODMAN_MACHINE_MEMORY_SUPPORTED_KEY = 'podman.podmanMachineMemorySupported';
export const PODMAN_MACHINE_DISK_SUPPORTED_KEY = 'podman.podmanMachineDiskSupported';
export const PODMAN_PROVIDER_LIBKRUN_SUPPORTED_KEY = 'podman.isLibkrunSupported';
export const CREATE_WSL_MACHINE_OPTION_SELECTED_KEY = 'podman.isCreateWSLOptionSelected';
export const WSL_HYPERV_ENABLED_KEY = 'podman.wslHypervEnabled';

export function initTelemetryLogger(): void {
telemetryLogger = extensionApi.env.createTelemetryLogger();
Expand Down Expand Up @@ -1276,6 +1281,8 @@ export async function activate(extensionContext: extensionApi.ExtensionContext):
extensionApi.context.setValue(START_NOW_MACHINE_INIT_SUPPORTED_KEY, isStartNowAtMachineInitSupported(version));
extensionApi.context.setValue(USER_MODE_NETWORKING_SUPPORTED_KEY, isUserModeNetworkingSupported(version));
extensionApi.context.setValue(PODMAN_PROVIDER_LIBKRUN_SUPPORTED_KEY, isLibkrunSupported(version));
const wslHypervEnabled = (await isWSLEnabled()) && (await isHyperVEnabled());
updateWSLHyperVEnabledValue(wslHypervEnabled);
isMovedPodmanSocket = isPodmanSocketLocationMoved(version);
}

Expand Down Expand Up @@ -1492,11 +1499,18 @@ export async function activate(extensionContext: extensionApi.ExtensionContext):
// create machines on Linux via Podman Desktop, however we will still support
// the lifecycle management of one.
if (isMac() || isWindows()) {
provider.setContainerProviderConnectionFactory({
initialize: () => createMachine({}),
create: createMachine,
creationDisplayName: 'Podman machine',
});
provider.setContainerProviderConnectionFactory(
{
initialize: () => createMachine({}),
create: createMachine,
creationDisplayName: 'Podman machine',
},
{
auditItems: async (items: extensionApi.AuditRequestItems) => {
return await connectionAuditor(items);
},
},
);
}

// Linux has native container support (no need for Podman Machine), so we don't need to create machines.
Expand Down Expand Up @@ -1669,7 +1683,7 @@ export async function activate(extensionContext: extensionApi.ExtensionContext):
const registrySetup = new RegistrySetup();
await registrySetup.setup();

await calcPodmanMachineSetting(podmanConfiguration);
await calcPodmanMachineSetting();

const podmanRemoteConnections = new PodmanRemoteConnections(extensionContext, provider);
podmanRemoteConnections.start();
Expand All @@ -1679,17 +1693,30 @@ export async function activate(extensionContext: extensionApi.ExtensionContext):
};
}

export async function calcPodmanMachineSetting(podmanConfiguration: PodmanConfiguration): Promise<void> {
async function connectionAuditor(items: extensionApi.AuditRequestItems): Promise<extensionApi.AuditResult> {
const records: extensionApi.AuditRecord[] = [];
const auditResult = {
records: records,
};
const winProvider = items['podman.factory.machine.win.provider'];
const isWSL = winProvider === 'wsl';
if (createWSLMachineOptionSelected !== isWSL) {
createWSLMachineOptionSelected = isWSL;
extensionApi.context.setValue(CREATE_WSL_MACHINE_OPTION_SELECTED_KEY, createWSLMachineOptionSelected);
}
return auditResult;
}

export async function calcPodmanMachineSetting(): Promise<void> {
let cpuSupported = true;
let memorySupported = true;
let diskSupported = true;

if (isWindows()) {
const isPodmanHyperv_Env = process.env.CONTAINERS_MACHINE_PROVIDER === 'hyperv';
const isPodmanHyperv_Config = await podmanConfiguration.matchRegexpInContainersConfig(/provider\s*=\s*"hyperv"/);
cpuSupported = isPodmanHyperv_Env || isPodmanHyperv_Config;
memorySupported = isPodmanHyperv_Env || isPodmanHyperv_Config;
diskSupported = isPodmanHyperv_Env || isPodmanHyperv_Config;
const isHyperV = await isHyperVEnabled();
cpuSupported = isHyperV;
memorySupported = isHyperV;
diskSupported = isHyperV;
}

extensionApi.context.setValue(PODMAN_MACHINE_CPU_SUPPORTED_KEY, cpuSupported);
Expand Down Expand Up @@ -1745,12 +1772,18 @@ export async function getJSONMachineList(): Promise<MachineJSONListOutput> {
containerMachineProviders.push(...['applehv', 'libkrun']);
}

let wslEnabled = false;
let hypervEnabled = false;
if (await isWSLEnabled()) {
wslEnabled = true;
containerMachineProviders.push('wsl');
}
if (await isHyperVEnabled()) {
hypervEnabled = true;
containerMachineProviders.push('hyperv');
}
// update context "wsl-hyperv enabled" value
updateWSLHyperVEnabledValue(wslEnabled && hypervEnabled);

if (containerMachineProviders.length === 0) {
// in all other cases we set undefined so that it executes normally by using the default container provider
Expand Down Expand Up @@ -1937,6 +1970,9 @@ export async function createMachine(
if (params['podman.factory.machine.provider']) {
provider = getProviderByLabel(params['podman.factory.machine.provider']);
telemetryRecords.provider = provider;
} else if (params['podman.factory.machine.win.provider']) {
provider = params['podman.factory.machine.win.provider'];
telemetryRecords.provider = provider;
}

// cpus
Expand All @@ -1955,7 +1991,9 @@ export async function createMachine(
if (params['podman.factory.machine.memory']) {
parameters.push('--memory');
const memoryAsMiB = +params['podman.factory.machine.memory'] / (1024 * 1024);
parameters.push(Math.floor(memoryAsMiB).toString());
// Hyper-V requires VMs to have memory in 2 MB increments. So we round it
const roundedMemoryMiB = Math.floor((memoryAsMiB + 1) / 2) * 2;
parameters.push(roundedMemoryMiB.toString());
telemetryRecords.memory = params['podman.factory.machine.memory'];
}

Expand Down Expand Up @@ -2161,3 +2199,10 @@ export async function handleCompatibilityModeSetting(): Promise<void> {
await socketCompatibilityMode.disable();
}
}

export function updateWSLHyperVEnabledValue(value: boolean): void {
if (wslAndHypervEnabled !== value) {
wslAndHypervEnabled = value;
extensionApi.context.setValue(WSL_HYPERV_ENABLED_KEY, value);
}
}
Loading

0 comments on commit 1982569

Please sign in to comment.