From 789dd52c778d24ba4e8ff6df6b63b0c6c5d4e683 Mon Sep 17 00:00:00 2001 From: Kevin Wang Date: Tue, 27 Jan 2026 12:36:48 +0000 Subject: [PATCH] feat(vmm-ui): Add init_script support and improve encrypted env visibility - Add init_script field to Create VM dialog (executed before dockerd starts) - Add init_script field to Update VM dialog (in compose update section) - Fix encrypted env editor to only show when KMS key provider is selected - Update useVmManager to handle init_script in form state and compose generation --- vmm/src/console_v1.html | 121 ++++++++++++++---------- vmm/ui/src/components/CreateVmDialog.ts | 15 ++- vmm/ui/src/components/UpdateVmDialog.ts | 68 +++++++------ vmm/ui/src/composables/useVmManager.ts | 14 ++- 4 files changed, 136 insertions(+), 82 deletions(-) diff --git a/vmm/src/console_v1.html b/vmm/src/console_v1.html index d4a83fe0..139f6ae7 100644 --- a/vmm/src/console_v1.html +++ b/vmm/src/console_v1.html @@ -2056,8 +2056,17 @@

Deploy a new instance

- - + + +
+ +
+ +
@@ -2101,7 +2110,7 @@

Deploy a new instance

-
+
@@ -2186,42 +2195,50 @@

Update VM Config

-
-
- - -
+
+ + +
-
- -
+
+ +
-
-
- -
-
- - or paste below - -
- +
+
+ +
+
+ + or paste below +
-
-
- - -
-
- Compose Hash: 0x{{ composeHashPreview }} +
+
+ + +
+
+ + +
+
+ Compose Hash: 0x{{ composeHashPreview }} +
+
+
@@ -2342,6 +2359,7 @@

Derive VM

name: '', image: '', dockerComposeFile: '', + initScript: '', preLaunchScript, vcpu: 1, memory: 2048, @@ -2378,6 +2396,7 @@

Derive VM

vm: null, updateCompose: false, dockerComposeFile: '', + initScript: '', preLaunchScript: '', encryptedEnvs: [], resetSecrets: false, @@ -2816,7 +2835,7 @@

Derive VM

