This repository was archived by the owner on Jun 26, 2026. It is now read-only.
Commit 7c443a9
feat(console): VM power controls (start/stop/restart) + VNC fixes (#27)
* feat(console): add VM power controls (start/stop/restart)
Add Start/Restart/Stop buttons to the VMInstance detail page that call
the KubeVirt subresources.kubevirt.io virtualmachines/{name}/start|stop|
restart endpoints. The underlying VirtualMachine is resolved as
<release.prefix><app-name> and its printableStatus drives which buttons
are enabled. Adds a generic subresource() method to the k8s client and a
useK8sSubresource() mutation hook.
Co-Authored-By: Claude <noreply@anthropic.com>
Signed-off-by: Andrei Kvapil <andrei.kvapil@aenix.io>
* fix(console): use prefixed VMI name for VNC connection
The VNC console targeted the VirtualMachineInstance by the cozystack app
name (e.g. "demo-vm"), but KubeVirt names it "<release.prefix><name>"
(e.g. "vm-instance-demo-vm"), so the websocket 404'd. Resolve the VMI
name via release.prefix, the same way the VM power controls do.
Co-Authored-By: Claude <noreply@anthropic.com>
Signed-off-by: Andrei Kvapil <andrei.kvapil@aenix.io>
* fix(k8s-client): handle empty 2xx response bodies
KubeVirt action subresources (virtualmachines/{name}/start|stop|restart)
return 202 Accepted with an empty body. request() called res.json()
unconditionally, throwing "Unexpected end of JSON input" and surfacing a
spurious error toast even though the action succeeded. Read the body as
text and return undefined when empty.
Co-Authored-By: Claude <noreply@anthropic.com>
Signed-off-by: Andrei Kvapil <andrei.kvapil@aenix.io>
* fix(console): don't open VNC when the VM is not running
VncTab connected the websocket whenever the app was a VMInstance,
regardless of power state, so a stopped VM showed a dead console. Poll
the VirtualMachine printableStatus and only attach when it is Running;
otherwise show a 'not running' notice.
Co-Authored-By: Claude <noreply@anthropic.com>
Signed-off-by: Andrei Kvapil <andrei.kvapil@aenix.io>
* style(console): match cozyportal VNC console toolbar
Restyle the VNC tab to mirror cozyportal-ui: a dark toolbar with a
connection-status indicator (Monitor icon + Connected/Connecting/
Disconnected + green dot) and icon buttons for Ctrl+Alt+Del, fullscreen
and reconnect; connecting/error overlays; aspect-ratio sizing with
fullscreen support. Reconnect now runs through a connectionKey-driven
effect instead of a duplicated RFB setup path.
Co-Authored-By: Claude <noreply@anthropic.com>
Signed-off-by: Andrei Kvapil <andrei.kvapil@aenix.io>
* fix(console): proxy WebSockets in dev so the VNC console connects
The dev server's /apis (and /api) proxy lacked ws: true, so the VNC
console's WebSocket upgrade was never forwarded to kubectl proxy and the
console hung on 'Connecting…'. Enable ws on both proxy entries.
Co-Authored-By: Claude <noreply@anthropic.com>
Signed-off-by: Andrei Kvapil <andrei.kvapil@aenix.io>
* test(k8s-client): cover subresource calls and empty 2xx body handling
KubeVirt action subresources (virtualmachines/{name}/start|stop|restart)
answer 2xx with an empty body, so the request helper must return undefined
instead of letting JSON.parse("") throw. Pin that, the 204 short-circuit,
and the subresource path/method construction.
Assisted-By: Claude <noreply@anthropic.com>
Signed-off-by: Aleksei Sviridkin <f@lex.la>
* fix(k8s-client): refresh the target resource after a subresource action
A subresource action and the resource whose status it changes can live under
different API groups: KubeVirt serves start/stop/restart under
subresources.kubevirt.io but the VirtualMachine (with printableStatus) under
kubevirt.io. The success handler built its invalidation key from the action
ref, so it never matched the status query. Add an optional invalidate target
ref and key off the resource prefix [k8s, group, version, plural, namespace],
which React Query prefix-matches against the by-name GET and any
field/label-selected LIST — so a watch-based status read is refreshed too.
Assisted-By: Claude <noreply@anthropic.com>
Signed-off-by: Aleksei Sviridkin <f@lex.la>
* fix(console): correct VM power controls and stream status via watch
Several fixes to the VM power controls:
- Resolve the KubeVirt VirtualMachine name from releasePrefix(ad) instead of an
empty-string prefix fallback, so it targets the same object the VNC tab does
and never queries a non-existent name when release.prefix is unset.
- Read status via useK8sList with a metadata.name field-selector instead of a
polled useK8sGet, so the watch layer streams printableStatus transitions live
(the architecture forbids refetchInterval); the action also invalidates the
resource for an instant refresh.
- Drop the dead "Halted" branch (not a KubeVirt printableStatus value) and
enable stop/restart for a Paused VM, which still has a running instance.
Assisted-By: Claude <noreply@anthropic.com>
Signed-off-by: Aleksei Sviridkin <f@lex.la>
* fix(console): stream VNC power state via watch and share VM-name resolution
The VNC tab hardcoded the "vm-instance-" prefix and polled the VirtualMachine
power state with refetchInterval. Resolve the name through releasePrefix(ad) so
it agrees with the power controls on the target object, and read status via
useK8sList with a metadata.name field-selector so the watch layer tracks the
power state live without polling.
Assisted-By: Claude <noreply@anthropic.com>
Signed-off-by: Aleksei Sviridkin <f@lex.la>
* fix(console): match Helm-managed resources by prefixed release name in events
EventsTab derived the Helm release name with an empty-string prefix fallback,
so when release.prefix was unset its app.kubernetes.io/instance selector queried
the bare app name and matched none of the Helm-managed Pods/PVCs (which carry
the "<singular>-<name>" instance label), silently dropping their events. Route
it through releasePrefix(ad) like the VM tabs, and cover both releasePrefix and
the resulting selector with tests.
Assisted-By: Claude <noreply@anthropic.com>
Signed-off-by: Aleksei Sviridkin <f@lex.la>
* refactor(forms): type the additionalProperties walk without as-any
The helper that binds AdditionalPropertiesField walked schema nodes through an
as-any cast. Route it through a small structural interface instead, narrowing
unknown values explicitly. No behaviour change — the existing array-items map
test still passes.
Assisted-By: Claude <noreply@anthropic.com>
Signed-off-by: Aleksei Sviridkin <f@lex.la>
---------
Signed-off-by: Andrei Kvapil <andrei.kvapil@aenix.io>
Signed-off-by: Aleksei Sviridkin <f@lex.la>
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Aleksei Sviridkin <f@lex.la>1 parent 27e9b1b commit 7c443a9
15 files changed
Lines changed: 1065 additions & 83 deletions
File tree
- apps/console
- src
- __tests__/k8s-client
- components
- lib
- routes/detail
- packages/k8s-client/src
Lines changed: 118 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
| 116 | + | |
| 117 | + | |
| 118 | + | |
Lines changed: 127 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
| 116 | + | |
| 117 | + | |
| 118 | + | |
| 119 | + | |
| 120 | + | |
| 121 | + | |
| 122 | + | |
| 123 | + | |
| 124 | + | |
| 125 | + | |
| 126 | + | |
| 127 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
103 | 103 | | |
104 | 104 | | |
105 | 105 | | |
| 106 | + | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
| 116 | + | |
| 117 | + | |
| 118 | + | |
106 | 119 | | |
107 | 120 | | |
108 | 121 | | |
| |||
112 | 125 | | |
113 | 126 | | |
114 | 127 | | |
115 | | - | |
| 128 | + | |
116 | 129 | | |
117 | 130 | | |
118 | 131 | | |
119 | 132 | | |
120 | 133 | | |
121 | | - | |
122 | | - | |
| 134 | + | |
123 | 135 | | |
124 | 136 | | |
125 | 137 | | |
126 | 138 | | |
127 | 139 | | |
128 | 140 | | |
129 | | - | |
| 141 | + | |
130 | 142 | | |
131 | 143 | | |
| 144 | + | |
132 | 145 | | |
133 | 146 | | |
134 | | - | |
135 | | - | |
136 | | - | |
| 147 | + | |
| 148 | + | |
| 149 | + | |
137 | 150 | | |
138 | | - | |
| 151 | + | |
139 | 152 | | |
140 | 153 | | |
141 | 154 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
33 | 33 | | |
34 | 34 | | |
35 | 35 | | |
| 36 | + | |
36 | 37 | | |
37 | 38 | | |
38 | 39 | | |
| |||
155 | 156 | | |
156 | 157 | | |
157 | 158 | | |
| 159 | + | |
| 160 | + | |
| 161 | + | |
158 | 162 | | |
159 | 163 | | |
160 | 164 | | |
| |||
0 commit comments