Skip to content

Commit

Permalink
feat: add user mode networking parameter for Podman machine (podman-d…
Browse files Browse the repository at this point in the history
…esktop#3251)

* feat: add user mode networking parameter for Podman machine

Fixes podman-desktop#2865

Signed-off-by: Jeff MAURY <[email protected]>

* fix: use when expression to control when user mode networking should be exposed

Signed-off-by: Jeff MAURY <[email protected]>

* fix: fix failing test

Signed-off-by: Jeff MAURY <[email protected]>

* fix: update expression when Podman is updated

Signed-off-by: Jeff MAURY <[email protected]>

* fix: switch description to markdown

Signed-off-by: Jeff MAURY <[email protected]>

* fix: addressing @cdrage review

Signed-off-by: Jeff MAURY <[email protected]>

---------

Signed-off-by: Jeff MAURY <[email protected]>
  • Loading branch information
jeffmaury authored Aug 14, 2023
1 parent f363824 commit 595d6fa
Show file tree
Hide file tree
Showing 7 changed files with 102 additions and 1 deletion.
7 changes: 7 additions & 0 deletions extensions/podman/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,13 @@
"default": true,
"scope": "ContainerProviderConnectionFactory",
"description": "Machine with root privileges"
},
"podman.factory.machine.user-mode-networking": {
"type": "boolean",
"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"
}
}
},
Expand Down
41 changes: 41 additions & 0 deletions extensions/podman/src/extension.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,47 @@ test('verify create command called with correct values', async () => {
expect(console.error).not.toBeCalled();
});

test('verify create command called with correct values with user mode networking', async () => {
const spyExecPromise = vi.spyOn(podmanCli, 'execPromise');
spyExecPromise.mockImplementation(() => {
return Promise.resolve('');
});
await extension.createMachine(
{
'podman.factory.machine.cpus': '2',
'podman.factory.machine.image-path': 'path',
'podman.factory.machine.memory': '1048000000',
'podman.factory.machine.diskSize': '250000000000',
'podman.factory.machine.user-mode-networking': true,
},
undefined,
undefined,
);
const parameters = [
'machine',
'init',
'--cpus',
'2',
'--memory',
'999',
'--disk-size',
'232',
'--image-path',
'path',
'--user-mode-networking',
];
expect(spyExecPromise).toBeCalledWith(
getPodmanCli(),
parameters,
{
env: {},
logger: undefined,
},
undefined,
);
expect(console.error).not.toBeCalled();
});