.join(''); } async function makeAppComposeFile() { - var _a; + var _a, _b; const appCompose = { manifest_version: 2, name: vmForm.value.name, @@ -2844,7 +2863,10 @@

Derive VM

if (vmForm.value.storage_fs) { appCompose.storage_fs = vmForm.value.storage_fs; } - if ((_a = vmForm.value.preLaunchScript) === null || _a === void 0 ? void 0 : _a.trim()) { + if ((_a = vmForm.value.initScript) === null || _a === void 0 ? void 0 : _a.trim()) { + appCompose.init_script = vmForm.value.initScript; + } + if ((_b = vmForm.value.preLaunchScript) === null || _b === void 0 ? void 0 : _b.trim()) { appCompose.pre_launch_script = vmForm.value.preLaunchScript; } const swapBytes = Math.max(0, Math.round(vmForm.value.swap_size || 0)); @@ -2863,7 +2885,7 @@

Derive VM

return JSON.stringify(appCompose); } async function makeUpdateComposeFile() { - var _a; + var _a, _b; const currentAppCompose = updateDialog.value.vm.appCompose; const appCompose = { ...currentAppCompose, @@ -2877,7 +2899,8 @@

Derive VM

appCompose.launch_token_hash = await calcComposeHash(launchToken.value); } } - appCompose.pre_launch_script = (_a = updateDialog.value.preLaunchScript) === null || _a === void 0 ? void 0 : _a.trim(); + appCompose.init_script = ((_a = updateDialog.value.initScript) === null || _a === void 0 ? void 0 : _a.trim()) || undefined; + appCompose.pre_launch_script = ((_b = updateDialog.value.preLaunchScript) === null || _b === void 0 ? void 0 : _b.trim()) || undefined; const swapBytes = Math.max(0, Math.round(updateDialog.value.swap_size || 0)); if (swapBytes > 0) { appCompose.swap_size = swapBytes; @@ -2955,6 +2978,7 @@

Derive VM

vm: detailedVm, updateCompose: false, dockerComposeFile: detailedVm.appCompose.docker_compose_file || '', + initScript: detailedVm.appCompose.init_script || '', preLaunchScript: detailedVm.appCompose.pre_launch_script || '', encryptedEnvs: [], resetSecrets: false, @@ -3145,7 +3169,7 @@

Derive VM

return 'none'; } async function showCloneConfig(vm) { - var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m; + var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o; const theVm = await ensureVmDetails(vm); if (!((_a = theVm === null || theVm === void 0 ? void 0 : theVm.configuration) === null || _a === void 0 ? void 0 : _a.compose_file)) { alert('Compose file not available for this VM. Please open its details first.'); @@ -3157,29 +3181,30 @@

Derive VM

name: `${config.name || vm.name}`, image: config.image || '', dockerComposeFile: ((_b = theVm.appCompose) === null || _b === void 0 ? void 0 : _b.docker_compose_file) || '', - preLaunchScript: ((_c = theVm.appCompose) === null || _c === void 0 ? void 0 : _c.pre_launch_script) || '', + initScript: ((_c = theVm.appCompose) === null || _c === void 0 ? void 0 : _c.init_script) || '', + preLaunchScript: ((_d = theVm.appCompose) === null || _d === void 0 ? void 0 : _d.pre_launch_script) || '', vcpu: config.vcpu || 1, memory: config.memory || 0, memoryValue: autoMemoryDisplay(config.memory || 0).memoryValue, memoryUnit: autoMemoryDisplay(config.memory || 0).memoryUnit, - swap_size: ((_d = theVm.appCompose) === null || _d === void 0 ? void 0 : _d.swap_size) || 0, - swapValue: autoMemoryDisplay(bytesToMB(((_e = theVm.appCompose) === null || _e === void 0 ? void 0 : _e.swap_size) || 0)).memoryValue, - swapUnit: autoMemoryDisplay(bytesToMB(((_f = theVm.appCompose) === null || _f === void 0 ? void 0 : _f.swap_size) || 0)).memoryUnit, + swap_size: ((_e = theVm.appCompose) === null || _e === void 0 ? void 0 : _e.swap_size) || 0, + swapValue: autoMemoryDisplay(bytesToMB(((_f = theVm.appCompose) === null || _f === void 0 ? void 0 : _f.swap_size) || 0)).memoryValue, + swapUnit: autoMemoryDisplay(bytesToMB(((_g = theVm.appCompose) === null || _g === void 0 ? void 0 : _g.swap_size) || 0)).memoryUnit, disk_size: config.disk_size || 0, selectedGpus: [], attachAllGpus: false, encryptedEnvs: [], // Clear environment variables ports: [], // Clear port mappings - storage_fs: ((_g = theVm.appCompose) === null || _g === void 0 ? void 0 : _g.storage_fs) || 'zfs', + storage_fs: ((_h = theVm.appCompose) === null || _h === void 0 ? void 0 : _h.storage_fs) || 'zfs', app_id: config.app_id || '', kms_urls: config.kms_urls || [], key_provider: getKeyProvider(theVm), - key_provider_id: ((_h = theVm.appCompose) === null || _h === void 0 ? void 0 : _h.key_provider_id) || '', - gateway_enabled: !!((_j = theVm.appCompose) === null || _j === void 0 ? void 0 : _j.gateway_enabled), + key_provider_id: ((_j = theVm.appCompose) === null || _j === void 0 ? void 0 : _j.key_provider_id) || '', + gateway_enabled: !!((_k = theVm.appCompose) === null || _k === void 0 ? void 0 : _k.gateway_enabled), gateway_urls: config.gateway_urls || [], - public_logs: !!((_k = theVm.appCompose) === null || _k === void 0 ? void 0 : _k.public_logs), - public_sysinfo: !!((_l = theVm.appCompose) === null || _l === void 0 ? void 0 : _l.public_sysinfo), - public_tcbinfo: !!((_m = theVm.appCompose) === null || _m === void 0 ? void 0 : _m.public_tcbinfo), + public_logs: !!((_l = theVm.appCompose) === null || _l === void 0 ? void 0 : _l.public_logs), + public_sysinfo: !!((_m = theVm.appCompose) === null || _m === void 0 ? void 0 : _m.public_sysinfo), + public_tcbinfo: !!((_o = theVm.appCompose) === null || _o === void 0 ? void 0 : _o.public_tcbinfo), pin_numa: !!config.pin_numa, hugepages: !!config.hugepages, no_tee: !!config.no_tee, diff --git a/vmm/ui/src/components/CreateVmDialog.ts b/vmm/ui/src/components/CreateVmDialog.ts index 0cc73ca2..fc79e0b3 100644 --- a/vmm/ui/src/components/CreateVmDialog.ts +++ b/vmm/ui/src/components/CreateVmDialog.ts @@ -105,8 +105,17 @@ const CreateVmDialogComponent = {
- - + + +
+ +
+ +
@@ -150,7 +159,7 @@ const CreateVmDialogComponent = {
-
+
diff --git a/vmm/ui/src/components/UpdateVmDialog.ts b/vmm/ui/src/components/UpdateVmDialog.ts index f1c499ec..c30ef894 100644 --- a/vmm/ui/src/components/UpdateVmDialog.ts +++ b/vmm/ui/src/components/UpdateVmDialog.ts @@ -62,42 +62,50 @@ const UpdateVmDialogComponent = {
-
-
- - -
+
+ + +
-
- -
+
+ +
-
-
- -
-
- - or paste below - -
- +
+
+ +
+
+ + or paste below +
-
-
- - -
-
- Compose Hash: 0x{{ composeHashPreview }} +
+
+ + +
+
+ + +
+
+ Compose Hash: 0x{{ composeHashPreview }} +
+
+
diff --git a/vmm/ui/src/composables/useVmManager.ts b/vmm/ui/src/composables/useVmManager.ts index d7553420..3c8f72e1 100644 --- a/vmm/ui/src/composables/useVmManager.ts +++ b/vmm/ui/src/composables/useVmManager.ts @@ -30,6 +30,7 @@ type AppCompose = { swap_size: number; launch_token_hash?: string; pre_launch_script?: string; + init_script?: string; }; type KeyProviderKind = 'none' | 'kms' | 'local' | 'tpm'; @@ -80,6 +81,7 @@ type VmFormState = { name: string; image: string; dockerComposeFile: string; + initScript: string; preLaunchScript: string; vcpu: number; memory: number; @@ -115,6 +117,7 @@ type UpdateDialogState = { vm: VmListItem | null; updateCompose: boolean; dockerComposeFile: string; + initScript: string; preLaunchScript: string; encryptedEnvs: EncryptedEnvEntry[]; resetSecrets: boolean; @@ -160,6 +163,7 @@ function createVmFormState(preLaunchScript: string): VmFormState { name: '', image: '', dockerComposeFile: '', + initScript: '', preLaunchScript, vcpu: 1, memory: 2048, @@ -197,6 +201,7 @@ function createUpdateDialogState(): UpdateDialogState { vm: null, updateCompose: false, dockerComposeFile: '', + initScript: '', preLaunchScript: '', encryptedEnvs: [], resetSecrets: false, @@ -732,6 +737,10 @@ type CreateVmPayloadSource = { appCompose.storage_fs = vmForm.value.storage_fs; } + if (vmForm.value.initScript?.trim()) { + appCompose.init_script = vmForm.value.initScript; + } + if (vmForm.value.preLaunchScript?.trim()) { appCompose.pre_launch_script = vmForm.value.preLaunchScript; } @@ -769,7 +778,8 @@ type CreateVmPayloadSource = { appCompose.launch_token_hash = await calcComposeHash(launchToken.value); } } - appCompose.pre_launch_script = updateDialog.value.preLaunchScript?.trim(); + appCompose.init_script = updateDialog.value.initScript?.trim() || undefined; + appCompose.pre_launch_script = updateDialog.value.preLaunchScript?.trim() || undefined; const swapBytes = Math.max(0, Math.round(updateDialog.value.swap_size || 0)); if (swapBytes > 0) { @@ -857,6 +867,7 @@ type CreateVmPayloadSource = { vm: detailedVm, updateCompose: false, dockerComposeFile: detailedVm.appCompose.docker_compose_file || '', + initScript: detailedVm.appCompose.init_script || '', preLaunchScript: detailedVm.appCompose.pre_launch_script || '', encryptedEnvs: [], resetSecrets: false, @@ -1088,6 +1099,7 @@ type CreateVmPayloadSource = { name: `${config.name || vm.name}`, image: config.image || '', dockerComposeFile: theVm.appCompose?.docker_compose_file || '', + initScript: theVm.appCompose?.init_script || '', preLaunchScript: theVm.appCompose?.pre_launch_script || '', vcpu: config.vcpu || 1, memory: config.memory || 0,