Merge branch 'main' into testing

This commit is contained in:
Michael Lipp 2025-03-31 22:57:49 +02:00
commit 8bf6692b8d
12 changed files with 178 additions and 44 deletions

View file

@ -1,6 +1,6 @@
/*
* VM-Operator
* Copyright (C) 2023 Michael N. Lipp
* Copyright (C) 2023,2025 Michael N. Lipp
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
@ -83,8 +83,18 @@
* [YamlConfigurationStore] *-right[hidden]- [Controller]
*
* [Manager] *-- [Controller]
* [Controller] *-- [VmWatcher]
* [Controller] *-- [Reconciler]
* Component VmMonitor as VmMonitor <<internal>>
* [Controller] *-- [VmMonitor]
* [VmMonitor] -right[hidden]- [PoolMonitor]
* Component PoolMonitor as PoolMonitor <<internal>>
* [Controller] *-- [PoolMonitor]
* Component PodMonitor as PodMonitor <<internal>>
* [Controller] *-- [PodMonitor]
* [PodMonitor] -up[hidden]- VmMonitor
* Component DisplaySecretMonitor as DisplaySecretMonitor <<internal>>
* [Controller] *-- [DisplaySecretMonitor]
* [DisplaySecretMonitor] -up[hidden]- VmMonitor
* [Controller] *-left- [Reconciler]
* [Controller] -right[hidden]- [GuiHttpServer]
*
* [Manager] *-down- [GuiSocketServer:8080]

View file

@ -1,8 +1,8 @@
apiVersion: "vmoperator.jdrupes.org/v1"
kind: VirtualMachine
metadata:
namespace: vmop-dev
name: unittest-vm
namespace: vmop-test
name: test-vm
spec:
image:
repository: docker-registry.lan.mnl.de

View file

@ -0,0 +1,111 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- ../../deploy
namespace: vmop-test
patches:
- patch: |-
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
name: vmop-image-repository
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 10Gi
storageClassName: local-path
- patch: |-
kind: ConfigMap
apiVersion: v1
metadata:
name: vm-operator
data:
# Keep in sync with config.yaml
config.yaml: |
"/Manager":
# clusterName: "test"
"/Controller":
"/Reconciler":
runnerData:
storageClassName: null
loadBalancerService:
labels:
label1: label1
label2: toBeReplaced
annotations:
metallb.universe.tf/loadBalancerIPs: 192.168.168.1
metallb.universe.tf/ip-allocated-from-pool: single-common
metallb.universe.tf/allow-shared-ip: single-common
"/GuiSocketServer":
port: 8888
"/GuiHttpServer":
# This configures the GUI
"/ConsoleWeblet":
"/WebConsole":
"/LoginConlet":
users:
- name: admin
fullName: Administrator
password: "$2b$05$NiBd74ZGdplLC63ePZf1f.UtjMKkbQ23cQoO2OKOFalDBHWAOy21."
- name: test1
fullName: Test Account
password: "$2b$05$hZaI/jToXf/d3BctZdT38Or7H7h6Pn2W3WiB49p5AyhDHFkkYCvo2"
- name: test2
fullName: Test Account
password: "$2b$05$hZaI/jToXf/d3BctZdT38Or7H7h6Pn2W3WiB49p5AyhDHFkkYCvo2"
- name: test3
fullName: Test Account
password: "$2b$05$hZaI/jToXf/d3BctZdT38Or7H7h6Pn2W3WiB49p5AyhDHFkkYCvo2"
"/RoleConfigurator":
rolesByUser:
# User admin has role admin
admin:
- admin
test1:
- user
test2:
- user
test3:
- user
# All users have role other
"*":
- other
replace: false
"/RoleConletFilter":
conletTypesByRole:
# Admins can use all conlets
admin:
- "*"
user:
- org.jdrupes.vmoperator.vmviewer.VmViewer
# Others cannot use any conlet (except login conlet to log out)
other:
- org.jgrapes.webconlet.locallogin.LoginConlet
"/ComponentCollector":
"/VmAccess":
displayResource:
preferredIpVersion: ipv4
syncPreviewsFor:
- role: user
- target:
group: apps
version: v1
kind: Deployment
name: vm-operator
patch: |-
- op: replace
path: /spec/template/spec/containers/0/image
value: docker-registry.lan.mnl.de/vmoperator/org.jdrupes.vmoperator.manager:test
- op: replace
path: /spec/template/spec/containers/0/imagePullPolicy
value: Always
- op: replace
path: /spec/replicas
value: 0

View file

@ -41,7 +41,7 @@ class BasicTests {
private static APIResource vmsContext;
private static K8sV1DeploymentStub mgrDeployment;
private static K8sDynamicStub vmStub;
private static final String VM_NAME = "unittest-vm";
private static final String VM_NAME = "test-vm";
private static final Object EXISTS = new Object();
@BeforeAll
@ -54,7 +54,7 @@ class BasicTests {
// Update manager pod by scaling deployment
mgrDeployment
= K8sV1DeploymentStub.get(client, "vmop-dev", "vm-operator");
= K8sV1DeploymentStub.get(client, "vmop-test", "vm-operator");
mgrDeployment.scale(0);
mgrDeployment.scale(1);
waitForManager();
@ -65,13 +65,13 @@ class BasicTests {
vmsContext = apiRes.get();
// Cleanup existing VM
K8sDynamicStub.get(client, vmsContext, "vmop-dev", VM_NAME)
K8sDynamicStub.get(client, vmsContext, "vmop-test", VM_NAME)
.delete();
ListOptions listOpts = new ListOptions();
listOpts.setLabelSelector("app.kubernetes.io/name=" + APP_NAME + ","
+ "app.kubernetes.io/instance=" + VM_NAME + ","
+ "app.kubernetes.io/component=" + DisplaySecret.NAME);
var secrets = K8sV1SecretStub.list(client, "vmop-dev", listOpts);
var secrets = K8sV1SecretStub.list(client, "vmop-test", listOpts);
for (var secret : secrets) {
secret.delete();
}
@ -103,7 +103,7 @@ class BasicTests {
"app.kubernetes.io/managed-by=" + VM_OP_NAME + ","
+ "app.kubernetes.io/name=" + APP_NAME + ","
+ "app.kubernetes.io/instance=" + VM_NAME);
var knownPvcs = K8sV1PvcStub.list(client, "vmop-dev", listOpts);
var knownPvcs = K8sV1PvcStub.list(client, "vmop-test", listOpts);
for (var pvc : knownPvcs) {
pvc.delete();
}
@ -112,7 +112,7 @@ class BasicTests {
@AfterAll
static void tearDownAfterClass() throws Exception {
// Cleanup
K8sDynamicStub.get(client, vmsContext, "vmop-dev", VM_NAME)
K8sDynamicStub.get(client, vmsContext, "vmop-test", VM_NAME)
.delete();
deletePvcs();
@ -124,7 +124,7 @@ class BasicTests {
void testConfigMap()
throws IOException, InterruptedException, ApiException {
K8sV1ConfigMapStub stub
= K8sV1ConfigMapStub.get(client, "vmop-dev", VM_NAME);
= K8sV1ConfigMapStub.get(client, "vmop-test", VM_NAME);
for (int i = 0; i < 10; i++) {
if (stub.model().isPresent()) {
break;
@ -134,7 +134,7 @@ class BasicTests {
// Check config map
var config = stub.model().get();
Map<List<? extends Object>, Object> toCheck = Map.of(
List.of("namespace"), "vmop-dev",
List.of("namespace"), "vmop-test",
List.of("name"), VM_NAME,
List.of("labels", "app.kubernetes.io/name"), Constants.APP_NAME,
List.of("labels", "app.kubernetes.io/instance"), VM_NAME,
@ -191,7 +191,7 @@ class BasicTests {
+ "app.kubernetes.io/component=" + DisplaySecret.NAME);
Collection<K8sV1SecretStub> secrets = null;
for (int i = 0; i < 10; i++) {
secrets = K8sV1SecretStub.list(client, "vmop-dev", listOpts);
secrets = K8sV1SecretStub.list(client, "vmop-test", listOpts);
if (secrets.size() > 0) {
break;
}
@ -207,7 +207,7 @@ class BasicTests {
@Test
void testRunnerPvc() throws ApiException, InterruptedException {
var stub
= K8sV1PvcStub.get(client, "vmop-dev", VM_NAME + "-runner-data");
= K8sV1PvcStub.get(client, "vmop-test", VM_NAME + "-runner-data");
for (int i = 0; i < 10; i++) {
if (stub.model().isPresent()) {
break;
@ -227,7 +227,7 @@ class BasicTests {
@Test
void testSystemDiskPvc() throws ApiException, InterruptedException {
var stub
= K8sV1PvcStub.get(client, "vmop-dev", VM_NAME + "-system-disk");
= K8sV1PvcStub.get(client, "vmop-test", VM_NAME + "-system-disk");
for (int i = 0; i < 10; i++) {
if (stub.model().isPresent()) {
break;
@ -248,7 +248,7 @@ class BasicTests {
@Test
void testDisk1Pvc() throws ApiException, InterruptedException {
var stub
= K8sV1PvcStub.get(client, "vmop-dev", VM_NAME + "-disk-1");
= K8sV1PvcStub.get(client, "vmop-test", VM_NAME + "-disk-1");
for (int i = 0; i < 10; i++) {
if (stub.model().isPresent()) {
break;
@ -274,7 +274,7 @@ class BasicTests {
new V1Patch("[{\"op\": \"replace\", \"path\": \"/spec/vm/state"
+ "\", \"value\": \"Running\"}]"),
client.defaultPatchOptions()).isPresent());
var stub = K8sV1PodStub.get(client, "vmop-dev", VM_NAME);
var stub = K8sV1PodStub.get(client, "vmop-test", VM_NAME);
for (int i = 0; i < 20; i++) {
if (stub.model().isPresent()) {
break;
@ -303,7 +303,7 @@ class BasicTests {
@Test
public void testLoadBalancer() throws ApiException, InterruptedException {
var stub = K8sV1ServiceStub.get(client, "vmop-dev", VM_NAME);
var stub = K8sV1ServiceStub.get(client, "vmop-test", VM_NAME);
for (int i = 0; i < 10; i++) {
if (stub.model().isPresent()) {
break;

View file

@ -157,6 +157,15 @@ import org.jgrapes.util.events.WatchFile;
*
* success --> Running
*
* state Running {
* state Booting
* state Booted
*
* [*] -right-> Booting
* Booting -down-> Booting: VserportChanged[guest agent connected]/fire GetOsinfo
* Booting --> Booted: Osinfo
* }
*
* state Terminating {
* state terminate <<entryPoint>>
* state qemuRunning <<choice>>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 42 KiB

After

Width:  |  Height:  |  Size: 69 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 14 KiB

Before After
Before After

View file

@ -12,10 +12,10 @@ layout: vm-operator
An overview display shows the current CPU and RAM usage and a graph
with recent changes.
![VM-Operator GUI](VM-Operator-GUI-preview.png)
![VM-Operator admin GUI preview](VM-Operator-GUI-preview.png)
The detail display lists all VMs. From here you can start and stop
the VMs and adjust the CPU and RAM usages (modifies the definition
in kubernetes).
![VM-Operator GUI](VM-Operator-GUI-view.png)
![VM-Operator admin GUI view](VM-Operator-GUI-view.png)

View file

@ -9,7 +9,7 @@ layout: vm-operator
When users log into the web GUI, they have already authenticated with the
VM-Operator. In some environments, requiring an additional login on the
guest OS can be cumbersome. To enhance the user experience, the VM-Operator
guest OS can be annoying. To enhance the user experience, the VM-Operator
supports automatic login on the guest operating system, thus eliminating
the need for multiple logins. However, this feature requires specific
support from the guest OS.
@ -18,9 +18,9 @@ support from the guest OS.
Automatic login requires an agent running inside the guest OS. Similar
to QEMU's standard guest agent, the VM-Operator agent communicates with
the host via a tty device (`/dev/virtio-ports/org.jdrupes.vmop_agent.0`). On
modern Linux systems, `udev` can detect this device and trigger the start
of an associated systemd service.
the host via a tty device (provided in the guest as
`/dev/virtio-ports/org.jdrupes.vmop_agent.0`). On modern Linux systems, `udev` can
detect this device and trigger the start of an associated systemd service.
Sample configuration files for a VM-Operator agent are available
[here](https://github.com/mnlipp/VM-Operator/tree/main/dev-example/vmop-agent).

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 588 KiB

After

Width:  |  Height:  |  Size: 591 KiB

Before After
Before After

View file

@ -9,7 +9,7 @@ layout: vm-operator
# Welcome to VM-Operator
![Overview picture](index-pic.svg)
![VM-Operator summary picture](index-pic.svg)
This project provides an easy to use and flexible solution for
running QEMU/KVM based virtual machines (VMs) in Kubernetes pods.

View file

@ -46,16 +46,20 @@ spec:
The `retention` specifies how long the assignment of a VM from the pool to
a user remains valid after the user closes the console. This ensures that
a user can resume work within this timeframe without the risk of another
user taking over the VM. The time is specified as
user taking over the VM. The time is specified as an
[ISO 8601 duration](https://en.wikipedia.org/wiki/ISO_8601#Durations).
Specifying an ISO 8601 time is also supported, but if you consider
using an absolute time, check again whether a dedicated VM for the user
isn't the more appropriate choice.
Setting `loginOnAssignment` to `true` triggers automatic login of the
user (as described in [section auto login](auto-login.html)) when
the VM is assigned. The `permissions` property specifies the actions
that users or roles can perform on assigned VMs.
Setting `loginOnAssignment` to `true` (defaults to `false`) triggers automatic
login of the user (as described in [section auto login](auto-login.html))
when the VM is assigned. The `permissions` property specifies the actions
that users or roles can perform on assigned VMs. The `may` property defaults
to `[accessConsole]` if not specified.
VMs become members of one (or more) pools by adding the pool name to
the `spec.pools` array, as shown below:
the `spec.pools` array in the VM definition, as shown below:
```yaml
apiVersion: "vmoperator.jdrupes.org/v1"