test('test checkDefaultMachine, if the machine running is not default, the function will prompt', async () => {
await extension.checkDefaultMachine(fakeMachineJSON);

Expand Down
25 changes: 25 additions & 0 deletions extensions/podman/src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import { PodmanConfiguration } from './podman-configuration';
import { getDetectionChecks } from './detection-checks';
import { getDisguisedPodmanInformation, getSocketPath, isDisguisedPodman } from './warnings';
import { getSocketCompatibility } from './compatibility-mode';
import { compareVersions } from 'compare-versions';

type StatusHandler = (name: string, event: extensionApi.ProviderConnectionStatus) => void;

Expand Down Expand Up @@ -68,6 +69,7 @@ export type MachineJSON = {
Running: boolean;
Starting: boolean;
Default: boolean;
UserModeNetworking?: boolean;
};

export type ConnectionJSON = {
Expand All @@ -83,6 +85,7 @@ export type MachineInfo = {
cpus: number;
memory: number;
diskSize: number;
userModeNetworking: boolean;
};

async function updateMachines(provider: extensionApi.Provider): Promise<void> {
Expand All @@ -109,11 +112,14 @@ async function updateMachines(provider: extensionApi.Provider): Promise<void> {
listeners.forEach(listener => listener(machine.Name, status));
podmanMachinesStatuses.set(machine.Name, status);
}

const userModeNetworking = isWindows() ? machine.UserModeNetworking : true;
podmanMachinesInfo.set(machine.Name, {
name: machine.Name,
memory: parseInt(machine.Memory),
cpus: machine.CPUs,
diskSize: parseInt(machine.DiskSize),
userModeNetworking: userModeNetworking,
});

if (!podmanMachinesStatuses.has(machine.Name)) {
Expand Down Expand Up @@ -597,6 +603,7 @@ async function doHandleWSLDistroNotFoundError(
'podman.factory.machine.diskSize': machineInfo.diskSize,
},
undefined,
undefined,
);
} catch (error) {
console.error(error);
Expand Down Expand Up @@ -626,13 +633,19 @@ async function registerUpdatesIfAny(
}
}

export const USER_MODE_NETWORKING_SUPPORTED_KEY = 'podman.isUserModeNetworkingSupported';

export async function activate(extensionContext: extensionApi.ExtensionContext): Promise<void> {
storedExtensionContext = extensionContext;
const podmanInstall = new PodmanInstall(extensionContext.storagePath);

const installedPodman = await getPodmanInstallation();
const version: string | undefined = installedPodman?.version;

if (version) {
extensionApi.context.setValue(USER_MODE_NETWORKING_SUPPORTED_KEY, isUserModeNetworkingSupported(version));
}

const detectionChecks: extensionApi.ProviderDetectionCheck[] = [];
let status: extensionApi.ProviderStatus = 'not-installed';
if (version) {
Expand Down Expand Up @@ -1028,6 +1041,13 @@ export async function deactivate(): Promise<void> {
});
}

const PODMAN_MINIMUM_VERSION_FOR_USER_MODE_NETWORKING = '4.6.0';

// Checks if user mode networking is supported. Only Windows platform allows this parameter to be tuned
export function isUserModeNetworkingSupported(podmanVersion: string) {
return isWindows() && compareVersions(podmanVersion, PODMAN_MINIMUM_VERSION_FOR_USER_MODE_NETWORKING) >= 0;
}

export async function createMachine(
// eslint-disable-next-line @typescript-eslint/no-explicit-any
params: { [key: string]: any },
Expand Down Expand Up @@ -1083,6 +1103,10 @@ export async function createMachine(
parameters.push('--rootful');
}

if (params['podman.factory.machine.user-mode-networking']) {
parameters.push('--user-mode-networking');
}

// name at the end
if (params['podman.factory.machine.name']) {
parameters.push(params['podman.factory.machine.name']);
Expand Down Expand Up @@ -1115,6 +1139,7 @@ export async function createMachine(
}
await execPromise(getPodmanCli(), parameters, { logger, env }, token);
}

function setupDisguisedPodmanSocketWatcher(
provider: extensionApi.Provider,
socketFile: string,
Expand Down
1 change: 1 addition & 0 deletions extensions/podman/src/podman-install.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ vi.mock('./util', async () => {
return {
getAssetsFolder: vi.fn().mockReturnValue(''),
runCliCommand: vi.fn(),
appHomeDir: vi.fn().mockReturnValue(''),
};
});

Expand Down
9 changes: 9 additions & 0 deletions extensions/podman/src/podman-install.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import { getAssetsFolder, runCliCommand } from './util';
import { getDetectionChecks } from './detection-checks';
import { BaseCheck } from './base-check';
import { MacCPUCheck, MacMemoryCheck, MacPodmanInstallCheck, MacVersionCheck } from './macos-checks';
import { isUserModeNetworkingSupported, USER_MODE_NETWORKING_SUPPORTED_KEY } from './extension';

const readFile = promisify(fs.readFile);
const writeFile = promisify(fs.writeFile);
Expand Down Expand Up @@ -128,6 +129,10 @@ export class PodmanInstall {
// write podman version
if (newInstalledPodman) {
this.podmanInfo.podmanVersion = newInstalledPodman.version;
extensionApi.context.setValue(
USER_MODE_NETWORKING_SUPPORTED_KEY,
isUserModeNetworkingSupported(newInstalledPodman.version),
);
}
// update detections checks
provider.updateDetectionChecks(getDetectionChecks(newInstalledPodman));
Expand Down Expand Up @@ -173,6 +178,10 @@ export class PodmanInstall {
provider.updateDetectionChecks(getDetectionChecks(installedPodman));
provider.updateVersion(updateInfo.bundledVersion);
this.podmanInfo.ignoreVersionUpdate = undefined;
extensionApi.context.setValue(
USER_MODE_NETWORKING_SUPPORTED_KEY,
isUserModeNetworkingSupported(updateInfo.bundledVersion),
);
} else if (answer === 'Ignore') {
this.podmanInfo.ignoreVersionUpdate = updateInfo.bundledVersion;
}
Expand Down
1 change: 1 addition & 0 deletions packages/main/src/plugin/configuration-registry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ export interface IConfigurationPropertySchema {
readonly?: boolean;
hidden?: boolean;
enum?: string[];
when?: string;
}

export type ConfigurationScope =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import {
} from './preferences-connection-rendering-task';
/* eslint-disable import/no-duplicates */
// https://github.com/import-js/eslint-plugin-import/issues/1479
import { get } from 'svelte/store';
import { get, type Unsubscriber } from 'svelte/store';
import { onDestroy, onMount } from 'svelte';
/* eslint-enable import/no-duplicates */
import { createConnectionsInfo } from '/@/stores/create-connections';
Expand All @@ -30,6 +30,9 @@ import AuditMessageBox from '../ui/AuditMessageBox.svelte';
import EmptyScreen from '../ui/EmptyScreen.svelte';
import { faCubes } from '@fortawesome/free-solid-svg-icons';
import Button from '../ui/Button.svelte';
import type { ContextUI } from '/@/lib/context/context';
import { ContextKeyExpr } from '/@/lib/context/contextKey';
import { context } from '/@/stores/context';
export let properties: IConfigurationPropertyRecordedSchema[] = [];
export let providerInfo: ProviderInfo;
Expand Down Expand Up @@ -69,8 +72,12 @@ let errorMessage = undefined;
let formEl;
let globalContext: ContextUI;
$: connectionAuditResult = undefined;
let contextsUnsubscribe: Unsubscriber;
// reconnect the logger handler
$: if (logsTerminal && loggerHandlerKey) {
try {
Expand All @@ -80,13 +87,20 @@ $: if (logsTerminal && loggerHandlerKey) {
}
}
function isPropertyValidInContext(when: string, context: ContextUI) {
const expr = ContextKeyExpr.deserialize(when);
return expr.evaluate(context);
}
onMount(async () => {
osMemory = await window.getOsMemory();
osCpu = await window.getOsCpu();
osFreeDisk = await window.getOsFreeDiskSize();
contextsUnsubscribe = context.subscribe(value => (globalContext = value));
configurationKeys = properties
.filter(property => property.scope === propertyScope)
.filter(property => property.id.startsWith(providerInfo.id))
.filter(property => !property.when || isPropertyValidInContext(property.when, globalContext))
.map(property => {
switch (property.maximum) {
case 'HOST_TOTAL_DISKSIZE': {
Expand Down Expand Up @@ -152,6 +166,9 @@ onDestroy(() => {
if (loggerHandlerKey) {
disconnectUI(loggerHandlerKey);
}
if (contextsUnsubscribe) {
contextsUnsubscribe();
}
});
function handleInvalidComponent() {
Expand Down

0 comments on commit 595d6fa

Please sign in to comment.