Skip to content

Commit dbf6e9c

Browse files
authored
feat: make Podman machine editable (podman-desktop#4999)
* feat: make Podman machine editable Fixes podman-desktop#4113 Signed-off-by: Jeff MAURY <[email protected]>
1 parent 5b6a27a commit dbf6e9c

20 files changed

+703
-368
lines changed

extensions/podman/package.json

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -43,41 +43,49 @@
4343
"format": "cpu",
4444
"minimum": 1,
4545
"default": 2,
46+
"maximum": "HOST_TOTAL_CPU",
4647
"scope": "ContainerConnection",
4748
"description": "CPU(s)"
4849
},
4950
"podman.machine.cpusUsage": {
5051
"type": "number",
5152
"format": "cpuUsage",
5253
"scope": "ContainerConnection",
53-
"description": "CPU(s) usage"
54+
"description": "CPU(s) usage",
55+
"readonly": true
5456
},
5557
"podman.machine.memory": {
5658
"type": "number",
5759
"format": "memory",
5860
"minimum": 1000000000,
5961
"default": 4000000000,
62+
"maximum": "HOST_TOTAL_MEMORY",
6063
"scope": "ContainerConnection",
64+
"step": 500000000,
6165
"description": "Memory"
6266
},
6367
"podman.machine.memoryUsage": {
6468
"type": "number",
6569
"format": "memoryUsage",
6670
"scope": "ContainerConnection",
67-
"description": "Memory usage"
71+
"description": "Memory usage",
72+
"readonly": true
6873
},
6974
"podman.machine.diskSize": {
70-
"type": "array",
75+
"type": "number",
7176
"format": "diskSize",
7277
"default": 100000000000,
78+
"maximum": "HOST_TOTAL_DISKSIZE",
7379
"scope": "ContainerConnection",
80+
"step": 500000000,
7481
"description": "Disk size"
7582
},
7683
"podman.machine.diskSizeUsage": {
7784
"type": "number",
7885
"format": "diskSizeUsage",
7986
"scope": "ContainerConnection",
80-
"description": "Disk size usage"
87+
"description": "Disk size usage",
88+
"readonly": true
8189
},
8290
"podman.factory.machine.name": {
8391
"type": "string",

extensions/podman/src/extension.ts

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -606,6 +606,37 @@ async function registerProviderFor(provider: extensionApi.Provider, machineInfo:
606606
delete: async (logger): Promise<void> => {
607607
await extensionApi.process.exec(getPodmanCli(), ['machine', 'rm', '-f', machineInfo.name], { logger });
608608
},
609+
edit: async (context, params, logger, _token): Promise<void> => {
610+
let effective = false;
611+
const args = ['machine', 'set', machineInfo.name];
612+
for (const key of Object.keys(params)) {
613+
if (key === 'podman.machine.cpus') {
614+
args.push('--cpus', params[key]);
615+
effective = true;
616+
} else if (key === 'podman.machine.memory') {
617+
args.push('--memory', Math.floor(params[key] / (1024 * 1024)).toString());
618+
effective = true;
619+
} else if (key === 'podman.machine.diskSize') {
620+
args.push('--disk-size', Math.floor(params[key] / (1024 * 1024 * 1024)).toString());
621+
effective = true;
622+
}
623+
}
624+
if (effective) {
625+
const state = podmanMachinesStatuses.get(machineInfo.name);
626+
try {
627+
if (state === 'started') {
628+
await lifecycle.stop(context, logger);
629+
}
630+
await extensionApi.process.exec(getPodmanCli(), args, {
631+
logger: new LoggerDelegator(context, logger),
632+
});
633+
} finally {
634+
if (state === 'started') {
635+
await lifecycle.start(context, logger);
636+
}
637+
}
638+
}
639+
},
609640
};
610641

611642
const containerProviderConnection: extensionApi.ContainerProviderConnection = {

packages/extension-api/src/extension-api.d.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -233,6 +233,13 @@ declare module '@podman-desktop/api' {
233233
start?(startContext: LifecycleContext, logger?: Logger): Promise<void>;
234234
stop?(stopContext: LifecycleContext, logger?: Logger): Promise<void>;
235235
delete?(logger?: Logger): Promise<void>;
236+
edit?(
237+
editContext: LifecycleContext,
238+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
239+
params: { [key: string]: any },
240+
logger?: Logger,
241+
token?: CancellationToken,
242+
): Promise<void>;
236243
}
237244

238245
export interface ContainerProviderConnectionEndpoint {

packages/main/src/plugin/api/provider-info.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ import type {
2626
ProviderInformation,
2727
} from '@podman-desktop/api';
2828

29-
export type LifecycleMethod = 'start' | 'stop' | 'delete';
29+
export type LifecycleMethod = 'start' | 'stop' | 'delete' | 'edit';
3030

3131
export interface ProviderContainerConnectionInfo {
3232
name: string;

packages/main/src/plugin/index.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1705,6 +1705,28 @@ export class PluginSystem {
17051705
},
17061706
);
17071707

1708+
this.ipcHandle(
1709+
'provider-registry:editProviderConnectionLifecycle',
1710+
async (
1711+
_listener: Electron.IpcMainInvokeEvent,
1712+
providerId: string,
1713+
providerConnectionInfo: ProviderContainerConnectionInfo | ProviderKubernetesConnectionInfo,
1714+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
1715+
params: { [key: string]: any },
1716+
loggerId: string,
1717+
tokenId?: number,
1718+
): Promise<void> => {
1719+
const logger = this.getLogHandler('provider-registry:taskConnection-onData', loggerId);
1720+
let token;
1721+
if (tokenId) {
1722+
const tokenSource = cancellationTokenRegistry.getCancellationTokenSource(tokenId);
1723+
token = tokenSource?.token;
1724+
}
1725+
await providerRegistry.editProviderConnection(providerId, providerConnectionInfo, params, logger, token);
1726+
logger.onEnd();
1727+
},
1728+
);
1729+
17081730
this.ipcHandle(
17091731
'provider-registry:deleteProviderConnectionLifecycle',
17101732
async (

packages/main/src/plugin/provider-registry.ts

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -562,6 +562,9 @@ export class ProviderRegistry {
562562
if (connection.lifecycle.stop) {
563563
lifecycleMethods.push('stop');
564564
}
565+
if (connection.lifecycle.edit) {
566+
lifecycleMethods.push('edit');
567+
}
565568
providerConnection.lifecycleMethods = lifecycleMethods;
566569
}
567570
return providerConnection;
@@ -903,6 +906,40 @@ export class ProviderRegistry {
903906
}
904907
}
905908

909+
async editProviderConnection(
910+
internalProviderId: string,
911+
providerConnectionInfo: ProviderContainerConnectionInfo | ProviderKubernetesConnectionInfo,
912+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
913+
params: { [key: string]: any },
914+
logHandler?: Logger,
915+
token?: CancellationToken,
916+
): Promise<void> {
917+
// grab the correct provider
918+
const connection = this.getMatchingConnectionFromProvider(internalProviderId, providerConnectionInfo);
919+
920+
const lifecycle = connection.lifecycle;
921+
if (!lifecycle?.edit) {
922+
throw new Error('The container connection does not support edit lifecycle');
923+
}
924+
925+
const context = this.connectionLifecycleContexts.get(connection);
926+
if (!context) {
927+
throw new Error('The connection does not have context to edit');
928+
}
929+
930+
const provider = this.providers.get(internalProviderId);
931+
if (!provider) {
932+
throw new Error('Cannot find provider');
933+
}
934+
935+
try {
936+
await lifecycle.edit(context, params, logHandler, token);
937+
} catch (err) {
938+
console.warn(`Can't edit connection ${provider.id}.${providerConnectionInfo.name}`, err);
939+
throw err;
940+
}
941+
}
942+
906943
async stopProviderConnection(
907944
internalProviderId: string,
908945
providerConnectionInfo: ProviderContainerConnectionInfo | ProviderKubernetesConnectionInfo,

packages/preload/src/index.ts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -902,6 +902,31 @@ function initExposure(): void {
902902
},
903903
);
904904

905+
contextBridge.exposeInMainWorld(
906+
'editProviderConnectionLifecycle',
907+
async (
908+
providerId: string,
909+
providerConnectionInfo: ProviderContainerConnectionInfo | ProviderKubernetesConnectionInfo,
910+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
911+
params: { [key: string]: any },
912+
key: symbol,
913+
keyLogger: (key: symbol, eventName: 'log' | 'warn' | 'error' | 'finish', args: string[]) => void,
914+
tokenId?: number,
915+
): Promise<void> => {
916+
onDataCallbacksTaskConnectionId++;
917+
onDataCallbacksTaskConnectionKeys.set(onDataCallbacksTaskConnectionId, key);
918+
onDataCallbacksTaskConnectionLogs.set(onDataCallbacksTaskConnectionId, keyLogger);
919+
return ipcInvoke(
920+
'provider-registry:editProviderConnectionLifecycle',
921+
providerId,
922+
providerConnectionInfo,
923+
params,
924+
onDataCallbacksTaskConnectionId,
925+
tokenId,
926+
);
927+
},
928+
);
929+
905930
contextBridge.exposeInMainWorld(
906931
'deleteProviderConnectionLifecycle',
907932
async (

packages/renderer/src/lib/onboarding/OnboardingComponent.svelte

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { configurationProperties } from '/@/stores/configurationProperties';
44
import { providerInfos } from '/@/stores/providers';
55
import type { IConfigurationPropertyRecordedSchema } from '../../../../main/src/plugin/configuration-registry';
66
import type { ProviderInfo } from '../../../../main/src/plugin/api/provider-info';
7-
import PreferencesConnectionCreationRendering from '../preferences/PreferencesConnectionCreationRendering.svelte';
7+
import PreferencesConnectionCreationOrEditRendering from '../preferences/PreferencesConnectionCreationOrEditRendering.svelte';
88
import type { OnboardingEmbeddedComponentType } from '../../../../main/src/plugin/api/onboarding';
99
import { faTriangleExclamation } from '@fortawesome/free-solid-svg-icons';
1010
import Fa from 'svelte-fa';
@@ -43,15 +43,15 @@ onMount(() => {
4343
Create a {providerDisplayName}
4444
</h1>
4545
{#if component === 'createContainerProviderConnection' && providerInfo?.containerProviderConnectionCreation === true}
46-
<PreferencesConnectionCreationRendering
46+
<PreferencesConnectionCreationOrEditRendering
4747
providerInfo="{providerInfo}"
4848
properties="{configurationItems}"
4949
propertyScope="ContainerProviderConnectionFactory"
5050
callback="{window.createContainerProviderConnection}"
5151
disableEmptyScreen="{true}"
5252
hideCloseButton="{true}" />
5353
{:else if component === 'createKubernetesProviderConnection' && providerInfo?.kubernetesProviderConnectionCreation === true}
54-
<PreferencesConnectionCreationRendering
54+
<PreferencesConnectionCreationOrEditRendering
5555
providerInfo="{providerInfo}"
5656
properties="{configurationItems}"
5757
propertyScope="KubernetesProviderConnectionFactory"

packages/renderer/src/lib/preferences/PreferencesConnectionActions.svelte

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<script lang="ts">
2-
import { faPlay, faRotateRight, faStop, faTrash } from '@fortawesome/free-solid-svg-icons';
2+
import { faEdit, faPlay, faRotateRight, faStop, faTrash } from '@fortawesome/free-solid-svg-icons';
33
import type {
44
ProviderContainerConnectionInfo,
55
ProviderInfo,
@@ -8,6 +8,8 @@ import type {
88
import LoadingIconButton from '../ui/LoadingIconButton.svelte';
99
import { type ConnectionCallback, eventCollect, startTask } from './preferences-connection-rendering-task';
1010
import { type IConnectionRestart, type IConnectionStatus } from './Util';
11+
import { router } from 'tinro';
12+
import { Buffer } from 'buffer';
1113
1214
export let connectionStatus: IConnectionStatus | undefined;
1315
export let provider: ProviderInfo;
@@ -97,6 +99,17 @@ async function stopConnectionProvider(
9799
}
98100
}
99101
102+
async function editConnectionProvider(
103+
provider: ProviderInfo,
104+
providerConnectionInfo: ProviderContainerConnectionInfo | ProviderKubernetesConnectionInfo,
105+
): Promise<void> {
106+
router.goto(
107+
`/preferences/container-connection/edit/${provider.internalId}/${Buffer.from(providerConnectionInfo.name).toString(
108+
'base64',
109+
)}`,
110+
);
111+
}
112+
100113
async function deleteConnectionProvider(
101114
provider: ProviderInfo,
102115
providerConnectionInfo: ProviderContainerConnectionInfo | ProviderKubernetesConnectionInfo,
@@ -169,6 +182,14 @@ function getLoggerHandler(
169182
state="{connectionStatus}"
170183
leftPosition="left-[0.22rem]" />
171184
{/if}
185+
{#if connection.lifecycleMethods.includes('edit')}
186+
<LoadingIconButton
187+
clickAction="{() => editConnectionProvider(provider, connection)}"
188+
action="edit"
189+
icon="{faEdit}"
190+
state="{connectionStatus}"
191+
leftPosition="left-[0.22rem]" />
192+
{/if}
172193
{#if connection.lifecycleMethods.includes('delete')}
173194
<div class="mr-2 text-sm">
174195
<LoadingIconButton

0 commit comments

Comments
 (0)