Merge branch 'main' into testing
This commit is contained in:
commit
8bf6692b8d
12 changed files with 178 additions and 44 deletions
|
|
@ -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]
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
111
org.jdrupes.vmoperator.manager/test-resources/kustomization.yaml
Normal file
111
org.jdrupes.vmoperator.manager/test-resources/kustomization.yaml
Normal 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
|
||||
|
||||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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 |
Binary file not shown.
|
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 14 KiB |
|
|
@ -12,10 +12,10 @@ layout: vm-operator
|
|||
An overview display shows the current CPU and RAM usage and a graph
|
||||
with recent changes.
|
||||
|
||||

|
||||

|
||||
|
||||
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).
|
||||
|
||||

|
||||

|
||||
|
|
|
|||
|
|
@ -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 |
|
|
@ -9,7 +9,7 @@ layout: vm-operator
|
|||
|
||||
# Welcome to VM-Operator
|
||||
|
||||

|
||||

|
||||
|
||||
This project provides an easy to use and flexible solution for
|
||||
running QEMU/KVM based virtual machines (VMs) in Kubernetes pods.
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue