mirror of
https://github.com/podman-desktop/podman-desktop
synced 2026-05-24 02:08:24 +00:00
feat: allow to use libkrun and applehv machines starting from 5.2.0 (#8247)
* feat: allow to use libkrun and applehv machines starting from 5.2.0 Signed-off-by: Luca Stocchi <luca@MacBook-Pro-di-Luca.local> * fix: add custom exec function to handle merge of provider and runoptions Signed-off-by: lstocchi <lstocchi@redhat.com> * fix: add tests Signed-off-by: lstocchi <lstocchi@redhat.com> * fix: remove console log Signed-off-by: lstocchi <lstocchi@redhat.com> * fix: use custom label instead of provider name Signed-off-by: Luca Stocchi <luca@MacBook-Pro-di-Luca.local> * fix: add tests Signed-off-by: lstocchi <lstocchi@redhat.com> --------- Signed-off-by: Luca Stocchi <luca@MacBook-Pro-di-Luca.local> Signed-off-by: lstocchi <lstocchi@redhat.com> Co-authored-by: Luca Stocchi <luca@MacBook-Pro-di-Luca.local>
This commit is contained in:
parent
9c35fa24e6
commit
ffe418e952
13 changed files with 396 additions and 78 deletions
|
|
@ -158,6 +158,17 @@
|
|||
"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"
|
||||
},
|
||||
"podman.factory.machine.provider": {
|
||||
"type": "string",
|
||||
"default": "default (Apple HyperVisor)",
|
||||
"enum": [
|
||||
"default (Apple HyperVisor)",
|
||||
"GPU enabled (LibKrun)"
|
||||
],
|
||||
"scope": "ContainerProviderConnectionFactory",
|
||||
"description": "Provider Type",
|
||||
"when": "podman.isLibkrunSupported"
|
||||
},
|
||||
"podman.factory.machine.now": {
|
||||
"type": "boolean",
|
||||
"default": true,
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@ import type { InstalledPodman } from './podman-cli';
|
|||
import * as podmanCli from './podman-cli';
|
||||
import { PodmanConfiguration } from './podman-configuration';
|
||||
import { PodmanInstall } from './podman-install';
|
||||
import { getAssetsFolder, isLinux, isMac, isWindows, LoggerDelegator } from './util';
|
||||
import { getAssetsFolder, isLinux, isMac, isWindows, LIBKRUN_LABEL, LoggerDelegator, VMTYPE } from './util';
|
||||
|
||||
const config: Configuration = {
|
||||
get: () => {
|
||||
|
|
@ -87,6 +87,7 @@ const machineInfo: extension.MachineInfo = {
|
|||
cpuUsage: 0,
|
||||
diskUsage: 0,
|
||||
memoryUsage: 0,
|
||||
vmType: VMTYPE.LIBKRUN,
|
||||
};
|
||||
|
||||
const podmanConfiguration = {} as unknown as PodmanConfiguration;
|
||||
|
|
@ -120,6 +121,7 @@ beforeEach(() => {
|
|||
Running: true,
|
||||
Starting: false,
|
||||
Default: false,
|
||||
VMType: VMTYPE.LIBKRUN,
|
||||
},
|
||||
{
|
||||
Name: machine1Name,
|
||||
|
|
@ -129,6 +131,7 @@ beforeEach(() => {
|
|||
Running: false,
|
||||
Starting: false,
|
||||
Default: true,
|
||||
VMType: VMTYPE.LIBKRUN,
|
||||
},
|
||||
];
|
||||
|
||||
|
|
@ -300,6 +303,7 @@ test('verify create command called with correct values', async () => {
|
|||
'podman.factory.machine.image-path': 'path',
|
||||
'podman.factory.machine.memory': '1048000000', // 1048MB = 999.45MiB
|
||||
'podman.factory.machine.diskSize': '250000000000', // 250GB = 232.83GiB
|
||||
'podman.factory.machine.provider': LIBKRUN_LABEL,
|
||||
},
|
||||
undefined,
|
||||
);
|
||||
|
|
@ -309,6 +313,9 @@ test('verify create command called with correct values', async () => {
|
|||
{
|
||||
logger: undefined,
|
||||
token: undefined,
|
||||
env: {
|
||||
CONTAINERS_MACHINE_PROVIDER: VMTYPE.LIBKRUN,
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
|
|
@ -497,6 +504,7 @@ test('checkDefaultMachine: do not prompt if the running machine is already the d
|
|||
Running: true,
|
||||
Starting: false,
|
||||
Default: true,
|
||||
VMType: VMTYPE.LIBKRUN,
|
||||
},
|
||||
{
|
||||
Name: 'podman-machine-1',
|
||||
|
|
@ -506,6 +514,7 @@ test('checkDefaultMachine: do not prompt if the running machine is already the d
|
|||
Running: false,
|
||||
Starting: false,
|
||||
Default: false,
|
||||
VMType: VMTYPE.LIBKRUN,
|
||||
},
|
||||
];
|
||||
|
||||
|
|
@ -566,6 +575,9 @@ test('if a machine is successfully started it changes its state to started', asy
|
|||
await extension.startMachine(provider, podmanConfiguration, machineInfo);
|
||||
|
||||
expect(spyExecPromise).toBeCalledWith(podmanCli.getPodmanCli(), ['machine', 'start', 'name'], {
|
||||
env: {
|
||||
CONTAINERS_MACHINE_PROVIDER: VMTYPE.LIBKRUN,
|
||||
},
|
||||
logger: new LoggerDelegator(),
|
||||
});
|
||||
|
||||
|
|
@ -794,13 +806,20 @@ test('test checkDefaultMachine - if user wants to change default machine, check
|
|||
|
||||
await extension.checkDefaultMachine(fakeMachineJSON);
|
||||
|
||||
expect(spyExecPromise).toHaveBeenCalledWith(podmanCli.getPodmanCli(), [
|
||||
'system',
|
||||
'connection',
|
||||
'default',
|
||||
`${machineDefaultName}-root`,
|
||||
]);
|
||||
expect(inspectCall).toHaveBeenCalledWith(podmanCli.getPodmanCli(), ['machine', 'inspect', machineDefaultName]);
|
||||
expect(spyExecPromise).toHaveBeenCalledWith(
|
||||
podmanCli.getPodmanCli(),
|
||||
['system', 'connection', 'default', `${machineDefaultName}-root`],
|
||||
{
|
||||
env: {
|
||||
CONTAINERS_MACHINE_PROVIDER: VMTYPE.LIBKRUN,
|
||||
},
|
||||
},
|
||||
);
|
||||
expect(inspectCall).toHaveBeenCalledWith(podmanCli.getPodmanCli(), ['machine', 'inspect', machineDefaultName], {
|
||||
env: {
|
||||
CONTAINERS_MACHINE_PROVIDER: VMTYPE.LIBKRUN,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
test('test checkDefaultMachine - if user wants to change machine, check that it only change the connection once if it is rootless', async () => {
|
||||
|
|
@ -837,13 +856,20 @@ test('test checkDefaultMachine - if user wants to change machine, check that it
|
|||
|
||||
await extension.checkDefaultMachine(fakeMachineJSON);
|
||||
|
||||
expect(spyExecPromise).toHaveBeenCalledWith(podmanCli.getPodmanCli(), [
|
||||
'system',
|
||||
'connection',
|
||||
'default',
|
||||
machineDefaultName,
|
||||
]);
|
||||
expect(inspectCall).toHaveBeenCalledWith(podmanCli.getPodmanCli(), ['machine', 'inspect', machineDefaultName]);
|
||||
expect(spyExecPromise).toHaveBeenCalledWith(
|
||||
podmanCli.getPodmanCli(),
|
||||
['system', 'connection', 'default', machineDefaultName],
|
||||
{
|
||||
env: {
|
||||
CONTAINERS_MACHINE_PROVIDER: VMTYPE.LIBKRUN,
|
||||
},
|
||||
},
|
||||
);
|
||||
expect(inspectCall).toHaveBeenCalledWith(podmanCli.getPodmanCli(), ['machine', 'inspect', machineDefaultName], {
|
||||
env: {
|
||||
CONTAINERS_MACHINE_PROVIDER: VMTYPE.LIBKRUN,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
test('test checkDefaultMachine - if user wants to change machine, check that it only changes to rootless as machine inspect is not returning Rootful field (old versions of podman)', async () => {
|
||||
|
|
@ -873,13 +899,20 @@ test('test checkDefaultMachine - if user wants to change machine, check that it
|
|||
|
||||
await extension.checkDefaultMachine(fakeMachineJSON);
|
||||
|
||||
expect(spyExecPromise).toHaveBeenCalledWith(podmanCli.getPodmanCli(), [
|
||||
'system',
|
||||
'connection',
|
||||
'default',
|
||||
machineDefaultName,
|
||||
]);
|
||||
expect(inspectCall).toHaveBeenCalledWith(podmanCli.getPodmanCli(), ['machine', 'inspect', machineDefaultName]);
|
||||
expect(spyExecPromise).toHaveBeenCalledWith(
|
||||
podmanCli.getPodmanCli(),
|
||||
['system', 'connection', 'default', machineDefaultName],
|
||||
{
|
||||
env: {
|
||||
CONTAINERS_MACHINE_PROVIDER: VMTYPE.LIBKRUN,
|
||||
},
|
||||
},
|
||||
);
|
||||
expect(inspectCall).toHaveBeenCalledWith(podmanCli.getPodmanCli(), ['machine', 'inspect', machineDefaultName], {
|
||||
env: {
|
||||
CONTAINERS_MACHINE_PROVIDER: VMTYPE.LIBKRUN,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
test('test checkDefaultMachine, if the default connection is not in sync with the default machine, the function will prompt', async () => {
|
||||
|
|
@ -909,6 +942,7 @@ test('test checkDefaultMachine, if the default connection is not in sync with th
|
|||
Running: true,
|
||||
Starting: false,
|
||||
Default: true,
|
||||
VMType: VMTYPE.LIBKRUN,
|
||||
},
|
||||
];
|
||||
|
||||
|
|
@ -1170,6 +1204,8 @@ test('ensure showNotification is not called during update', async () => {
|
|||
new Promise<extensionApi.RunResult>((resolve, reject) => {
|
||||
if (args?.[0] === 'machine' && args?.[1] === 'list') {
|
||||
reject(new Error('error'));
|
||||
} else if (args?.[0] === '--version') {
|
||||
resolve({} as extensionApi.RunResult);
|
||||
}
|
||||
}),
|
||||
);
|
||||
|
|
@ -1555,8 +1591,14 @@ describe('registerOnboardingMachineExistsCommand', () => {
|
|||
|
||||
vi.mocked(extensionApi.commands.registerCommand).mockReturnValue({ dispose: vi.fn() });
|
||||
|
||||
// return 2 empty machines
|
||||
vi.mocked(extensionApi.process.exec).mockResolvedValue({ stdout: '[{}, {}]' } as unknown as extensionApi.RunResult);
|
||||
// return an empty object for the first call
|
||||
vi.spyOn(extensionApi.process, 'exec').mockResolvedValueOnce({
|
||||
stdout: 'podman version 5.0.0',
|
||||
} as extensionApi.RunResult);
|
||||
// return 2 empty machines for the second call
|
||||
vi.mocked(extensionApi.process.exec).mockResolvedValueOnce({
|
||||
stdout: '[{}, {}]',
|
||||
} as unknown as extensionApi.RunResult);
|
||||
|
||||
// perform the call
|
||||
const disposable = registerOnboardingMachineExistsCommand();
|
||||
|
|
@ -1693,7 +1735,11 @@ describe('registerOnboardingUnsupportedPodmanMachineCommand', () => {
|
|||
stdout: 'podman version 4.9.3',
|
||||
} as unknown as extensionApi.RunResult);
|
||||
|
||||
// second call to get the machine list
|
||||
vi.mocked(extensionApi.process.exec).mockResolvedValueOnce({
|
||||
stdout: 'podman version 4.9.3',
|
||||
} as unknown as extensionApi.RunResult);
|
||||
|
||||
// third call to get the machine list
|
||||
vi.mocked(extensionApi.process.exec).mockResolvedValueOnce({
|
||||
stdout: '[]',
|
||||
} as unknown as extensionApi.RunResult);
|
||||
|
|
@ -1811,6 +1857,10 @@ describe('registerOnboardingRemoveUnsupportedMachinesCommand', () => {
|
|||
|
||||
vi.mocked(extensionApi.commands.registerCommand).mockReturnValue({ dispose: vi.fn() });
|
||||
|
||||
vi.mocked(extensionApi.process.exec).mockResolvedValueOnce({
|
||||
stdout: 'podman version 5.0.0',
|
||||
} as unknown as extensionApi.RunResult);
|
||||
|
||||
vi.mocked(extensionApi.process.exec).mockResolvedValueOnce({
|
||||
stdout: 'podman version 5.0.0',
|
||||
} as unknown as extensionApi.RunResult);
|
||||
|
|
@ -1869,6 +1919,11 @@ describe('registerOnboardingRemoveUnsupportedMachinesCommand', () => {
|
|||
vi.mocked(extensionApi.process.exec).mockResolvedValueOnce({
|
||||
stdout: 'podman version 5.0.0',
|
||||
} as unknown as extensionApi.RunResult);
|
||||
|
||||
vi.mocked(extensionApi.process.exec).mockResolvedValueOnce({
|
||||
stdout: 'podman version 5.0.0',
|
||||
} as unknown as extensionApi.RunResult);
|
||||
|
||||
// two times false (no qemu folders)
|
||||
vi.mocked(fs.existsSync).mockReturnValueOnce(false);
|
||||
vi.mocked(fs.existsSync).mockReturnValueOnce(false);
|
||||
|
|
|
|||
|
|
@ -38,7 +38,19 @@ import { PodmanInfoHelper } from './podman-info-helper';
|
|||
import { PodmanInstall } from './podman-install';
|
||||
import { QemuHelper } from './qemu-helper';
|
||||
import { RegistrySetup } from './registry-setup';
|
||||
import { appConfigDir, appHomeDir, getAssetsFolder, isLinux, isMac, isWindows, LoggerDelegator } from './util';
|
||||
import {
|
||||
appConfigDir,
|
||||
appHomeDir,
|
||||
execPodman,
|
||||
getAssetsFolder,
|
||||
getProviderByLabel,
|
||||
getProviderLabel,
|
||||
isLinux,
|
||||
isMac,
|
||||
isWindows,
|
||||
LoggerDelegator,
|
||||
VMTYPE,
|
||||
} from './util';
|
||||
import { getDisguisedPodmanInformation, getSocketPath, isDisguisedPodman } from './warnings';
|
||||
import { WslHelper } from './wsl-helper';
|
||||
|
||||
|
|
@ -99,6 +111,7 @@ export type MachineJSON = {
|
|||
Running: boolean;
|
||||
Starting: boolean;
|
||||
Default: boolean;
|
||||
VMType: string;
|
||||
UserModeNetworking?: boolean;
|
||||
};
|
||||
|
||||
|
|
@ -119,6 +132,7 @@ export type MachineInfo = {
|
|||
cpuUsage: number;
|
||||
diskUsage: number;
|
||||
memoryUsage: number;
|
||||
vmType: string;
|
||||
};
|
||||
|
||||
export type MachineListOutput = {
|
||||
|
|
@ -126,6 +140,11 @@ export type MachineListOutput = {
|
|||
stderr: string;
|
||||
};
|
||||
|
||||
export type MachineJSONListOutput = {
|
||||
list: MachineJSON[];
|
||||
error: string;
|
||||
};
|
||||
|
||||
export function isIncompatibleMachineOutput(output: string | undefined): boolean {
|
||||
// apple HV v4 to v5 machine config error
|
||||
const APPLE_HV_V4_V5_ERROR = 'incompatible machine config';
|
||||
|
|
@ -152,7 +171,7 @@ export async function updateMachines(
|
|||
podmanConfiguration: PodmanConfiguration,
|
||||
): Promise<void> {
|
||||
// init machines available
|
||||
let machineListOutput: MachineListOutput;
|
||||
let machineListOutput: MachineJSONListOutput;
|
||||
try {
|
||||
machineListOutput = await getJSONMachineList();
|
||||
} catch (error) {
|
||||
|
|
@ -175,7 +194,7 @@ export async function updateMachines(
|
|||
}
|
||||
|
||||
// parse output
|
||||
const machines = JSON.parse(machineListOutput.stdout) as MachineJSON[];
|
||||
const machines = machineListOutput.list;
|
||||
extensionApi.context.setValue('podmanMachineExists', machines.length > 0, 'onboarding');
|
||||
const installedPodman = await getPodmanInstallation();
|
||||
let shouldCleanMachine = false;
|
||||
|
|
@ -184,7 +203,7 @@ export async function updateMachines(
|
|||
}
|
||||
// check if the machine needs to be cleaned for v4 --> v5 format
|
||||
if (!shouldCleanMachine) {
|
||||
shouldCleanMachine = isIncompatibleMachineOutput(machineListOutput.stderr);
|
||||
shouldCleanMachine = isIncompatibleMachineOutput(machineListOutput.error);
|
||||
}
|
||||
|
||||
// invalid machines is not making the provider working properly so always notify
|
||||
|
|
@ -255,6 +274,7 @@ export async function updateMachines(
|
|||
machineInfo?.memory !== undefined && machineInfo?.memoryUsed !== undefined && machineInfo?.memoryUsed > 0
|
||||
? (machineInfo?.memoryUsed * 100) / machineInfo?.memory
|
||||
: 0,
|
||||
vmType: getProviderLabel(machine.VMType),
|
||||
});
|
||||
|
||||
if (!podmanMachinesStatuses.has(machine.Name)) {
|
||||
|
|
@ -297,13 +317,11 @@ export async function updateMachines(
|
|||
]);
|
||||
socketPath = socket;
|
||||
} else {
|
||||
const { stdout: socket } = await extensionApi.process.exec(getPodmanCli(), [
|
||||
'machine',
|
||||
'inspect',
|
||||
'--format',
|
||||
'{{.ConnectionInfo.PodmanSocket.Path}}',
|
||||
machineName,
|
||||
]);
|
||||
const podmanMachineInfo = podmanMachinesInfo.get(machineName);
|
||||
const { stdout: socket } = await execPodman(
|
||||
['machine', 'inspect', '--format', '{{.ConnectionInfo.PodmanSocket.Path}}', machineName],
|
||||
podmanMachineInfo?.vmType,
|
||||
);
|
||||
socketPath = socket;
|
||||
}
|
||||
} catch (error) {
|
||||
|
|
@ -389,7 +407,7 @@ export async function checkDefaultMachine(machines: MachineJSON[]): Promise<void
|
|||
// check if connection is in sync with machine. If the default connection is rootless but the machine is rootful ask the user to update the connection
|
||||
if (defaultConnectionNotify && !!runningMachine?.Default) {
|
||||
const defaultConnection = await getDefaultConnection();
|
||||
const isRootful = await isRootfulMachine(runningMachine.Name);
|
||||
const isRootful = await isRootfulMachine(runningMachine);
|
||||
if (!defaultConnection?.Name.endsWith(ROOTFUL_SUFFIX) && isRootful) {
|
||||
const result = await extensionApi.window.showInformationMessage(
|
||||
`${isRootful ? 'Rootful' : 'Rootless'} Podman Machine '${runningMachine.Name}' does not match default connection. This will cause podman CLI errors while trying to connect to '${runningMachine.Name}'. Do you want to update the default connection?`,
|
||||
|
|
@ -401,7 +419,7 @@ export async function checkDefaultMachine(machines: MachineJSON[]): Promise<void
|
|||
try {
|
||||
const connectionName = isRootful ? `${runningMachine.Name}${ROOTFUL_SUFFIX}` : runningMachine.Name;
|
||||
// make it the default to run the info command
|
||||
await extensionApi.process.exec(getPodmanCli(), ['system', 'connection', 'default', connectionName]);
|
||||
await execPodman(['system', 'connection', 'default', connectionName], runningMachine.VMType);
|
||||
} catch (error) {
|
||||
// eslint-disable-next-line quotes
|
||||
console.error("Error running 'podman system connection default': ", error);
|
||||
|
|
@ -433,12 +451,12 @@ export async function checkDefaultMachine(machines: MachineJSON[]): Promise<void
|
|||
);
|
||||
if (result === 'Yes') {
|
||||
// check if machine is rootless or rootful
|
||||
const machineIsRootful = await isRootfulMachine(runningMachine.Name);
|
||||
const machineIsRootful = await isRootfulMachine(runningMachine);
|
||||
|
||||
try {
|
||||
const connectionName = machineIsRootful ? `${runningMachine.Name}${ROOTFUL_SUFFIX}` : runningMachine.Name;
|
||||
// make it the default to run the info command
|
||||
await extensionApi.process.exec(getPodmanCli(), ['system', 'connection', 'default', connectionName]);
|
||||
await execPodman(['system', 'connection', 'default', connectionName], runningMachine.VMType);
|
||||
} catch (error) {
|
||||
// eslint-disable-next-line quotes
|
||||
console.error("Error running 'podman system connection default': ", error);
|
||||
|
|
@ -458,17 +476,16 @@ export async function checkDefaultMachine(machines: MachineJSON[]): Promise<void
|
|||
}
|
||||
}
|
||||
|
||||
async function isRootfulMachine(machineName: string): Promise<boolean> {
|
||||
async function isRootfulMachine(machineJSON: MachineJSON): Promise<boolean> {
|
||||
let isRootful = false;
|
||||
try {
|
||||
const { stdout: machineInspectJson } = await extensionApi.process.exec(getPodmanCli(), [
|
||||
'machine',
|
||||
'inspect',
|
||||
machineName,
|
||||
]);
|
||||
const { stdout: machineInspectJson } = await execPodman(
|
||||
['machine', 'inspect', machineJSON.Name],
|
||||
machineJSON.VMType,
|
||||
);
|
||||
const machinesInspect = JSON.parse(machineInspectJson);
|
||||
// find the machine name in the array
|
||||
const machineInspect = machinesInspect.find((machine: { Name: string }) => machine.Name === machineName);
|
||||
const machineInspect = machinesInspect.find((machine: { Name: string }) => machine.Name === machineJSON.Name);
|
||||
isRootful = machineInspect?.Rootful ?? false;
|
||||
} catch (error) {
|
||||
console.error('Error when checking rootful machine: ', error);
|
||||
|
|
@ -728,13 +745,15 @@ export async function registerProviderFor(
|
|||
await startMachine(provider, podmanConfiguration, machineInfo, context, logger, undefined, false);
|
||||
},
|
||||
stop: async (context, logger): Promise<void> => {
|
||||
await extensionApi.process.exec(getPodmanCli(), ['machine', 'stop', machineInfo.name], {
|
||||
await execPodman(['machine', 'stop', machineInfo.name], machineInfo.vmType, {
|
||||
logger: new LoggerDelegator(context, logger),
|
||||
});
|
||||
provider.updateStatus('stopped');
|
||||
},
|
||||
delete: async (logger): Promise<void> => {
|
||||
await extensionApi.process.exec(getPodmanCli(), ['machine', 'rm', '-f', machineInfo.name], { logger });
|
||||
await execPodman(['machine', 'rm', '-f', machineInfo.name], machineInfo.vmType, {
|
||||
logger,
|
||||
});
|
||||
},
|
||||
};
|
||||
//support edit only on MacOS as Podman WSL is nop and generates errors
|
||||
|
|
@ -760,7 +779,7 @@ export async function registerProviderFor(
|
|||
if (state === 'started') {
|
||||
await lifecycle.stop?.(context, logger);
|
||||
}
|
||||
await extensionApi.process.exec(getPodmanCli(), args, {
|
||||
await execPodman(args, machineInfo.vmType, {
|
||||
logger: new LoggerDelegator(context, logger),
|
||||
});
|
||||
} finally {
|
||||
|
|
@ -780,6 +799,7 @@ export async function registerProviderFor(
|
|||
endpoint: {
|
||||
socketPath,
|
||||
},
|
||||
vmType: getProviderLabel(machineInfo.vmType),
|
||||
};
|
||||
|
||||
// Since Podman 4.5, machines are using the same path for all sockets of machines
|
||||
|
|
@ -854,7 +874,7 @@ export async function startMachine(
|
|||
|
||||
try {
|
||||
// start the machine
|
||||
await extensionApi.process.exec(getPodmanCli(), ['machine', 'start', machineInfo.name], {
|
||||
await execPodman(['machine', 'start', machineInfo.name], machineInfo.vmType, {
|
||||
logger: new LoggerDelegator(context, logger),
|
||||
});
|
||||
provider.updateStatus('started');
|
||||
|
|
@ -967,6 +987,7 @@ export const CLEANUP_REQUIRED_MACHINE_KEY = 'podman.needPodmanMachineCleanup';
|
|||
export const PODMAN_MACHINE_CPU_SUPPORTED_KEY = 'podman.podmanMachineCpuSupported';
|
||||
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 function initTelemetryLogger(): void {
|
||||
telemetryLogger = extensionApi.env.createTelemetryLogger();
|
||||
|
|
@ -1026,8 +1047,7 @@ export function registerOnboardingMachineExistsCommand(): extensionApi.Disposabl
|
|||
let machineLength;
|
||||
try {
|
||||
const machineListOutput = await getJSONMachineList();
|
||||
const machines = JSON.parse(machineListOutput.stdout) as MachineJSON[];
|
||||
machineLength = machines.length;
|
||||
machineLength = machineListOutput.list.length;
|
||||
} catch (error) {
|
||||
machineLength = 0;
|
||||
}
|
||||
|
|
@ -1047,7 +1067,7 @@ export function registerOnboardingUnsupportedPodmanMachineCommand(): extensionAp
|
|||
if (!isUnsupported) {
|
||||
try {
|
||||
const machineListOutput = await getJSONMachineList();
|
||||
isUnsupported = isIncompatibleMachineOutput(machineListOutput.stderr);
|
||||
isUnsupported = isIncompatibleMachineOutput(machineListOutput.error);
|
||||
} catch (error) {
|
||||
// check if stderr in the error object
|
||||
const runError = error as RunError;
|
||||
|
|
@ -1098,7 +1118,7 @@ export function registerOnboardingRemoveUnsupportedMachinesCommand(): extensionA
|
|||
let machineListError = '';
|
||||
try {
|
||||
const machineListOutput = await getJSONMachineList();
|
||||
machineListError = machineListOutput.stderr;
|
||||
machineListError = machineListOutput.error;
|
||||
} catch (error) {
|
||||
machineListError = (error as RunError).stderr;
|
||||
}
|
||||
|
|
@ -1117,10 +1137,11 @@ export function registerOnboardingRemoveUnsupportedMachinesCommand(): extensionA
|
|||
const files = await fs.promises.readdir(machineFolderToCheck);
|
||||
const machineFilesToAnalyze = files.filter(file => file.endsWith('.json'));
|
||||
let machineConfigJson: { GvProxy?: string } = {};
|
||||
const machineFolderToCheckValue = machineFolderToCheck;
|
||||
const allMachines = await Promise.all(
|
||||
machineFilesToAnalyze.map(async file => {
|
||||
// read content of the file
|
||||
const absoluteFile = path.join(machineFolderToCheck, file);
|
||||
const absoluteFile = path.join(machineFolderToCheckValue, file);
|
||||
try {
|
||||
const machineConfigJsonRaw = await fs.promises.readFile(absoluteFile, 'utf-8');
|
||||
machineConfigJson = JSON.parse(machineConfigJsonRaw);
|
||||
|
|
@ -1215,6 +1236,7 @@ export async function activate(extensionContext: extensionApi.ExtensionContext):
|
|||
extensionApi.context.setValue(ROOTFUL_MACHINE_INIT_SUPPORTED_KEY, isRootfulMachineInitSupported(version));
|
||||
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));
|
||||
isMovedPodmanSocket = isPodmanSocketLocationMoved(version);
|
||||
}
|
||||
|
||||
|
|
@ -1635,7 +1657,7 @@ export async function findRunningMachine(): Promise<string | undefined> {
|
|||
|
||||
// Find the machines
|
||||
const machineListOutput = await getJSONMachineList();
|
||||
const machines = JSON.parse(machineListOutput.stdout) as MachineJSON[];
|
||||
const machines = machineListOutput.list;
|
||||
|
||||
// Find the machine that is running
|
||||
const found: MachineJSON | undefined = machines.find(machine => machine?.Running);
|
||||
|
|
@ -1654,7 +1676,7 @@ async function stopAutoStartedMachine(): Promise<void> {
|
|||
}
|
||||
const machineListOutput = await getJSONMachineList();
|
||||
|
||||
const machines = JSON.parse(machineListOutput.stdout) as MachineJSON[];
|
||||
const machines = machineListOutput.list;
|
||||
|
||||
// Find the autostarted machine and check its status
|
||||
const currentMachine: MachineJSON | undefined = machines.find(machine => machine?.Name === autoMachineName);
|
||||
|
|
@ -1665,11 +1687,34 @@ async function stopAutoStartedMachine(): Promise<void> {
|
|||
return;
|
||||
}
|
||||
console.log('stopping autostarted machine', autoMachineName);
|
||||
await extensionApi.process.exec(getPodmanCli(), ['machine', 'stop', autoMachineName]);
|
||||
await execPodman(['machine', 'stop', autoMachineName], currentMachine.VMType);
|
||||
}
|
||||
|
||||
export async function getJSONMachineList(): Promise<MachineListOutput> {
|
||||
const { stdout, stderr } = await extensionApi.process.exec(getPodmanCli(), ['machine', 'list', '--format', 'json']);
|
||||
export async function getJSONMachineList(): Promise<MachineJSONListOutput> {
|
||||
const installedPodman = await getPodmanInstallation();
|
||||
|
||||
const containerMachineProviders: (string | undefined)[] = [];
|
||||
// if libkrun is supported we want to show both applehv and libkrun machines
|
||||
if (installedPodman && isLibkrunSupported(installedPodman.version)) {
|
||||
containerMachineProviders.push(...['applehv', 'libkrun']);
|
||||
} else {
|
||||
// in all other cases we set undefined so that it executes normally by using the default container provider
|
||||
containerMachineProviders.push(undefined);
|
||||
}
|
||||
|
||||
const list: MachineJSON[] = [];
|
||||
let error = '';
|
||||
for (const provider of containerMachineProviders) {
|
||||
const machineListOutput = await getJSONMachineListByProvider(provider);
|
||||
list.push(...(JSON.parse(machineListOutput.stdout) as MachineJSON[]));
|
||||
error += machineListOutput.stderr + '\n';
|
||||
}
|
||||
|
||||
return { list, error };
|
||||
}
|
||||
|
||||
export async function getJSONMachineListByProvider(containerMachineProvider?: string): Promise<MachineListOutput> {
|
||||
const { stdout, stderr } = await execPodman(['machine', 'list', '--format', 'json'], containerMachineProvider);
|
||||
return { stdout, stderr };
|
||||
}
|
||||
|
||||
|
|
@ -1710,6 +1755,13 @@ export function isUserModeNetworkingSupported(podmanVersion: string): boolean {
|
|||
return isWindows() && compareVersions(podmanVersion, PODMAN_MINIMUM_VERSION_FOR_USER_MODE_NETWORKING) >= 0;
|
||||
}
|
||||
|
||||
const PODMAN_MINIMUM_VERSION_FOR_LIBKRUN_SUPPORT = '5.2.0';
|
||||
|
||||
// Checks if libkrun is supported. Only Mac platform allows this parameter to be tuned
|
||||
export function isLibkrunSupported(podmanVersion: string): boolean {
|
||||
return isMac() && compareVersions(podmanVersion, PODMAN_MINIMUM_VERSION_FOR_LIBKRUN_SUPPORT) >= 0;
|
||||
}
|
||||
|
||||
function sendTelemetryRecords(
|
||||
eventName: string,
|
||||
telemetryRecords: Record<string, unknown>,
|
||||
|
|
@ -1799,11 +1851,21 @@ export async function createMachine(
|
|||
|
||||
const telemetryRecords: Record<string, unknown> = {};
|
||||
|
||||
let provider: string | undefined;
|
||||
if (params['podman.factory.machine.provider']) {
|
||||
provider = getProviderByLabel(params['podman.factory.machine.provider']);
|
||||
}
|
||||
|
||||
// cpus
|
||||
if (params['podman.factory.machine.cpus']) {
|
||||
let cpusValue = params['podman.factory.machine.cpus'];
|
||||
// libkrun has an issue that prevent to start a machine that has been created with more than 8 cpus, so we limit it here
|
||||
if (provider === VMTYPE.LIBKRUN && parseInt(cpusValue) > 8) {
|
||||
cpusValue = '8';
|
||||
}
|
||||
parameters.push('--cpus');
|
||||
parameters.push(params['podman.factory.machine.cpus']);
|
||||
telemetryRecords.cpus = params['podman.factory.machine.cpus'];
|
||||
parameters.push(cpusValue);
|
||||
telemetryRecords.cpus = cpusValue;
|
||||
}
|
||||
|
||||
// memory
|
||||
|
|
@ -1896,7 +1958,10 @@ export async function createMachine(
|
|||
|
||||
const startTime = performance.now();
|
||||
try {
|
||||
await extensionApi.process.exec(getPodmanCli(), parameters, { logger, token });
|
||||
await execPodman(parameters, provider, {
|
||||
logger,
|
||||
token,
|
||||
});
|
||||
} catch (error) {
|
||||
telemetryRecords.error = error;
|
||||
const runError = error as RunError;
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ import type { InstalledPodman } from './podman-cli';
|
|||
import type { Installer, UpdateCheck } from './podman-install';
|
||||
import { getBundledPodmanVersion, PodmanInstall, WinInstaller } from './podman-install';
|
||||
import * as podmanInstallObj from './podman-install';
|
||||
import * as utils from './util';
|
||||
|
||||
const originalConsoleError = console.error;
|
||||
const consoleErrorMock = vi.fn();
|
||||
|
|
@ -85,6 +86,7 @@ vi.mock('./util', async () => {
|
|||
isLinux: vi.fn(),
|
||||
isWindows: vi.fn(),
|
||||
isMac: vi.fn(),
|
||||
execPodman: vi.fn(),
|
||||
};
|
||||
});
|
||||
|
||||
|
|
@ -729,9 +731,13 @@ describe('update checks', () => {
|
|||
test('stopPodmanMachinesIfAnyBeforeUpdating with one machine running', async () => {
|
||||
const podmanInstall = new TestPodmanInstall(extensionContext);
|
||||
|
||||
vi.spyOn(extensionApi.process, 'exec').mockResolvedValueOnce({
|
||||
stdout: 'podman version 5.0.0',
|
||||
} as extensionApi.RunResult);
|
||||
|
||||
// return empty machine list
|
||||
vi.mocked(extensionApi.process.exec).mockResolvedValueOnce({
|
||||
stdout: JSON.stringify([{ Name: 'test', Running: true }]),
|
||||
vi.spyOn(utils, 'execPodman').mockResolvedValueOnce({
|
||||
stdout: JSON.stringify([{ Name: 'test', Running: true, VMType: 'libkrun' }]),
|
||||
} as unknown as extensionApi.RunResult);
|
||||
|
||||
// mock user response
|
||||
|
|
|
|||
|
|
@ -28,9 +28,11 @@ import { getDetectionChecks } from './detection-checks';
|
|||
import type { MachineJSON } from './extension';
|
||||
import {
|
||||
getJSONMachineList,
|
||||
isLibkrunSupported,
|
||||
isRootfulMachineInitSupported,
|
||||
isStartNowAtMachineInitSupported,
|
||||
isUserModeNetworkingSupported,
|
||||
PODMAN_PROVIDER_LIBKRUN_SUPPORTED_KEY,
|
||||
ROOTFUL_MACHINE_INIT_SUPPORTED_KEY,
|
||||
START_NOW_MACHINE_INIT_SUPPORTED_KEY,
|
||||
USER_MODE_NETWORKING_SUPPORTED_KEY,
|
||||
|
|
@ -174,6 +176,10 @@ export class PodmanInstall {
|
|||
START_NOW_MACHINE_INIT_SUPPORTED_KEY,
|
||||
isStartNowAtMachineInitSupported(newInstalledPodman.version),
|
||||
);
|
||||
extensionApi.context.setValue(
|
||||
PODMAN_PROVIDER_LIBKRUN_SUPPORTED_KEY,
|
||||
isLibkrunSupported(newInstalledPodman.version),
|
||||
);
|
||||
}
|
||||
// update detections checks
|
||||
provider.updateDetectionChecks(getDetectionChecks(newInstalledPodman));
|
||||
|
|
@ -209,9 +215,7 @@ export class PodmanInstall {
|
|||
const machinesRunning: MachineJSON[] = [];
|
||||
try {
|
||||
const machineListOutput = await getJSONMachineList();
|
||||
const machines = JSON.parse(machineListOutput.stdout) as MachineJSON[];
|
||||
|
||||
machinesRunning.push(...machines.filter(machine => machine.Running || machine.Starting));
|
||||
machinesRunning.push(...machineListOutput.list.filter(machine => machine.Running || machine.Starting));
|
||||
} catch (error) {
|
||||
console.debug('Unable to query machines before updating', error);
|
||||
}
|
||||
|
|
@ -356,6 +360,10 @@ export class PodmanInstall {
|
|||
START_NOW_MACHINE_INIT_SUPPORTED_KEY,
|
||||
isStartNowAtMachineInitSupported(updateInfo.bundledVersion),
|
||||
);
|
||||
extensionApi.context.setValue(
|
||||
PODMAN_PROVIDER_LIBKRUN_SUPPORTED_KEY,
|
||||
isLibkrunSupported(updateInfo.bundledVersion),
|
||||
);
|
||||
}
|
||||
} else if (answer === 'Ignore') {
|
||||
this.podmanInfo.ignoreVersionUpdate = updateInfo.bundledVersion;
|
||||
|
|
|
|||
|
|
@ -16,9 +16,43 @@
|
|||
* SPDX-License-Identifier: Apache-2.0
|
||||
***********************************************************************/
|
||||
|
||||
import { expect, test } from 'vitest';
|
||||
import * as extensionApi from '@podman-desktop/api';
|
||||
import { afterEach, expect, test, vi } from 'vitest';
|
||||
|
||||
import { normalizeWSLOutput } from './util';
|
||||
import { getPodmanCli } from './podman-cli';
|
||||
import {
|
||||
APPLEHV_LABEL,
|
||||
execPodman,
|
||||
getProviderByLabel,
|
||||
getProviderLabel,
|
||||
LIBKRUN_LABEL,
|
||||
normalizeWSLOutput,
|
||||
VMTYPE,
|
||||
} from './util';
|
||||
|
||||
const config: extensionApi.Configuration = {
|
||||
get: () => {
|
||||
// not implemented
|
||||
},
|
||||
has: () => true,
|
||||
update: vi.fn(),
|
||||
};
|
||||
|
||||
vi.mock('@podman-desktop/api', () => {
|
||||
return {
|
||||
configuration: {
|
||||
getConfiguration: (): extensionApi.Configuration => config,
|
||||
},
|
||||
process: {
|
||||
exec: vi.fn(),
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
vi.resetAllMocks();
|
||||
vi.restoreAllMocks();
|
||||
});
|
||||
|
||||
test('normalizeWSLOutput returns the same string if there is no need to normalize it', async () => {
|
||||
const text = 'blabla';
|
||||
|
|
@ -46,3 +80,76 @@ function strEncodeUTF16(str: string): Uint16Array {
|
|||
}
|
||||
return bufView;
|
||||
}
|
||||
|
||||
test('expect exec called with CONTAINERS_MACHINE_PROVIDER if a provider is defined', async () => {
|
||||
const execMock = vi.spyOn(extensionApi.process, 'exec').mockImplementation(
|
||||
() =>
|
||||
new Promise<extensionApi.RunResult>(resolve => {
|
||||
resolve({} as extensionApi.RunResult);
|
||||
}),
|
||||
);
|
||||
|
||||
await execPodman(['machine', 'inspect'], 'libkrun', {
|
||||
env: {
|
||||
label: 'one',
|
||||
},
|
||||
});
|
||||
|
||||
expect(execMock).toBeCalledWith(getPodmanCli(), ['machine', 'inspect'], {
|
||||
env: {
|
||||
label: 'one',
|
||||
CONTAINERS_MACHINE_PROVIDER: 'libkrun',
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
test('expect exec called without CONTAINERS_MACHINE_PROVIDER if a provider is NOT defined', async () => {
|
||||
const execMock = vi.spyOn(extensionApi.process, 'exec').mockImplementation(
|
||||
() =>
|
||||
new Promise<extensionApi.RunResult>(resolve => {
|
||||
resolve({} as extensionApi.RunResult);
|
||||
}),
|
||||
);
|
||||
|
||||
await execPodman(['machine', 'inspect'], undefined, {
|
||||
env: {
|
||||
label: 'one',
|
||||
},
|
||||
});
|
||||
|
||||
expect(execMock).toBeCalledWith(getPodmanCli(), ['machine', 'inspect'], {
|
||||
env: {
|
||||
label: 'one',
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
test('expect libkrun label with libkrun provider', async () => {
|
||||
const label = getProviderLabel(VMTYPE.LIBKRUN);
|
||||
expect(label).equals(LIBKRUN_LABEL);
|
||||
});
|
||||
|
||||
test('expect applehv label with applehv provider', async () => {
|
||||
const label = getProviderLabel(VMTYPE.APPLEHV);
|
||||
expect(label).equals(APPLEHV_LABEL);
|
||||
});
|
||||
|
||||
test('expect provider name with provider different from libkrun and applehv', async () => {
|
||||
const label = getProviderLabel(VMTYPE.WSL);
|
||||
expect(label).equals(VMTYPE.WSL);
|
||||
});
|
||||
|
||||
test('expect libkrun provider with libkrun label', async () => {
|
||||
const provider = getProviderByLabel(LIBKRUN_LABEL);
|
||||
expect(provider).equals(VMTYPE.LIBKRUN);
|
||||
});
|
||||
|
||||
test('expect applehv provider with applehv label', async () => {
|
||||
const provider = getProviderByLabel(APPLEHV_LABEL);
|
||||
expect(provider).equals(VMTYPE.APPLEHV);
|
||||
});
|
||||
|
||||
test('expect provider name with provider different from libkrun and applehv', async () => {
|
||||
const provider = getProviderByLabel(VMTYPE.WSL);
|
||||
expect(provider).equals(VMTYPE.WSL);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -19,7 +19,9 @@
|
|||
import * as os from 'node:os';
|
||||
import * as path from 'node:path';
|
||||
|
||||
import type { LifecycleContext, Logger } from '@podman-desktop/api';
|
||||
import * as extensionApi from '@podman-desktop/api';
|
||||
|
||||
import { getPodmanCli } from './podman-cli';
|
||||
|
||||
const windows = os.platform() === 'win32';
|
||||
export function isWindows(): boolean {
|
||||
|
|
@ -83,10 +85,10 @@ export function getAssetsFolder(): string {
|
|||
* field can be used directly, simplifying the process of passing the logger to the process API while preserving
|
||||
* the necessary functionalities.
|
||||
*/
|
||||
export class LoggerDelegator implements Logger {
|
||||
private loggers: Logger[] = [];
|
||||
export class LoggerDelegator implements extensionApi.Logger {
|
||||
private loggers: extensionApi.Logger[] = [];
|
||||
|
||||
constructor(...loggersOrContexts: (Logger | LifecycleContext | undefined)[]) {
|
||||
constructor(...loggersOrContexts: (extensionApi.Logger | extensionApi.LifecycleContext | undefined)[]) {
|
||||
loggersOrContexts.forEach(loggerOrContext => {
|
||||
if (loggerOrContext === undefined) {
|
||||
return;
|
||||
|
|
@ -94,7 +96,7 @@ export class LoggerDelegator implements Logger {
|
|||
if (typeof loggerOrContext.log === 'object') {
|
||||
this.loggers.push(loggerOrContext.log);
|
||||
} else if (typeof loggerOrContext.log === 'function') {
|
||||
this.loggers.push(loggerOrContext as Logger);
|
||||
this.loggers.push(loggerOrContext as extensionApi.Logger);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
@ -132,3 +134,52 @@ export function normalizeWSLOutput(out: string): string {
|
|||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
export function execPodman(
|
||||
args: string[],
|
||||
containersProvider?: string,
|
||||
options?: extensionApi.RunOptions,
|
||||
): Promise<extensionApi.RunResult> {
|
||||
const finalOptions: extensionApi.RunOptions = { ...options };
|
||||
|
||||
if (containersProvider) {
|
||||
finalOptions.env = {
|
||||
...(finalOptions.env ?? {}),
|
||||
CONTAINERS_MACHINE_PROVIDER: getProviderByLabel(containersProvider),
|
||||
};
|
||||
}
|
||||
|
||||
return extensionApi.process.exec(getPodmanCli(), args, finalOptions);
|
||||
}
|
||||
|
||||
export enum VMTYPE {
|
||||
WSL = 'wsl',
|
||||
HYPERV = 'hyperv',
|
||||
APPLEHV = 'applehv',
|
||||
LIBKRUN = 'libkrun',
|
||||
}
|
||||
|
||||
export const APPLEHV_LABEL = 'default (Apple HyperVisor)';
|
||||
export const LIBKRUN_LABEL = 'GPU enabled (LibKrun)';
|
||||
|
||||
export function getProviderLabel(provider: string): string {
|
||||
switch (provider) {
|
||||
case VMTYPE.LIBKRUN:
|
||||
return LIBKRUN_LABEL;
|
||||
case VMTYPE.APPLEHV:
|
||||
return APPLEHV_LABEL;
|
||||
default:
|
||||
return provider;
|
||||
}
|
||||
}
|
||||
|
||||
export function getProviderByLabel(label: string): string {
|
||||
switch (label) {
|
||||
case LIBKRUN_LABEL:
|
||||
return VMTYPE.LIBKRUN;
|
||||
case APPLEHV_LABEL:
|
||||
return VMTYPE.APPLEHV;
|
||||
default:
|
||||
return label;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -37,6 +37,7 @@ export interface ProviderContainerConnectionInfo {
|
|||
};
|
||||
lifecycleMethods?: LifecycleMethod[];
|
||||
type: 'docker' | 'podman';
|
||||
vmType?: string;
|
||||
}
|
||||
|
||||
export interface ProviderKubernetesConnectionInfo {
|
||||
|
|
|
|||
|
|
@ -362,6 +362,7 @@ declare module '@podman-desktop/api' {
|
|||
endpoint: ContainerProviderConnectionEndpoint;
|
||||
lifecycle?: ProviderConnectionLifecycle;
|
||||
status(): ProviderConnectionStatus;
|
||||
vmType?: string;
|
||||
}
|
||||
|
||||
export interface PodCreatePortOptions {
|
||||
|
|
|
|||
|
|
@ -297,6 +297,7 @@ test('should send events when starting a container connection', async () => {
|
|||
socketPath: '/endpoint1.sock',
|
||||
},
|
||||
status: 'started',
|
||||
vmType: 'libkrun',
|
||||
};
|
||||
|
||||
const startMock = vi.fn();
|
||||
|
|
@ -314,6 +315,7 @@ test('should send events when starting a container connection', async () => {
|
|||
status() {
|
||||
return 'started';
|
||||
},
|
||||
vmType: 'libkrun',
|
||||
});
|
||||
|
||||
let onBeforeDidUpdateContainerConnectionCalled = false;
|
||||
|
|
@ -360,6 +362,7 @@ test('should send events when stopping a container connection', async () => {
|
|||
socketPath: '/endpoint1.sock',
|
||||
},
|
||||
status: 'stopped',
|
||||
vmType: 'libkrun',
|
||||
};
|
||||
|
||||
const startMock = vi.fn();
|
||||
|
|
@ -377,6 +380,7 @@ test('should send events when stopping a container connection', async () => {
|
|||
status() {
|
||||
return 'stopped';
|
||||
},
|
||||
vmType: 'libkrun',
|
||||
});
|
||||
|
||||
let onBeforeDidUpdateContainerConnectionCalled = false;
|
||||
|
|
|
|||
|
|
@ -639,6 +639,7 @@ export class ProviderRegistry {
|
|||
endpoint: {
|
||||
socketPath: connection.endpoint.socketPath,
|
||||
},
|
||||
vmType: connection.vmType,
|
||||
};
|
||||
} else {
|
||||
providerConnection = {
|
||||
|
|
|
|||
|
|
@ -52,6 +52,7 @@ const providerInfo: ProviderInfo = {
|
|||
},
|
||||
lifecycleMethods: ['start', 'stop', 'delete'],
|
||||
type: 'podman',
|
||||
vmType: 'libkrun',
|
||||
},
|
||||
],
|
||||
installationSupport: false,
|
||||
|
|
@ -167,6 +168,8 @@ test('Expect type to be reported for Podman engines', async () => {
|
|||
expect(typeDiv.textContent).toBe('Podman endpoint');
|
||||
const endpointSpan = await vi.waitFor(() => screen.getByTitle('unix://socket'));
|
||||
expect(endpointSpan.textContent).toBe('unix://socket');
|
||||
const connectionType = screen.getByLabelText('Connection Type');
|
||||
expect(connectionType.textContent).equal('Libkrun');
|
||||
});
|
||||
|
||||
test('Expect type to be reported for Docker engines', async () => {
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ import { normalizeOnboardingWhenClause } from '../onboarding/onboarding-utils';
|
|||
import ConnectionErrorInfoButton from '../ui/ConnectionErrorInfoButton.svelte';
|
||||
import ConnectionStatus from '../ui/ConnectionStatus.svelte';
|
||||
import EngineIcon from '../ui/EngineIcon.svelte';
|
||||
import { capitalize } from '../ui/Util';
|
||||
import { PeerProperties } from './PeerProperties';
|
||||
import { eventCollect } from './preferences-connection-rendering-task';
|
||||
import PreferencesConnectionActions from './PreferencesConnectionActions.svelte';
|
||||
|
|
@ -530,8 +531,12 @@ function hasAnyConfiguration(provider: ProviderInfo) {
|
|||
connectionStatus={containerConnectionStatus.get(getProviderConnectionName(provider, container))}
|
||||
updateConnectionStatus={updateContainerStatus}
|
||||
addConnectionToRestartingQueue={addConnectionToRestartingQueue} />
|
||||
<div class="mt-1.5 text-gray-900 text-[9px]" aria-label="Connection Version">
|
||||
<div>{provider.name} {provider.version ? `v${provider.version}` : ''}</div>
|
||||
<div class="mt-1.5 text-gray-900 text-[9px] flex justify-between">
|
||||
<div aria-label="Connection Version">
|
||||
{provider.name}
|
||||
{provider.version ? `v${provider.version}` : ''}
|
||||
</div>
|
||||
<div aria-label="Connection Type">{container.vmType ? capitalize(container.vmType) : ''}</div>
|
||||
</div>
|
||||
</div>
|
||||
{/each}
|
||||
|
|
|
|||
Loading…
Reference in a new issue