From 2b471f3852538b15b2ea3e6b2c445d8a7aa27662 Mon Sep 17 00:00:00 2001 From: "Michael N. Lipp" Date: Thu, 14 Sep 2023 09:56:28 +0200 Subject: [PATCH 01/18] More resources used by both runner and manager. --- .../manager/ConfigMapReconciler.java | 7 +++- .../jdrupes/vmoperator/manager/Constants.java | 9 ----- .../manager/LoadBalancerReconciler.java | 2 + .../jdrupes/vmoperator/manager/Manager.java | 2 +- .../vmoperator/manager/Reconciler.java | 4 +- .../manager/StatefuleSetReconciler.java | 2 + .../jdrupes/vmoperator/manager/VmWatcher.java | 14 ++++--- .../.settings/org.eclipse.jdt.core.prefs | 2 +- org.jdrupes.vmoperator.util/build.gradle | 1 + .../jdrupes/vmoperator/util/Constants.java | 37 +++++++++++++++++++ .../org/jdrupes/vmoperator/util}/GsonPtr.java | 2 +- .../src/org/jdrupes/vmoperator/util}/K8s.java | 2 +- 12 files changed, 62 insertions(+), 22 deletions(-) create mode 100644 org.jdrupes.vmoperator.util/src/org/jdrupes/vmoperator/util/Constants.java rename {org.jdrupes.vmoperator.manager/src/org/jdrupes/vmoperator/manager => org.jdrupes.vmoperator.util/src/org/jdrupes/vmoperator/util}/GsonPtr.java (99%) rename {org.jdrupes.vmoperator.manager/src/org/jdrupes/vmoperator/manager => org.jdrupes.vmoperator.util/src/org/jdrupes/vmoperator/util}/K8s.java (99%) diff --git a/org.jdrupes.vmoperator.manager/src/org/jdrupes/vmoperator/manager/ConfigMapReconciler.java b/org.jdrupes.vmoperator.manager/src/org/jdrupes/vmoperator/manager/ConfigMapReconciler.java index 340a3cd..754669d 100644 --- a/org.jdrupes.vmoperator.manager/src/org/jdrupes/vmoperator/manager/ConfigMapReconciler.java +++ b/org.jdrupes.vmoperator.manager/src/org/jdrupes/vmoperator/manager/ConfigMapReconciler.java @@ -32,6 +32,9 @@ import java.io.IOException; import java.io.StringWriter; import java.util.Map; import java.util.logging.Logger; +import static org.jdrupes.vmoperator.manager.Constants.APP_NAME; +import static org.jdrupes.vmoperator.util.Constants.VM_OP_NAME; +import org.jdrupes.vmoperator.util.K8s; import org.yaml.snakeyaml.LoaderOptions; import org.yaml.snakeyaml.Yaml; import org.yaml.snakeyaml.constructor.SafeConstructor; @@ -98,8 +101,8 @@ import org.yaml.snakeyaml.constructor.SafeConstructor; DynamicKubernetesObject newCm) { ListOptions listOpts = new ListOptions(); listOpts.setLabelSelector( - "app.kubernetes.io/managed-by=" + Constants.VM_OP_NAME + "," - + "app.kubernetes.io/name=" + Constants.APP_NAME); + "app.kubernetes.io/managed-by=" + VM_OP_NAME + "," + + "app.kubernetes.io/name=" + APP_NAME); // Get pod, selected by label var podApi = new DynamicKubernetesApi("", "v1", "pods", client); var pods = podApi diff --git a/org.jdrupes.vmoperator.manager/src/org/jdrupes/vmoperator/manager/Constants.java b/org.jdrupes.vmoperator.manager/src/org/jdrupes/vmoperator/manager/Constants.java index d1482b6..dd2774e 100644 --- a/org.jdrupes.vmoperator.manager/src/org/jdrupes/vmoperator/manager/Constants.java +++ b/org.jdrupes.vmoperator.manager/src/org/jdrupes/vmoperator/manager/Constants.java @@ -23,15 +23,6 @@ package org.jdrupes.vmoperator.manager; */ public class Constants { - /** The Constant VM_OP_NAME. */ - public static final String VM_OP_NAME = "vm-operator"; - - /** The Constant VM_OP_GROUP. */ - public static final String VM_OP_GROUP = "vmoperator.jdrupes.org"; - - /** The Constant VM_OP_KIND_VM. */ - public static final String VM_OP_KIND_VM = "VirtualMachine"; - /** The Constant APP_NAME. */ public static final String APP_NAME = "vm-runner"; diff --git a/org.jdrupes.vmoperator.manager/src/org/jdrupes/vmoperator/manager/LoadBalancerReconciler.java b/org.jdrupes.vmoperator.manager/src/org/jdrupes/vmoperator/manager/LoadBalancerReconciler.java index a9c0d2b..b8decd8 100644 --- a/org.jdrupes.vmoperator.manager/src/org/jdrupes/vmoperator/manager/LoadBalancerReconciler.java +++ b/org.jdrupes.vmoperator.manager/src/org/jdrupes/vmoperator/manager/LoadBalancerReconciler.java @@ -33,6 +33,8 @@ import java.util.Collections; import java.util.Map; import java.util.Optional; import java.util.logging.Logger; +import org.jdrupes.vmoperator.util.GsonPtr; +import org.jdrupes.vmoperator.util.K8s; import org.yaml.snakeyaml.LoaderOptions; import org.yaml.snakeyaml.Yaml; import org.yaml.snakeyaml.constructor.SafeConstructor; diff --git a/org.jdrupes.vmoperator.manager/src/org/jdrupes/vmoperator/manager/Manager.java b/org.jdrupes.vmoperator.manager/src/org/jdrupes/vmoperator/manager/Manager.java index 5b55ca8..e26b813 100644 --- a/org.jdrupes.vmoperator.manager/src/org/jdrupes/vmoperator/manager/Manager.java +++ b/org.jdrupes.vmoperator.manager/src/org/jdrupes/vmoperator/manager/Manager.java @@ -30,7 +30,7 @@ import org.apache.commons.cli.CommandLineParser; import org.apache.commons.cli.DefaultParser; import org.apache.commons.cli.Option; import org.apache.commons.cli.Options; -import static org.jdrupes.vmoperator.manager.Constants.VM_OP_NAME; +import static org.jdrupes.vmoperator.util.Constants.VM_OP_NAME; import org.jdrupes.vmoperator.util.FsdUtils; import org.jgrapes.core.Component; import org.jgrapes.core.Components; diff --git a/org.jdrupes.vmoperator.manager/src/org/jdrupes/vmoperator/manager/Reconciler.java b/org.jdrupes.vmoperator.manager/src/org/jdrupes/vmoperator/manager/Reconciler.java index 9777af1..94b79dc 100644 --- a/org.jdrupes.vmoperator.manager/src/org/jdrupes/vmoperator/manager/Reconciler.java +++ b/org.jdrupes.vmoperator.manager/src/org/jdrupes/vmoperator/manager/Reconciler.java @@ -43,10 +43,12 @@ import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Optional; -import static org.jdrupes.vmoperator.manager.Constants.VM_OP_GROUP; import org.jdrupes.vmoperator.manager.VmDefChanged.Type; +import static org.jdrupes.vmoperator.util.Constants.VM_OP_GROUP; import org.jdrupes.vmoperator.util.Convertions; import org.jdrupes.vmoperator.util.ExtendedObjectWrapper; +import org.jdrupes.vmoperator.util.GsonPtr; +import org.jdrupes.vmoperator.util.K8s; import org.jgrapes.core.Channel; import org.jgrapes.core.Component; import org.jgrapes.core.annotation.Handler; diff --git a/org.jdrupes.vmoperator.manager/src/org/jdrupes/vmoperator/manager/StatefuleSetReconciler.java b/org.jdrupes.vmoperator.manager/src/org/jdrupes/vmoperator/manager/StatefuleSetReconciler.java index 6f6d09c..94764ad 100644 --- a/org.jdrupes.vmoperator.manager/src/org/jdrupes/vmoperator/manager/StatefuleSetReconciler.java +++ b/org.jdrupes.vmoperator.manager/src/org/jdrupes/vmoperator/manager/StatefuleSetReconciler.java @@ -29,6 +29,8 @@ import java.io.IOException; import java.io.StringWriter; import java.util.Map; import java.util.logging.Logger; +import org.jdrupes.vmoperator.util.GsonPtr; +import org.jdrupes.vmoperator.util.K8s; import org.yaml.snakeyaml.LoaderOptions; import org.yaml.snakeyaml.Yaml; import org.yaml.snakeyaml.constructor.SafeConstructor; diff --git a/org.jdrupes.vmoperator.manager/src/org/jdrupes/vmoperator/manager/VmWatcher.java b/org.jdrupes.vmoperator.manager/src/org/jdrupes/vmoperator/manager/VmWatcher.java index a304c0b..7ecce4c 100644 --- a/org.jdrupes.vmoperator.manager/src/org/jdrupes/vmoperator/manager/VmWatcher.java +++ b/org.jdrupes.vmoperator.manager/src/org/jdrupes/vmoperator/manager/VmWatcher.java @@ -44,9 +44,11 @@ import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.logging.Level; -import static org.jdrupes.vmoperator.manager.Constants.VM_OP_GROUP; -import static org.jdrupes.vmoperator.manager.Constants.VM_OP_KIND_VM; +import static org.jdrupes.vmoperator.manager.Constants.APP_NAME; import org.jdrupes.vmoperator.manager.VmDefChanged.Type; +import static org.jdrupes.vmoperator.util.Constants.VM_OP_GROUP; +import static org.jdrupes.vmoperator.util.Constants.VM_OP_KIND_VM; +import static org.jdrupes.vmoperator.util.Constants.VM_OP_NAME; import org.jgrapes.core.Channel; import org.jgrapes.core.Component; import org.jgrapes.core.Components; @@ -132,7 +134,7 @@ public class VmWatcher extends Component { for (var version : vmOpApiVersions) { coa.getAPIResources(VM_OP_GROUP, version) .getResources().stream() - .filter(r -> Constants.VM_OP_KIND_VM.equals(r.getKind())) + .filter(r -> VM_OP_KIND_VM.equals(r.getKind())) .findFirst() .ifPresent(crd -> watchVmDefs(crd, version)); } @@ -148,15 +150,15 @@ public class VmWatcher extends Component { // Get all known CR instances. coa.getAPIResources(VM_OP_GROUP, version) .getResources().stream() - .filter(r -> Constants.VM_OP_KIND_VM.equals(r.getKind())) + .filter(r -> VM_OP_KIND_VM.equals(r.getKind())) .findFirst() .ifPresent(crd -> known.addAll(getKnown(client, crd, version))); } ListOptions opts = new ListOptions(); opts.setLabelSelector( - "app.kubernetes.io/managed-by=" + Constants.VM_OP_NAME + "," - + "app.kubernetes.io/name=" + Constants.APP_NAME); + "app.kubernetes.io/managed-by=" + VM_OP_NAME + "," + + "app.kubernetes.io/name=" + APP_NAME); for (String resource : List.of("apps/v1/statefulsets", "v1/configmaps", "v1/secrets")) { var resParts = new LinkedList<>(List.of(resource.split("/"))); diff --git a/org.jdrupes.vmoperator.util/.settings/org.eclipse.jdt.core.prefs b/org.jdrupes.vmoperator.util/.settings/org.eclipse.jdt.core.prefs index 4250f30..17fc9079 100644 --- a/org.jdrupes.vmoperator.util/.settings/org.eclipse.jdt.core.prefs +++ b/org.jdrupes.vmoperator.util/.settings/org.eclipse.jdt.core.prefs @@ -1,5 +1,5 @@ # -#Fri Sep 01 16:56:14 CEST 2023 +#Thu Sep 14 09:53:57 CEST 2023 org.eclipse.jdt.core.formatter.insert_space_after_ellipsis=insert org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_declarations=insert org.eclipse.jdt.core.formatter.insert_new_line_in_empty_annotation_declaration=insert diff --git a/org.jdrupes.vmoperator.util/build.gradle b/org.jdrupes.vmoperator.util/build.gradle index 0dac0c5..f20afcb 100644 --- a/org.jdrupes.vmoperator.util/build.gradle +++ b/org.jdrupes.vmoperator.util/build.gradle @@ -10,4 +10,5 @@ plugins { dependencies { implementation 'org.freemarker:freemarker:[2.3.32,2.4)' + implementation 'io.kubernetes:client-java:[18.0.0,19)' } diff --git a/org.jdrupes.vmoperator.util/src/org/jdrupes/vmoperator/util/Constants.java b/org.jdrupes.vmoperator.util/src/org/jdrupes/vmoperator/util/Constants.java new file mode 100644 index 0000000..14fad65 --- /dev/null +++ b/org.jdrupes.vmoperator.util/src/org/jdrupes/vmoperator/util/Constants.java @@ -0,0 +1,37 @@ +/* + * VM-Operator + * Copyright (C) 2023 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 + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package org.jdrupes.vmoperator.util; + +/** + * Some constants. + */ +public class Constants { + + /** The Constant VM_OP_NAME. */ + public static final String VM_OP_NAME = "vm-operator"; + + /** The Constant VM_OP_GROUP. */ + public static final String VM_OP_GROUP = "vmoperator.jdrupes.org"; + + /** The Constant VM_OP_CRD_NAME. */ + public static final String VM_OP_CRD_NAME = "vms." + VM_OP_GROUP; + + /** The Constant VM_OP_KIND_VM. */ + public static final String VM_OP_KIND_VM = "VirtualMachine"; +} diff --git a/org.jdrupes.vmoperator.manager/src/org/jdrupes/vmoperator/manager/GsonPtr.java b/org.jdrupes.vmoperator.util/src/org/jdrupes/vmoperator/util/GsonPtr.java similarity index 99% rename from org.jdrupes.vmoperator.manager/src/org/jdrupes/vmoperator/manager/GsonPtr.java rename to org.jdrupes.vmoperator.util/src/org/jdrupes/vmoperator/util/GsonPtr.java index 7d0f403..22a76f3 100644 --- a/org.jdrupes.vmoperator.manager/src/org/jdrupes/vmoperator/manager/GsonPtr.java +++ b/org.jdrupes.vmoperator.util/src/org/jdrupes/vmoperator/util/GsonPtr.java @@ -16,7 +16,7 @@ * along with this program. If not, see . */ -package org.jdrupes.vmoperator.manager; +package org.jdrupes.vmoperator.util; import com.google.gson.JsonArray; import com.google.gson.JsonElement; diff --git a/org.jdrupes.vmoperator.manager/src/org/jdrupes/vmoperator/manager/K8s.java b/org.jdrupes.vmoperator.util/src/org/jdrupes/vmoperator/util/K8s.java similarity index 99% rename from org.jdrupes.vmoperator.manager/src/org/jdrupes/vmoperator/manager/K8s.java rename to org.jdrupes.vmoperator.util/src/org/jdrupes/vmoperator/util/K8s.java index efc304e..539a870 100644 --- a/org.jdrupes.vmoperator.manager/src/org/jdrupes/vmoperator/manager/K8s.java +++ b/org.jdrupes.vmoperator.util/src/org/jdrupes/vmoperator/util/K8s.java @@ -16,7 +16,7 @@ * along with this program. If not, see . */ -package org.jdrupes.vmoperator.manager; +package org.jdrupes.vmoperator.util; import io.kubernetes.client.common.KubernetesListObject; import io.kubernetes.client.common.KubernetesObject; From f50a4af46c9fd425bf8f6d26020dda004f87b496 Mon Sep 17 00:00:00 2001 From: "Michael N. Lipp" Date: Thu, 14 Sep 2023 09:57:20 +0200 Subject: [PATCH 02/18] Namespace can be set for development. --- org.jdrupes.vmoperator.runner.qemu/config-sample.yaml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/org.jdrupes.vmoperator.runner.qemu/config-sample.yaml b/org.jdrupes.vmoperator.runner.qemu/config-sample.yaml index 3e18871..461e79b 100644 --- a/org.jdrupes.vmoperator.runner.qemu/config-sample.yaml +++ b/org.jdrupes.vmoperator.runner.qemu/config-sample.yaml @@ -22,6 +22,11 @@ # the first time. Subsequent starts use the copy unless this option is set. # "updateTemplate": false + # The namespace that this runner runs in. Usually obtained from + # /var/run/secrets/kubernetes.io/serviceaccount/namespace. Should only + # be set when starting the runner during development e.g. from the IDE. + # "namespace": ... + # Define the VM (required) "vm": # The VM's name (required) From 74e05ce02326fc6ab022004956ab0b1f984afd2b Mon Sep 17 00:00:00 2001 From: "Michael N. Lipp" Date: Thu, 14 Sep 2023 14:14:07 +0200 Subject: [PATCH 03/18] Provide "Running" condition in status. --- deploy/crds/vms-crd.yaml | 34 ++- .../build.gradle | 2 + .../vmoperator/runner/qemu/Runner.java | 46 +++- .../vmoperator/runner/qemu/StatusUpdater.java | 244 ++++++++++++++++++ .../runner/qemu/events/RunnerStateChange.java | 42 ++- 5 files changed, 353 insertions(+), 15 deletions(-) create mode 100644 org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/StatusUpdater.java diff --git a/deploy/crds/vms-crd.yaml b/deploy/crds/vms-crd.yaml index 0b24bd6..713b785 100644 --- a/deploy/crds/vms-crd.yaml +++ b/deploy/crds/vms-crd.yaml @@ -9,6 +9,8 @@ spec: - name: v1 served: true storage: true + subresources: + status: {} schema: openAPIV3Schema: type: object @@ -1372,10 +1374,28 @@ spec: - vm status: type: object - properties: + default: {} + properties: + cpus: + description: >- + Number of CPUs currently in use. + type: integer + default: 0 + ram: + description: >- + Amount of memory in use. + type: string + default: "0" conditions: description: >- List of component conditions observed + default: + - type: Running + status: "False" + observedGeneration: 1 + lastTransitionTime: "1970-01-01T00:00:00Z" + reason: Creation + message: "Creation of CR" type: array items: type: object @@ -1383,6 +1403,12 @@ spec: Information about the condition of a component. See https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#spec-and-status and https://github.com/kubernetes/apimachinery/blob/release-1.23/pkg/apis/meta/v1/types.go#L1432-L1492 + required: + - type + - status + - lastTransitionTime + - reason + - message properties: type: type: string @@ -1428,12 +1454,6 @@ spec: Message is a human readable message indicating details about the transition. This may be an empty string. default: "" - required: - - type - - status - - lastTransitionTime - - reason - - message # either Namespaced or Cluster scope: Namespaced names: diff --git a/org.jdrupes.vmoperator.runner.qemu/build.gradle b/org.jdrupes.vmoperator.runner.qemu/build.gradle index 9f48a58..3e693bb 100644 --- a/org.jdrupes.vmoperator.runner.qemu/build.gradle +++ b/org.jdrupes.vmoperator.runner.qemu/build.gradle @@ -18,6 +18,8 @@ dependencies { implementation 'commons-cli:commons-cli:1.5.0' implementation 'org.freemarker:freemarker:[2.3.32,2.4)' implementation 'com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:[2.15.1,3]' + + runtimeOnly 'org.slf4j:slf4j-jdk14:[2.0.7,3)' } application { diff --git a/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/Runner.java b/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/Runner.java index 1370e18..2e8216b 100644 --- a/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/Runner.java +++ b/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/Runner.java @@ -66,6 +66,7 @@ import org.jgrapes.core.annotation.Handler; import org.jgrapes.core.events.Start; import org.jgrapes.core.events.Started; import org.jgrapes.core.events.Stop; +import org.jgrapes.core.internal.EventProcessor; import org.jgrapes.io.NioDispatcher; import org.jgrapes.io.events.Input; import org.jgrapes.io.events.ProcessExited; @@ -90,6 +91,13 @@ import org.jgrapes.util.events.WatchFile; * * ![Runner state diagram](RunnerStates.svg) * + * The {@link Runner} associates an {@link EventProcessor} with the + * {@link Start} event. This "runner event processor" must be used + * for all events related to the application level function. Components + * that handle events from other sources (and thus event processors) + * must fire any resulting events on the runner event processor in order + * to maintain synchronization. + * * @startuml RunnerStates.svg * [*] --> Initializing * Initializing -> Initializing: InitialConfiguration/configure Runner @@ -149,8 +157,13 @@ import org.jgrapes.util.events.WatchFile; * error --> terminate * StartingProcess --> terminate: ProcessExited * + * state Stopped { + * state stopped <> * - * terminated --> [*] + * stopped --> [*] + * } + * + * terminated --> stopped * * @enduml * @@ -211,6 +224,7 @@ public class Runner extends Component { attach(new ProcessManager(channel())); attach(new SocketConnector(channel())); attach(qemuMonitor = new QemuMonitor(channel())); + attach(new StatusUpdater(channel())); // Configuration store with file in /etc/opt (default) File config = new File(cmdLine.getOptionValue('c', @@ -352,6 +366,11 @@ public class Runner extends Component { return; } + // Make sure to use thread specific client + // https://github.com/kubernetes-client/java/issues/100 + io.kubernetes.client.openapi.Configuration.setDefaultApiClient(null); + + // Prepare specific event pipeline to avoid concurrency. rep = newEventPipeline(); event.setAssociated(EventPipeline.class, rep); try { @@ -387,7 +406,8 @@ public class Runner extends Component { @Handler public void onStarted(Started event) { state = State.STARTING; - fire(new RunnerStateChange(state)); + rep.fire(new RunnerStateChange(state, "RunnerStarted", + "Runner has been started")); // Start first process if (config.vm.useTpm && swtpmDefinition != null) { startProcess(swtpmDefinition); @@ -476,7 +496,7 @@ public class Runner extends Component { */ @Handler public void onMonitorReady(MonitorReady event) { - fire(new RunnerConfigurationUpdate(config, state)); + rep.fire(new RunnerConfigurationUpdate(config, state)); } /** @@ -489,7 +509,8 @@ public class Runner extends Component { if (state == State.STARTING) { fire(new MonitorCommand(new QmpCont())); state = State.RUNNING; - fire(new RunnerStateChange(state)); + rep.fire(new RunnerStateChange(state, "VmStarted", + "Qemu has been configured and is continuing")); } } @@ -524,9 +545,22 @@ public class Runner extends Component { * @param event the event */ @Handler(priority = 10_000) - public void onStop(Stop event) { + public void onStopFirst(Stop event) { state = State.TERMINATING; - fire(new RunnerStateChange(state)); + rep.fire(new RunnerStateChange(state, "VmTerminating", + "The VM is being shut down")); + } + + /** + * On stop. + * + * @param event the event + */ + @Handler(priority = -10_000) + public void onStopLast(Stop event) { + state = State.STOPPED; + rep.fire(new RunnerStateChange(state, "VmStopped", + "The VM has been shut down")); } private void shutdown() { diff --git a/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/StatusUpdater.java b/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/StatusUpdater.java new file mode 100644 index 0000000..e45b5e6 --- /dev/null +++ b/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/StatusUpdater.java @@ -0,0 +1,244 @@ +/* + * VM-Operator + * Copyright (C) 2023 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 + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package org.jdrupes.vmoperator.runner.qemu; + +import com.google.gson.JsonObject; +import io.kubernetes.client.openapi.ApiException; +import io.kubernetes.client.openapi.apis.ApiextensionsV1Api; +import io.kubernetes.client.openapi.models.V1CustomResourceDefinitionVersion; +import io.kubernetes.client.util.Config; +import io.kubernetes.client.util.generic.dynamic.DynamicKubernetesApi; +import io.kubernetes.client.util.generic.dynamic.DynamicKubernetesObject; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.time.Instant; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import java.util.logging.Level; +import org.jdrupes.vmoperator.runner.qemu.events.RunnerConfigurationUpdate; +import org.jdrupes.vmoperator.runner.qemu.events.RunnerStateChange; +import org.jdrupes.vmoperator.runner.qemu.events.RunnerStateChange.State; +import static org.jdrupes.vmoperator.util.Constants.VM_OP_CRD_NAME; +import static org.jdrupes.vmoperator.util.Constants.VM_OP_GROUP; +import org.jgrapes.core.Channel; +import org.jgrapes.core.Component; +import org.jgrapes.core.annotation.Handler; +import org.jgrapes.core.events.Start; +import org.jgrapes.util.events.ConfigurationUpdate; +import org.jgrapes.util.events.InitialConfiguration; + +/** + * Updates the CR status. + */ +public class StatusUpdater extends Component { + + private static final Set RUNNING_STATES + = Set.of(State.RUNNING, State.TERMINATING); + + private String namespace; + private String vmName; + private DynamicKubernetesApi vmCrApi; + private long observedGeneration; + + /** + * Instantiates a new status updater. + * + * @param componentChannel the component channel + */ + public StatusUpdater(Channel componentChannel) { + super(componentChannel); + } + + /** + * On configuration update. + * + * @param event the event + */ + @Handler + @SuppressWarnings("unchecked") + public void onConfigurationUpdate(ConfigurationUpdate event) { + event.structured("/Runner").ifPresent(c -> { + if (event instanceof InitialConfiguration) { + namespace = (String) c.get("namespace"); + updateNamespace(); + vmName = Optional.ofNullable((Map) c.get("vm")) + .map(vm -> vm.get("name")).orElse(null); + } + }); + } + + private void updateNamespace() { + if (namespace == null) { + var path = Path + .of("/var/run/secrets/kubernetes.io/serviceaccount/namespace"); + if (Files.isReadable(path)) { + try { + namespace = Files.lines(path).findFirst().orElse(null); + } catch (IOException e) { + logger.log(Level.WARNING, e, + () -> "Cannot read namespace."); + } + } + } + if (namespace == null) { + logger.warning(() -> "Namespace is unknown, some functions" + + " won't be available."); + } + } + + /** + * Handle the start event. + * + * @param event the event + */ + @Handler + @SuppressWarnings({ "PMD.DataflowAnomalyAnalysis", + "PMD.AvoidInstantiatingObjectsInLoops" }) + public void onStart(Start event) { + try { + var client = Config.defaultClient(); + var extsApi = new ApiextensionsV1Api(client); + var crds = extsApi.listCustomResourceDefinition(null, null, null, + "metadata.name=" + VM_OP_CRD_NAME, null, null, null, + null, null, null); + if (crds.getItems().isEmpty()) { + logger.warning(() -> "CRD is unknown, status will not" + + " be updated."); + return; + } + var crd = crds.getItems().get(0); + if (crd.getSpec().getVersions().stream() + .filter(v -> v.getSubresources() == null).findAny() + .isPresent()) { + logger.warning(() -> "You are using an old version of the CRD," + + " status will not be updated."); + return; + } + var crdPlural = crd.getSpec().getNames().getPlural(); + var vmOpApiVersions = crd.getSpec().getVersions().stream() + .map(V1CustomResourceDefinitionVersion::getName).toList(); + for (var apiVer : vmOpApiVersions) { + var api = new DynamicKubernetesApi(VM_OP_GROUP, apiVer, + crdPlural, client); + var res = api.get(namespace, vmName); + if (res.isSuccess()) { + vmCrApi = api; + observedGeneration + = res.getObject().getMetadata().getGeneration(); + break; + } + } + if (vmCrApi == null) { + logger.warning(() -> "VM's CR is unknown, status will not" + + " be updated."); + } + } catch (IOException | ApiException e) { + logger.log(Level.WARNING, e, () -> "Cannot access kubernetes: " + + e.getMessage()); + } + } + + @SuppressWarnings("PMD.AvoidDuplicateLiterals") + private JsonObject currentStatus(DynamicKubernetesObject vmCr) { + return vmCr.getRaw().getAsJsonObject("status").deepCopy(); + } + + /** + * On runner state changed. + * + * @param event the event + 8 * @throws ApiException the api exception + */ + @Handler + public void onRunnerStateChanged(RunnerStateChange event) + throws ApiException { + if (vmCrApi == null) { + return; + } + var vmCr = vmCrApi.get(namespace, vmName) + .throwsApiException().getObject(); + vmCrApi.updateStatus(vmCr, from -> { + JsonObject status = currentStatus(from); + status.getAsJsonArray("conditions").asList().stream() + .map(cond -> (JsonObject) cond) + .forEach(cond -> { + if ("Running".equals(cond.get("type").getAsString())) { + updateRunningCondition(event, from, cond); + } + }); + return status; + }); + } + + private void updateRunningCondition(RunnerStateChange event, + DynamicKubernetesObject from, JsonObject cond) { + boolean reportedRunning + = "True".equals(cond.get("status").getAsString()); + if (RUNNING_STATES.contains(event.state()) + && !reportedRunning) { + cond.addProperty("status", "True"); + cond.addProperty("lastTransitionTime", + Instant.now().toString()); + } + if (!RUNNING_STATES.contains(event.state()) + && reportedRunning) { + cond.addProperty("status", "False"); + cond.addProperty("lastTransitionTime", + Instant.now().toString()); + } + cond.addProperty("reason", event.reason()); + cond.addProperty("message", event.message()); + cond.addProperty("observedGeneration", + from.getMetadata().getGeneration()); + } + + /** + * On runner configuration update. + * + * @param event the event + * @throws ApiException + */ + @Handler + public void onRunnerConfigurationUpdate(RunnerConfigurationUpdate event) + throws ApiException { + if (vmCrApi == null) { + return; + } + // A change of the runner configuration is typically caused + // by a new version of the CR. So we observe the new CR. + var vmCr = vmCrApi.get(namespace, vmName).throwsApiException() + .getObject(); + if (vmCr.getMetadata().getGeneration() == observedGeneration) { + return; + } + vmCrApi.updateStatus(vmCr, from -> { + JsonObject status = currentStatus(from); + status.getAsJsonArray("conditions").asList().stream() + .map(cond -> (JsonObject) cond) + .filter( + cond -> "Running".equals(cond.get("type").getAsString())) + .forEach(cond -> cond.addProperty("observedGeneration", + from.getMetadata().getGeneration())); + return status; + }); + } + +} diff --git a/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/events/RunnerStateChange.java b/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/events/RunnerStateChange.java index a827fa4..46fa1f8 100644 --- a/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/events/RunnerStateChange.java +++ b/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/events/RunnerStateChange.java @@ -19,6 +19,7 @@ package org.jdrupes.vmoperator.runner.qemu.events; import org.jgrapes.core.Channel; +import org.jgrapes.core.Components; import org.jgrapes.core.Event; /** @@ -30,19 +31,24 @@ public class RunnerStateChange extends Event { * The state. */ public enum State { - INITIALIZING, STARTING, RUNNING, TERMINATING + INITIALIZING, STARTING, RUNNING, TERMINATING, STOPPED } private final State state; + private final String reason; + private final String message; /** * Instantiates a new runner state change. * * @param channels the channels */ - public RunnerStateChange(State state, Channel... channels) { + public RunnerStateChange(State state, String reason, String message, + Channel... channels) { super(channels); this.state = state; + this.reason = reason; + this.message = message; } /** @@ -53,4 +59,36 @@ public class RunnerStateChange extends Event { public State state() { return state; } + + /** + * Gets the reason. + * + * @return the reason + */ + public String reason() { + return reason; + } + + /** + * Gets the message. + * + * @return the message + */ + public String message() { + return message; + } + + @Override + public String toString() { + StringBuilder builder = new StringBuilder(); + builder.append(Components.objectName(this)) + .append(" [").append(state).append(": ").append(reason); + if (channels() != null) { + builder.append(", channels="); + builder.append(Channel.toString(channels())); + } + builder.append(']'); + return builder.toString(); + } + } From 9ba0dd8dc3c819b77e2c3cb353c5e7979b5d20c3 Mon Sep 17 00:00:00 2001 From: "Michael N. Lipp" Date: Thu, 14 Sep 2023 14:45:06 +0200 Subject: [PATCH 04/18] Combine constants for easy use in templates. --- .../org/jdrupes/vmoperator/manager/ConfigMapReconciler.java | 2 +- .../src/org/jdrupes/vmoperator/manager/Constants.java | 2 +- .../src/org/jdrupes/vmoperator/manager/Manager.java | 2 +- .../src/org/jdrupes/vmoperator/manager/Reconciler.java | 2 +- .../src/org/jdrupes/vmoperator/manager/VmWatcher.java | 6 +++--- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/org.jdrupes.vmoperator.manager/src/org/jdrupes/vmoperator/manager/ConfigMapReconciler.java b/org.jdrupes.vmoperator.manager/src/org/jdrupes/vmoperator/manager/ConfigMapReconciler.java index 754669d..15bdb76 100644 --- a/org.jdrupes.vmoperator.manager/src/org/jdrupes/vmoperator/manager/ConfigMapReconciler.java +++ b/org.jdrupes.vmoperator.manager/src/org/jdrupes/vmoperator/manager/ConfigMapReconciler.java @@ -33,7 +33,7 @@ import java.io.StringWriter; import java.util.Map; import java.util.logging.Logger; import static org.jdrupes.vmoperator.manager.Constants.APP_NAME; -import static org.jdrupes.vmoperator.util.Constants.VM_OP_NAME; +import static org.jdrupes.vmoperator.manager.Constants.VM_OP_NAME; import org.jdrupes.vmoperator.util.K8s; import org.yaml.snakeyaml.LoaderOptions; import org.yaml.snakeyaml.Yaml; diff --git a/org.jdrupes.vmoperator.manager/src/org/jdrupes/vmoperator/manager/Constants.java b/org.jdrupes.vmoperator.manager/src/org/jdrupes/vmoperator/manager/Constants.java index dd2774e..fabae37 100644 --- a/org.jdrupes.vmoperator.manager/src/org/jdrupes/vmoperator/manager/Constants.java +++ b/org.jdrupes.vmoperator.manager/src/org/jdrupes/vmoperator/manager/Constants.java @@ -21,7 +21,7 @@ package org.jdrupes.vmoperator.manager; /** * Some constants. */ -public class Constants { +public class Constants extends org.jdrupes.vmoperator.util.Constants { /** The Constant APP_NAME. */ public static final String APP_NAME = "vm-runner"; diff --git a/org.jdrupes.vmoperator.manager/src/org/jdrupes/vmoperator/manager/Manager.java b/org.jdrupes.vmoperator.manager/src/org/jdrupes/vmoperator/manager/Manager.java index e26b813..5b55ca8 100644 --- a/org.jdrupes.vmoperator.manager/src/org/jdrupes/vmoperator/manager/Manager.java +++ b/org.jdrupes.vmoperator.manager/src/org/jdrupes/vmoperator/manager/Manager.java @@ -30,7 +30,7 @@ import org.apache.commons.cli.CommandLineParser; import org.apache.commons.cli.DefaultParser; import org.apache.commons.cli.Option; import org.apache.commons.cli.Options; -import static org.jdrupes.vmoperator.util.Constants.VM_OP_NAME; +import static org.jdrupes.vmoperator.manager.Constants.VM_OP_NAME; import org.jdrupes.vmoperator.util.FsdUtils; import org.jgrapes.core.Component; import org.jgrapes.core.Components; diff --git a/org.jdrupes.vmoperator.manager/src/org/jdrupes/vmoperator/manager/Reconciler.java b/org.jdrupes.vmoperator.manager/src/org/jdrupes/vmoperator/manager/Reconciler.java index 94b79dc..ea43adf 100644 --- a/org.jdrupes.vmoperator.manager/src/org/jdrupes/vmoperator/manager/Reconciler.java +++ b/org.jdrupes.vmoperator.manager/src/org/jdrupes/vmoperator/manager/Reconciler.java @@ -43,8 +43,8 @@ import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Optional; +import static org.jdrupes.vmoperator.manager.Constants.VM_OP_GROUP; import org.jdrupes.vmoperator.manager.VmDefChanged.Type; -import static org.jdrupes.vmoperator.util.Constants.VM_OP_GROUP; import org.jdrupes.vmoperator.util.Convertions; import org.jdrupes.vmoperator.util.ExtendedObjectWrapper; import org.jdrupes.vmoperator.util.GsonPtr; diff --git a/org.jdrupes.vmoperator.manager/src/org/jdrupes/vmoperator/manager/VmWatcher.java b/org.jdrupes.vmoperator.manager/src/org/jdrupes/vmoperator/manager/VmWatcher.java index 7ecce4c..ab2dc4a 100644 --- a/org.jdrupes.vmoperator.manager/src/org/jdrupes/vmoperator/manager/VmWatcher.java +++ b/org.jdrupes.vmoperator.manager/src/org/jdrupes/vmoperator/manager/VmWatcher.java @@ -45,10 +45,10 @@ import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.logging.Level; import static org.jdrupes.vmoperator.manager.Constants.APP_NAME; +import static org.jdrupes.vmoperator.manager.Constants.VM_OP_GROUP; +import static org.jdrupes.vmoperator.manager.Constants.VM_OP_KIND_VM; +import static org.jdrupes.vmoperator.manager.Constants.VM_OP_NAME; import org.jdrupes.vmoperator.manager.VmDefChanged.Type; -import static org.jdrupes.vmoperator.util.Constants.VM_OP_GROUP; -import static org.jdrupes.vmoperator.util.Constants.VM_OP_KIND_VM; -import static org.jdrupes.vmoperator.util.Constants.VM_OP_NAME; import org.jgrapes.core.Channel; import org.jgrapes.core.Component; import org.jgrapes.core.Components; From a045cba998fd125746f56ff852daa75562cd84c8 Mon Sep 17 00:00:00 2001 From: "Michael N. Lipp" Date: Thu, 14 Sep 2023 14:55:50 +0200 Subject: [PATCH 05/18] Fix (make use of) transient dependencies. --- org.jdrupes.vmoperator.manager/build.gradle | 2 -- org.jdrupes.vmoperator.runner.qemu/build.gradle | 1 - org.jdrupes.vmoperator.util/build.gradle | 4 ++-- 3 files changed, 2 insertions(+), 5 deletions(-) diff --git a/org.jdrupes.vmoperator.manager/build.gradle b/org.jdrupes.vmoperator.manager/build.gradle index 2c2dd7f..3a6dda3 100644 --- a/org.jdrupes.vmoperator.manager/build.gradle +++ b/org.jdrupes.vmoperator.manager/build.gradle @@ -16,8 +16,6 @@ dependencies { implementation project(':org.jdrupes.vmoperator.util') implementation 'commons-cli:commons-cli:1.5.0' - implementation 'org.freemarker:freemarker:[2.3.32,2.4)' - implementation 'io.kubernetes:client-java:[18.0.0,19)' runtimeOnly 'com.electronwill.night-config:yaml:[3.6.7,3.7)' runtimeOnly 'org.slf4j:slf4j-jdk14:[2.0.7,3)' diff --git a/org.jdrupes.vmoperator.runner.qemu/build.gradle b/org.jdrupes.vmoperator.runner.qemu/build.gradle index 3e693bb..69f527f 100644 --- a/org.jdrupes.vmoperator.runner.qemu/build.gradle +++ b/org.jdrupes.vmoperator.runner.qemu/build.gradle @@ -16,7 +16,6 @@ dependencies { implementation project(':org.jdrupes.vmoperator.util') implementation 'commons-cli:commons-cli:1.5.0' - implementation 'org.freemarker:freemarker:[2.3.32,2.4)' implementation 'com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:[2.15.1,3]' runtimeOnly 'org.slf4j:slf4j-jdk14:[2.0.7,3)' diff --git a/org.jdrupes.vmoperator.util/build.gradle b/org.jdrupes.vmoperator.util/build.gradle index f20afcb..3c772a8 100644 --- a/org.jdrupes.vmoperator.util/build.gradle +++ b/org.jdrupes.vmoperator.util/build.gradle @@ -9,6 +9,6 @@ plugins { } dependencies { - implementation 'org.freemarker:freemarker:[2.3.32,2.4)' - implementation 'io.kubernetes:client-java:[18.0.0,19)' + api 'org.freemarker:freemarker:[2.3.32,2.4)' + api 'io.kubernetes:client-java:[18.0.0,19)' } From 9d29266907f5c53448cf29feb8217a7d4bc61a2b Mon Sep 17 00:00:00 2001 From: "Michael N. Lipp" Date: Thu, 14 Sep 2023 18:59:18 +0200 Subject: [PATCH 06/18] Report current RAM usage in status. --- .../vmoperator/runner/qemu/StatusUpdater.java | 128 +++++++++++------- .../qemu/events/BalloonChangeEvent.java | 47 +++++++ .../runner/qemu/events/MonitorEvent.java | 5 +- 3 files changed, 130 insertions(+), 50 deletions(-) create mode 100644 org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/events/BalloonChangeEvent.java diff --git a/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/StatusUpdater.java b/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/StatusUpdater.java index e45b5e6..3523af9 100644 --- a/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/StatusUpdater.java +++ b/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/StatusUpdater.java @@ -19,6 +19,8 @@ package org.jdrupes.vmoperator.runner.qemu; import com.google.gson.JsonObject; +import io.kubernetes.client.custom.Quantity; +import io.kubernetes.client.custom.Quantity.Format; import io.kubernetes.client.openapi.ApiException; import io.kubernetes.client.openapi.apis.ApiextensionsV1Api; import io.kubernetes.client.openapi.models.V1CustomResourceDefinitionVersion; @@ -26,6 +28,7 @@ import io.kubernetes.client.util.Config; import io.kubernetes.client.util.generic.dynamic.DynamicKubernetesApi; import io.kubernetes.client.util.generic.dynamic.DynamicKubernetesObject; import java.io.IOException; +import java.math.BigDecimal; import java.nio.file.Files; import java.nio.file.Path; import java.time.Instant; @@ -33,11 +36,13 @@ import java.util.Map; import java.util.Optional; import java.util.Set; import java.util.logging.Level; +import org.jdrupes.vmoperator.runner.qemu.events.BalloonChangeEvent; import org.jdrupes.vmoperator.runner.qemu.events.RunnerConfigurationUpdate; import org.jdrupes.vmoperator.runner.qemu.events.RunnerStateChange; import org.jdrupes.vmoperator.runner.qemu.events.RunnerStateChange.State; import static org.jdrupes.vmoperator.util.Constants.VM_OP_CRD_NAME; import static org.jdrupes.vmoperator.util.Constants.VM_OP_GROUP; +import org.jdrupes.vmoperator.util.GsonPtr; import org.jgrapes.core.Channel; import org.jgrapes.core.Component; import org.jgrapes.core.annotation.Handler; @@ -161,55 +166,6 @@ public class StatusUpdater extends Component { return vmCr.getRaw().getAsJsonObject("status").deepCopy(); } - /** - * On runner state changed. - * - * @param event the event - 8 * @throws ApiException the api exception - */ - @Handler - public void onRunnerStateChanged(RunnerStateChange event) - throws ApiException { - if (vmCrApi == null) { - return; - } - var vmCr = vmCrApi.get(namespace, vmName) - .throwsApiException().getObject(); - vmCrApi.updateStatus(vmCr, from -> { - JsonObject status = currentStatus(from); - status.getAsJsonArray("conditions").asList().stream() - .map(cond -> (JsonObject) cond) - .forEach(cond -> { - if ("Running".equals(cond.get("type").getAsString())) { - updateRunningCondition(event, from, cond); - } - }); - return status; - }); - } - - private void updateRunningCondition(RunnerStateChange event, - DynamicKubernetesObject from, JsonObject cond) { - boolean reportedRunning - = "True".equals(cond.get("status").getAsString()); - if (RUNNING_STATES.contains(event.state()) - && !reportedRunning) { - cond.addProperty("status", "True"); - cond.addProperty("lastTransitionTime", - Instant.now().toString()); - } - if (!RUNNING_STATES.contains(event.state()) - && reportedRunning) { - cond.addProperty("status", "False"); - cond.addProperty("lastTransitionTime", - Instant.now().toString()); - } - cond.addProperty("reason", event.reason()); - cond.addProperty("message", event.message()); - cond.addProperty("observedGeneration", - from.getMetadata().getGeneration()); - } - /** * On runner configuration update. * @@ -241,4 +197,78 @@ public class StatusUpdater extends Component { }); } + /** + * On runner state changed. + * + * @param event the event + * @throws ApiException the api exception + */ + @Handler + public void onRunnerStateChanged(RunnerStateChange event) + throws ApiException { + if (vmCrApi == null) { + return; + } + var vmCr = vmCrApi.get(namespace, vmName) + .throwsApiException().getObject(); + vmCrApi.updateStatus(vmCr, from -> { + JsonObject status = currentStatus(from); + status.getAsJsonArray("conditions").asList().stream() + .map(cond -> (JsonObject) cond) + .forEach(cond -> { + if ("Running".equals(cond.get("type").getAsString())) { + updateRunningCondition(event, from, cond); + } + }); + if (event.state() == State.STARTING) { + status.addProperty("ram", GsonPtr.to(from.getRaw()) + .getAsString("spec", "vm", "maximumRam").orElse("0")); + } + return status; + }); + } + + private void updateRunningCondition(RunnerStateChange event, + DynamicKubernetesObject from, JsonObject cond) { + boolean reportedRunning + = "True".equals(cond.get("status").getAsString()); + if (RUNNING_STATES.contains(event.state()) + && !reportedRunning) { + cond.addProperty("status", "True"); + cond.addProperty("lastTransitionTime", + Instant.now().toString()); + } + if (!RUNNING_STATES.contains(event.state()) + && reportedRunning) { + cond.addProperty("status", "False"); + cond.addProperty("lastTransitionTime", + Instant.now().toString()); + } + cond.addProperty("reason", event.reason()); + cond.addProperty("message", event.message()); + cond.addProperty("observedGeneration", + from.getMetadata().getGeneration()); + } + + /** + * On ballon change. + * + * @param event the event + * @throws ApiException + */ + @Handler + public void onBallonChange(BalloonChangeEvent event) throws ApiException { + if (vmCrApi == null) { + return; + } + var vmCr + = vmCrApi.get(namespace, vmName).throwsApiException().getObject(); + vmCrApi.updateStatus(vmCr, from -> { + JsonObject status = currentStatus(from); + status.addProperty("ram", + new Quantity(new BigDecimal(event.size()), Format.BINARY_SI) + .toSuffixedString()); + return status; + }); + } } diff --git a/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/events/BalloonChangeEvent.java b/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/events/BalloonChangeEvent.java new file mode 100644 index 0000000..9cc67c8 --- /dev/null +++ b/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/events/BalloonChangeEvent.java @@ -0,0 +1,47 @@ +/* + * VM-Operator + * Copyright (C) 2023 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 + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package org.jdrupes.vmoperator.runner.qemu.events; + +import com.fasterxml.jackson.databind.JsonNode; +import java.math.BigInteger; + +/** + * Signals a change of the balloon. + */ +public class BalloonChangeEvent extends MonitorEvent { + + /** + * Instantiates a new tray moved. + * + * @param kind the kind + * @param data the data + */ + public BalloonChangeEvent(Kind kind, JsonNode data) { + super(kind, data); + } + + /** + * Returns the actual value. + * + * @return the actual value + */ + public BigInteger size() { + return new BigInteger(data().get("actual").asText()); + } +} diff --git a/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/events/MonitorEvent.java b/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/events/MonitorEvent.java index 28d2e4c..72647a1 100644 --- a/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/events/MonitorEvent.java +++ b/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/events/MonitorEvent.java @@ -32,7 +32,7 @@ public class MonitorEvent extends Event { * The kind of monitor event. */ public enum Kind { - READY, POWERDOWN, DEVICE_TRAY_MOVED + READY, POWERDOWN, DEVICE_TRAY_MOVED, BALLOON_CHANGE } private final Kind kind; @@ -55,6 +55,9 @@ public class MonitorEvent extends Event { case DEVICE_TRAY_MOVED: return Optional .of(new TrayMovedEvent(kind, response.get("data"))); + case BALLOON_CHANGE: + return Optional + .of(new BalloonChangeEvent(kind, response.get("data"))); default: return Optional .of(new MonitorEvent(kind, response.get("data"))); From 86ea626dd54d2562b8ab15ba6699cb6772cf5f0f Mon Sep 17 00:00:00 2001 From: "Michael N. Lipp" Date: Thu, 14 Sep 2023 18:59:46 +0200 Subject: [PATCH 07/18] Update. --- .../.settings/org.eclipse.jdt.core.prefs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/org.jdrupes.vmoperator.util/.settings/org.eclipse.jdt.core.prefs b/org.jdrupes.vmoperator.util/.settings/org.eclipse.jdt.core.prefs index 17fc9079..f78a58f 100644 --- a/org.jdrupes.vmoperator.util/.settings/org.eclipse.jdt.core.prefs +++ b/org.jdrupes.vmoperator.util/.settings/org.eclipse.jdt.core.prefs @@ -1,5 +1,5 @@ # -#Thu Sep 14 09:53:57 CEST 2023 +#Thu Sep 14 18:57:03 CEST 2023 org.eclipse.jdt.core.formatter.insert_space_after_ellipsis=insert org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_declarations=insert org.eclipse.jdt.core.formatter.insert_new_line_in_empty_annotation_declaration=insert @@ -22,12 +22,12 @@ org.eclipse.jdt.core.formatter.blank_lines_after_package=1 org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_local_declarations=insert org.eclipse.jdt.core.formatter.alignment_for_arguments_in_enum_constant=16 org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_parameterized_type_reference=do not insert -org.eclipse.jdt.core.formatter.wrap_before_or_operator_multicatch=true org.eclipse.jdt.core.formatter.comment.indent_root_tags=false +org.eclipse.jdt.core.formatter.wrap_before_or_operator_multicatch=true org.eclipse.jdt.core.formatter.enabling_tag=@formatter\:on org.eclipse.jdt.core.formatter.comment.count_line_length_from_starting_position=false -org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_method_declaration=16 org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_parameter=do not insert +org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_method_declaration=16 org.eclipse.jdt.core.formatter.keep_then_statement_on_same_line=false org.eclipse.jdt.core.formatter.insert_space_after_comma_in_explicitconstructorcall_arguments=insert org.eclipse.jdt.core.formatter.insert_space_after_prefix_operator=do not insert @@ -43,8 +43,8 @@ org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_invoc org.eclipse.jdt.core.formatter.insert_space_after_assignment_operator=insert org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_type_declaration=insert org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_for=do not insert -org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_local_variable=insert org.eclipse.jdt.core.formatter.comment.preserve_white_space_between_code_and_line_comments=true +org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_local_variable=insert org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_invocation=do not insert org.eclipse.jdt.core.formatter.alignment_for_union_type_in_multicatch=16 org.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_method_body=0 @@ -63,8 +63,8 @@ org.eclipse.jdt.core.formatter.alignment_for_type_parameters=16 org.eclipse.jdt.core.compiler.problem.assertIdentifier=error org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_block_comment=false org.eclipse.jdt.core.formatter.insert_new_line_at_end_of_file_if_missing=do not insert -org.eclipse.jdt.core.formatter.insert_space_after_comma_in_array_initializer=insert org.eclipse.jdt.core.formatter.insert_space_before_unary_operator=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_array_initializer=insert org.eclipse.jdt.core.formatter.format_line_comment_starting_on_first_column=false org.eclipse.jdt.core.formatter.parentheses_positions_in_annotation=common_lines org.eclipse.jdt.core.formatter.insert_space_before_ellipsis=do not insert From ce786ea70db2e9737edcec86b803a70929fcc88b Mon Sep 17 00:00:00 2001 From: "Michael N. Lipp" Date: Thu, 14 Sep 2023 19:03:45 +0200 Subject: [PATCH 08/18] Clear RAM usage on stop. --- .../src/org/jdrupes/vmoperator/runner/qemu/StatusUpdater.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/StatusUpdater.java b/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/StatusUpdater.java index 3523af9..b72a478 100644 --- a/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/StatusUpdater.java +++ b/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/StatusUpdater.java @@ -223,6 +223,8 @@ public class StatusUpdater extends Component { if (event.state() == State.STARTING) { status.addProperty("ram", GsonPtr.to(from.getRaw()) .getAsString("spec", "vm", "maximumRam").orElse("0")); + } else if (event.state() == State.STOPPED) { + status.addProperty("ram", "0"); } return status; }); From 9c60153c8b61873172b11575d94fa7f17e86e010 Mon Sep 17 00:00:00 2001 From: "Michael N. Lipp" Date: Thu, 14 Sep 2023 20:57:48 +0200 Subject: [PATCH 09/18] Set explicit encoding. --- buildSrc/.settings/org.eclipse.core.resources.prefs | 2 ++ buildSrc/.settings/org.eclipse.core.runtime.prefs | 2 ++ 2 files changed, 4 insertions(+) create mode 100644 buildSrc/.settings/org.eclipse.core.resources.prefs create mode 100644 buildSrc/.settings/org.eclipse.core.runtime.prefs diff --git a/buildSrc/.settings/org.eclipse.core.resources.prefs b/buildSrc/.settings/org.eclipse.core.resources.prefs new file mode 100644 index 0000000..99f26c0 --- /dev/null +++ b/buildSrc/.settings/org.eclipse.core.resources.prefs @@ -0,0 +1,2 @@ +eclipse.preferences.version=1 +encoding/=UTF-8 diff --git a/buildSrc/.settings/org.eclipse.core.runtime.prefs b/buildSrc/.settings/org.eclipse.core.runtime.prefs new file mode 100644 index 0000000..5a0ad22 --- /dev/null +++ b/buildSrc/.settings/org.eclipse.core.runtime.prefs @@ -0,0 +1,2 @@ +eclipse.preferences.version=1 +line.separator=\n From 77a4a274613b8de50e7bbda7d1b8bd521c310abc Mon Sep 17 00:00:00 2001 From: "Michael N. Lipp" Date: Thu, 14 Sep 2023 21:27:12 +0200 Subject: [PATCH 10/18] Update. --- .settings/org.eclipse.buildship.core.prefs | 2 +- .../.settings/org.eclipse.jdt.core.prefs | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.settings/org.eclipse.buildship.core.prefs b/.settings/org.eclipse.buildship.core.prefs index 33d02f4..d0fed22 100644 --- a/.settings/org.eclipse.buildship.core.prefs +++ b/.settings/org.eclipse.buildship.core.prefs @@ -5,7 +5,7 @@ connection.gradle.distribution=GRADLE_DISTRIBUTION(WRAPPER) connection.project.dir= eclipse.preferences.version=1 gradle.user.home= -java.home=/usr/lib/jvm/java-1.8.0-openjdk-1.8.0.362.b09-2.fc37.x86_64 +java.home= jvm.arguments= offline.mode=false override.workspace.settings=true diff --git a/org.jdrupes.vmoperator.util/.settings/org.eclipse.jdt.core.prefs b/org.jdrupes.vmoperator.util/.settings/org.eclipse.jdt.core.prefs index f78a58f..300cb64 100644 --- a/org.jdrupes.vmoperator.util/.settings/org.eclipse.jdt.core.prefs +++ b/org.jdrupes.vmoperator.util/.settings/org.eclipse.jdt.core.prefs @@ -1,5 +1,5 @@ # -#Thu Sep 14 18:57:03 CEST 2023 +#Thu Sep 14 21:26:11 CEST 2023 org.eclipse.jdt.core.formatter.insert_space_after_ellipsis=insert org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_declarations=insert org.eclipse.jdt.core.formatter.insert_new_line_in_empty_annotation_declaration=insert @@ -22,12 +22,12 @@ org.eclipse.jdt.core.formatter.blank_lines_after_package=1 org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_local_declarations=insert org.eclipse.jdt.core.formatter.alignment_for_arguments_in_enum_constant=16 org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_parameterized_type_reference=do not insert -org.eclipse.jdt.core.formatter.comment.indent_root_tags=false org.eclipse.jdt.core.formatter.wrap_before_or_operator_multicatch=true +org.eclipse.jdt.core.formatter.comment.indent_root_tags=false org.eclipse.jdt.core.formatter.enabling_tag=@formatter\:on org.eclipse.jdt.core.formatter.comment.count_line_length_from_starting_position=false -org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_parameter=do not insert org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_method_declaration=16 +org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_parameter=do not insert org.eclipse.jdt.core.formatter.keep_then_statement_on_same_line=false org.eclipse.jdt.core.formatter.insert_space_after_comma_in_explicitconstructorcall_arguments=insert org.eclipse.jdt.core.formatter.insert_space_after_prefix_operator=do not insert @@ -43,8 +43,8 @@ org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_invoc org.eclipse.jdt.core.formatter.insert_space_after_assignment_operator=insert org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_type_declaration=insert org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_for=do not insert -org.eclipse.jdt.core.formatter.comment.preserve_white_space_between_code_and_line_comments=true org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_local_variable=insert +org.eclipse.jdt.core.formatter.comment.preserve_white_space_between_code_and_line_comments=true org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_invocation=do not insert org.eclipse.jdt.core.formatter.alignment_for_union_type_in_multicatch=16 org.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_method_body=0 @@ -63,8 +63,8 @@ org.eclipse.jdt.core.formatter.alignment_for_type_parameters=16 org.eclipse.jdt.core.compiler.problem.assertIdentifier=error org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_block_comment=false org.eclipse.jdt.core.formatter.insert_new_line_at_end_of_file_if_missing=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_unary_operator=do not insert org.eclipse.jdt.core.formatter.insert_space_after_comma_in_array_initializer=insert +org.eclipse.jdt.core.formatter.insert_space_before_unary_operator=do not insert org.eclipse.jdt.core.formatter.format_line_comment_starting_on_first_column=false org.eclipse.jdt.core.formatter.parentheses_positions_in_annotation=common_lines org.eclipse.jdt.core.formatter.insert_space_before_ellipsis=do not insert From 3eb8ca514ba05973a68dd20003678a745902f740 Mon Sep 17 00:00:00 2001 From: "Michael N. Lipp" Date: Thu, 14 Sep 2023 22:07:01 +0200 Subject: [PATCH 11/18] Support eclipse configuration. --- buildSrc/build.gradle | 38 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 36 insertions(+), 2 deletions(-) diff --git a/buildSrc/build.gradle b/buildSrc/build.gradle index 8bad6d4..38f8ffd 100644 --- a/buildSrc/build.gradle +++ b/buildSrc/build.gradle @@ -9,6 +9,9 @@ plugins { // are build scripts in 'src/main' that automatically become available // as plugins in the main build. id 'groovy-gradle-plugin' + + // Apply eclipse plugin + id 'eclipse' } repositories { @@ -30,10 +33,41 @@ sourceSets { } } -/* java { toolchain { languageVersion = JavaLanguageVersion.of(17) } } -*/ + +eclipse { + + project { + file { + // closure executed after .project content is loaded from existing file + // and before gradle build information is merged + beforeMerged { project -> + project.natures.clear() + project.buildCommands.clear() + } + + project.natures += 'org.eclipse.buildship.core.gradleprojectnature' + project.buildCommand 'org.eclipse.buildship.core.gradleprojectbuilder' + } + } + + classpath { + downloadJavadoc = true + downloadSources = true + } + + jdt { + file { + withProperties { properties -> + def formatterPrefs = new Properties() + rootProject.file("gradle/org.eclipse.jdt.core.formatter.prefs") + .withInputStream { formatterPrefs.load(it) } + properties.putAll(formatterPrefs) + } + } + } +} From 7f512082f0096cfc0a7917aaf136502b50c25dc8 Mon Sep 17 00:00:00 2001 From: "Michael N. Lipp" Date: Thu, 14 Sep 2023 22:08:15 +0200 Subject: [PATCH 12/18] Update. --- .project | 46 ++++++++++--------- buildSrc/.project | 13 ++++++ buildSrc/.settings/org.eclipse.jdt.core.prefs | 32 ++++++------- .../.settings/org.eclipse.jdt.core.prefs | 10 ++-- 4 files changed, 58 insertions(+), 43 deletions(-) diff --git a/.project b/.project index 505b6f1..e96d2da 100644 --- a/.project +++ b/.project @@ -2,37 +2,41 @@ VM-Operator - + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.buildship.core.gradleprojectbuilder + + + + + net.sf.eclipsecs.core.CheckstyleBuilder + + + + + ch.acanda.eclipse.pmd.builder.PMDBuilder + + + + org.eclipse.jdt.core.javanature org.eclipse.buildship.core.gradleprojectnature net.sf.eclipsecs.core.CheckstyleNature ch.acanda.eclipse.pmd.builder.PMDNature - - - org.eclipse.jdt.core.javabuilder - - - - org.eclipse.buildship.core.gradleprojectbuilder - - - - net.sf.eclipsecs.core.CheckstyleBuilder - - - - ch.acanda.eclipse.pmd.builder.PMDBuilder - - - - 1 + 30 - org.eclipse.core.resources.regexFilterMatcher node_modules|\.git|__CREATED_BY_JAVA_LANGUAGE_SERVER__ diff --git a/buildSrc/.project b/buildSrc/.project index 8624d9f..effb550 100644 --- a/buildSrc/.project +++ b/buildSrc/.project @@ -5,7 +5,20 @@ + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.buildship.core.gradleprojectbuilder + + + + org.eclipse.jdt.groovy.core.groovyNature + org.eclipse.jdt.core.javanature + org.eclipse.buildship.core.gradleprojectnature diff --git a/buildSrc/.settings/org.eclipse.jdt.core.prefs b/buildSrc/.settings/org.eclipse.jdt.core.prefs index a15493a..68fda12 100644 --- a/buildSrc/.settings/org.eclipse.jdt.core.prefs +++ b/buildSrc/.settings/org.eclipse.jdt.core.prefs @@ -1,24 +1,22 @@ -# -#Sun May 07 20:03:15 CEST 2023 -org.eclipse.jdt.core.compiler.codegen.methodParameters=do not generate -org.eclipse.jdt.core.compiler.problem.assertIdentifier=error -org.eclipse.jdt.core.compiler.problem.nullReference=warning -org.eclipse.jdt.core.compiler.debug.localVariable=generate -org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.annotation.nonnull=org.eclipse.jdt.annotation.NonNull +org.eclipse.jdt.core.compiler.annotation.nullable=org.eclipse.jdt.annotation.Nullable +org.eclipse.jdt.core.compiler.annotation.nullanalysis=disabled +org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled +org.eclipse.jdt.core.compiler.codegen.methodParameters=do not generate +org.eclipse.jdt.core.compiler.codegen.targetPlatform=21 +org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve +org.eclipse.jdt.core.compiler.compliance=21 +org.eclipse.jdt.core.compiler.debug.lineNumber=generate +org.eclipse.jdt.core.compiler.debug.localVariable=generate +org.eclipse.jdt.core.compiler.debug.sourceFile=generate +org.eclipse.jdt.core.compiler.problem.assertIdentifier=error org.eclipse.jdt.core.compiler.problem.enablePreviewFeatures=disabled org.eclipse.jdt.core.compiler.problem.enumIdentifier=error -org.eclipse.jdt.core.compiler.annotation.nullable=org.eclipse.jdt.annotation.Nullable -org.eclipse.jdt.core.compiler.debug.sourceFile=generate -org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled org.eclipse.jdt.core.compiler.problem.nullAnnotationInferenceConflict=error -org.eclipse.jdt.core.compiler.codegen.targetPlatform=17 +org.eclipse.jdt.core.compiler.problem.nullReference=warning org.eclipse.jdt.core.compiler.problem.nullSpecViolation=error org.eclipse.jdt.core.compiler.problem.potentialNullReference=ignore -org.eclipse.jdt.core.compiler.source=17 -org.eclipse.jdt.core.compiler.annotation.nonnull=org.eclipse.jdt.annotation.NonNull -org.eclipse.jdt.core.compiler.release=disabled org.eclipse.jdt.core.compiler.problem.reportPreviewFeatures=warning -org.eclipse.jdt.core.compiler.annotation.nullanalysis=disabled -org.eclipse.jdt.core.compiler.debug.lineNumber=generate -org.eclipse.jdt.core.compiler.compliance=17 +org.eclipse.jdt.core.compiler.release=disabled +org.eclipse.jdt.core.compiler.source=21 diff --git a/org.jdrupes.vmoperator.util/.settings/org.eclipse.jdt.core.prefs b/org.jdrupes.vmoperator.util/.settings/org.eclipse.jdt.core.prefs index 300cb64..672f85e 100644 --- a/org.jdrupes.vmoperator.util/.settings/org.eclipse.jdt.core.prefs +++ b/org.jdrupes.vmoperator.util/.settings/org.eclipse.jdt.core.prefs @@ -1,5 +1,5 @@ # -#Thu Sep 14 21:26:11 CEST 2023 +#Thu Sep 14 22:05:28 CEST 2023 org.eclipse.jdt.core.formatter.insert_space_after_ellipsis=insert org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_declarations=insert org.eclipse.jdt.core.formatter.insert_new_line_in_empty_annotation_declaration=insert @@ -22,12 +22,12 @@ org.eclipse.jdt.core.formatter.blank_lines_after_package=1 org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_local_declarations=insert org.eclipse.jdt.core.formatter.alignment_for_arguments_in_enum_constant=16 org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_parameterized_type_reference=do not insert -org.eclipse.jdt.core.formatter.wrap_before_or_operator_multicatch=true org.eclipse.jdt.core.formatter.comment.indent_root_tags=false +org.eclipse.jdt.core.formatter.wrap_before_or_operator_multicatch=true org.eclipse.jdt.core.formatter.enabling_tag=@formatter\:on org.eclipse.jdt.core.formatter.comment.count_line_length_from_starting_position=false -org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_method_declaration=16 org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_parameter=do not insert +org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_method_declaration=16 org.eclipse.jdt.core.formatter.keep_then_statement_on_same_line=false org.eclipse.jdt.core.formatter.insert_space_after_comma_in_explicitconstructorcall_arguments=insert org.eclipse.jdt.core.formatter.insert_space_after_prefix_operator=do not insert @@ -43,8 +43,8 @@ org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_invoc org.eclipse.jdt.core.formatter.insert_space_after_assignment_operator=insert org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_type_declaration=insert org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_for=do not insert -org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_local_variable=insert org.eclipse.jdt.core.formatter.comment.preserve_white_space_between_code_and_line_comments=true +org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_local_variable=insert org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_invocation=do not insert org.eclipse.jdt.core.formatter.alignment_for_union_type_in_multicatch=16 org.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_method_body=0 @@ -63,8 +63,8 @@ org.eclipse.jdt.core.formatter.alignment_for_type_parameters=16 org.eclipse.jdt.core.compiler.problem.assertIdentifier=error org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_block_comment=false org.eclipse.jdt.core.formatter.insert_new_line_at_end_of_file_if_missing=do not insert -org.eclipse.jdt.core.formatter.insert_space_after_comma_in_array_initializer=insert org.eclipse.jdt.core.formatter.insert_space_before_unary_operator=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_array_initializer=insert org.eclipse.jdt.core.formatter.format_line_comment_starting_on_first_column=false org.eclipse.jdt.core.formatter.parentheses_positions_in_annotation=common_lines org.eclipse.jdt.core.formatter.insert_space_before_ellipsis=do not insert From 8a7d9d6621992b8f755b37e3e05fb216f842b48c Mon Sep 17 00:00:00 2001 From: "Michael N. Lipp" Date: Fri, 15 Sep 2023 11:51:19 +0200 Subject: [PATCH 13/18] Report current CPUs in status. --- .../vmoperator/runner/qemu/CpuController.java | 50 +++++++++---------- .../vmoperator/runner/qemu/StatusUpdater.java | 23 +++++++++ .../qemu/events/HotpluggableCpuStatus.java | 40 +++++++++++++++ 3 files changed, 86 insertions(+), 27 deletions(-) diff --git a/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/CpuController.java b/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/CpuController.java index 6d8ccc1..f0face4 100644 --- a/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/CpuController.java +++ b/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/CpuController.java @@ -19,8 +19,8 @@ package org.jdrupes.vmoperator.runner.qemu; import com.fasterxml.jackson.databind.node.ObjectNode; -import java.util.ArrayList; import java.util.HashSet; +import java.util.LinkedList; import java.util.List; import java.util.Optional; import java.util.Set; @@ -81,34 +81,28 @@ public class CpuController extends Component { /** * On monitor result. * - * @param result the result + * @param event the result */ @Handler - public void onHotpluggableCpuStatus(HotpluggableCpuStatus result) { - if (!result.successful()) { + public void onHotpluggableCpuStatus(HotpluggableCpuStatus event) { + if (!event.successful()) { logger.warning(() -> "Failed to get hotpluggable CPU status " - + "(won't adjust number of CPUs.): " + result.errorMessage()); + + "(won't adjust number of CPUs.): " + event.errorMessage()); } - - // Sort - List used = new ArrayList<>(); - List unused = new ArrayList<>(); - for (var itr = result.values().iterator(); itr.hasNext();) { - ObjectNode cpu = (ObjectNode) itr.next(); - if (cpu.has("qom-path")) { - used.add(cpu); - } else { - unused.add(cpu); - } - } - currentCpus = used.size(); if (desiredCpus == null) { return; } // Process - int diff = used.size() - desiredCpus; - diff = addCpus(used, unused, diff); - deleteCpus(used, diff); + currentCpus = event.usedCpus().size(); + int diff = currentCpus - desiredCpus; + if (diff == 0) { + return; + } + diff = addCpus(event.usedCpus(), event.unusedCpus(), diff); + removeCpus(event.usedCpus(), diff); + + // Report result + fire(new MonitorCommand(new QmpQueryHotpluggableCpus())); } @SuppressWarnings("PMD.AvoidInstantiatingObjectsInLoops") @@ -123,22 +117,24 @@ public class CpuController extends Component { } } int nextId = 1; - while (diff < 0 && !unused.isEmpty()) { + List remaining = new LinkedList<>(unused); + while (diff < 0 && !remaining.isEmpty()) { String id; do { id = "cpu-" + nextId++; } while (usedIds.contains(id)); - fire(new MonitorCommand(new QmpAddCpu(unused.get(0), id))); - unused.remove(0); + fire(new MonitorCommand(new QmpAddCpu(remaining.get(0), id))); + remaining.remove(0); diff += 1; } return diff; } @SuppressWarnings("PMD.AvoidInstantiatingObjectsInLoops") - private int deleteCpus(List used, int diff) { - while (diff > 0 && !used.isEmpty()) { - ObjectNode cpu = used.remove(0); + private int removeCpus(List used, int diff) { + List removable = new LinkedList<>(used); + while (diff > 0 && !removable.isEmpty()) { + ObjectNode cpu = removable.remove(0); String qomPath = cpu.get("qom-path").asText(); if (!qomPath.startsWith("/machine/peripheral/cpu-")) { continue; diff --git a/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/StatusUpdater.java b/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/StatusUpdater.java index b72a478..030075d 100644 --- a/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/StatusUpdater.java +++ b/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/StatusUpdater.java @@ -37,6 +37,7 @@ import java.util.Optional; import java.util.Set; import java.util.logging.Level; import org.jdrupes.vmoperator.runner.qemu.events.BalloonChangeEvent; +import org.jdrupes.vmoperator.runner.qemu.events.HotpluggableCpuStatus; import org.jdrupes.vmoperator.runner.qemu.events.RunnerConfigurationUpdate; import org.jdrupes.vmoperator.runner.qemu.events.RunnerStateChange; import org.jdrupes.vmoperator.runner.qemu.events.RunnerStateChange.State; @@ -223,8 +224,10 @@ public class StatusUpdater extends Component { if (event.state() == State.STARTING) { status.addProperty("ram", GsonPtr.to(from.getRaw()) .getAsString("spec", "vm", "maximumRam").orElse("0")); + status.addProperty("cpus", 1); } else if (event.state() == State.STOPPED) { status.addProperty("ram", "0"); + status.addProperty("cpus", 0); } return status; }); @@ -273,4 +276,24 @@ public class StatusUpdater extends Component { return status; }); } + + /** + * On ballon change. + * + * @param event the event + * @throws ApiException + */ + @Handler + public void onCpuChange(HotpluggableCpuStatus event) throws ApiException { + if (vmCrApi == null) { + return; + } + var vmCr + = vmCrApi.get(namespace, vmName).throwsApiException().getObject(); + vmCrApi.updateStatus(vmCr, from -> { + JsonObject status = currentStatus(from); + status.addProperty("cpus", event.usedCpus().size()); + return status; + }); + } } diff --git a/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/events/HotpluggableCpuStatus.java b/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/events/HotpluggableCpuStatus.java index 3ed2e50..68641c9 100644 --- a/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/events/HotpluggableCpuStatus.java +++ b/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/events/HotpluggableCpuStatus.java @@ -19,6 +19,10 @@ package org.jdrupes.vmoperator.runner.qemu.events; import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; import org.jdrupes.vmoperator.runner.qemu.commands.QmpCommand; /** @@ -26,6 +30,9 @@ import org.jdrupes.vmoperator.runner.qemu.commands.QmpCommand; */ public class HotpluggableCpuStatus extends MonitorResult { + private List usedCpus = new ArrayList<>(); + private List unusedCpus = new ArrayList<>(); + /** * Instantiates a new hotpluggable cpu result. * @@ -34,6 +41,39 @@ public class HotpluggableCpuStatus extends MonitorResult { */ public HotpluggableCpuStatus(QmpCommand command, JsonNode response) { super(command, response); + if (!successful()) { + return; + } + + // Sort + for (var itr = values().iterator(); itr.hasNext();) { + ObjectNode cpu = (ObjectNode) itr.next(); + if (cpu.has("qom-path")) { + usedCpus.add(cpu); + } else { + unusedCpus.add(cpu); + } + } + usedCpus = Collections.unmodifiableList(usedCpus); + unusedCpus = Collections.unmodifiableList(unusedCpus); + } + + /** + * Gets the used cpus. + * + * @return the usedCpus + */ + public List usedCpus() { + return usedCpus; + } + + /** + * Gets the unused cpus. + * + * @return the unusedCpus + */ + public List unusedCpus() { + return unusedCpus; } } From 2210dbcae24922e3d3845c81d2378045327e9f82 Mon Sep 17 00:00:00 2001 From: "Michael N. Lipp" Date: Sat, 16 Sep 2023 11:43:51 +0200 Subject: [PATCH 14/18] Log handling errors. --- .../jdrupes/vmoperator/runner/qemu/Runner.java | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/Runner.java b/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/Runner.java index 2e8216b..6ca3b2c 100644 --- a/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/Runner.java +++ b/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/Runner.java @@ -58,11 +58,13 @@ import org.jdrupes.vmoperator.runner.qemu.events.RunnerStateChange; import org.jdrupes.vmoperator.runner.qemu.events.RunnerStateChange.State; import org.jdrupes.vmoperator.util.ExtendedObjectWrapper; import org.jdrupes.vmoperator.util.FsdUtils; +import org.jgrapes.core.Channel; import org.jgrapes.core.Component; import org.jgrapes.core.Components; import org.jgrapes.core.EventPipeline; import org.jgrapes.core.TypedIdKey; import org.jgrapes.core.annotation.Handler; +import org.jgrapes.core.events.HandlingError; import org.jgrapes.core.events.Start; import org.jgrapes.core.events.Started; import org.jgrapes.core.events.Stop; @@ -238,6 +240,20 @@ public class Runner extends Component { fire(new WatchFile(config.toPath())); } + /** + * Log the exception when a handling error is reported. + * + * @param event the event + */ + @Handler(channels = Channel.class, priority = -10_000) + @SuppressWarnings("PMD.GuardLogStatement") + public void onHandlingError(HandlingError event) { + logger.log(Level.WARNING, event.throwable(), + () -> "Problem invoking handler with " + event.event() + ": " + + event.message()); + event.stop(); + } + /** * On configuration update. * From ea6751282c8147fbbe12ea48077e14bf31e0687d Mon Sep 17 00:00:00 2001 From: "Michael N. Lipp" Date: Sat, 16 Sep 2023 11:47:24 +0200 Subject: [PATCH 15/18] Take different approach to finding CR. Requires less privileges. --- .../vmoperator/runner/qemu/StatusUpdater.java | 117 +++++++++--------- .../jdrupes/vmoperator/util/Constants.java | 3 - 2 files changed, 61 insertions(+), 59 deletions(-) diff --git a/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/StatusUpdater.java b/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/StatusUpdater.java index 030075d..af8ab7a 100644 --- a/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/StatusUpdater.java +++ b/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/StatusUpdater.java @@ -22,8 +22,10 @@ import com.google.gson.JsonObject; import io.kubernetes.client.custom.Quantity; import io.kubernetes.client.custom.Quantity.Format; import io.kubernetes.client.openapi.ApiException; -import io.kubernetes.client.openapi.apis.ApiextensionsV1Api; -import io.kubernetes.client.openapi.models.V1CustomResourceDefinitionVersion; +import io.kubernetes.client.openapi.apis.ApisApi; +import io.kubernetes.client.openapi.apis.CustomObjectsApi; +import io.kubernetes.client.openapi.models.V1APIGroup; +import io.kubernetes.client.openapi.models.V1GroupVersionForDiscovery; import io.kubernetes.client.util.Config; import io.kubernetes.client.util.generic.dynamic.DynamicKubernetesApi; import io.kubernetes.client.util.generic.dynamic.DynamicKubernetesObject; @@ -41,12 +43,13 @@ import org.jdrupes.vmoperator.runner.qemu.events.HotpluggableCpuStatus; import org.jdrupes.vmoperator.runner.qemu.events.RunnerConfigurationUpdate; import org.jdrupes.vmoperator.runner.qemu.events.RunnerStateChange; import org.jdrupes.vmoperator.runner.qemu.events.RunnerStateChange.State; -import static org.jdrupes.vmoperator.util.Constants.VM_OP_CRD_NAME; import static org.jdrupes.vmoperator.util.Constants.VM_OP_GROUP; +import static org.jdrupes.vmoperator.util.Constants.VM_OP_KIND_VM; import org.jdrupes.vmoperator.util.GsonPtr; import org.jgrapes.core.Channel; import org.jgrapes.core.Component; import org.jgrapes.core.annotation.Handler; +import org.jgrapes.core.events.HandlingError; import org.jgrapes.core.events.Start; import org.jgrapes.util.events.ConfigurationUpdate; import org.jgrapes.util.events.InitialConfiguration; @@ -73,6 +76,20 @@ public class StatusUpdater extends Component { super(componentChannel); } + /** + * On handling error. + * + * @param event the event + */ + @Handler(channels = Channel.class) + public void onHandlingError(HandlingError event) { + if (event.throwable() instanceof ApiException exc) { + logger.log(Level.WARNING, exc, + () -> "Problem accessing kubernetes: " + exc.getResponseBody()); + event.stop(); + } + } + /** * On configuration update. * @@ -114,51 +131,40 @@ public class StatusUpdater extends Component { * Handle the start event. * * @param event the event + * @throws IOException + * @throws ApiException */ @Handler @SuppressWarnings({ "PMD.DataflowAnomalyAnalysis", - "PMD.AvoidInstantiatingObjectsInLoops" }) - public void onStart(Start event) { - try { - var client = Config.defaultClient(); - var extsApi = new ApiextensionsV1Api(client); - var crds = extsApi.listCustomResourceDefinition(null, null, null, - "metadata.name=" + VM_OP_CRD_NAME, null, null, null, - null, null, null); - if (crds.getItems().isEmpty()) { - logger.warning(() -> "CRD is unknown, status will not" - + " be updated."); - return; + "PMD.AvoidInstantiatingObjectsInLoops", "PMD.AvoidDuplicateLiterals" }) + public void onStart(Start event) throws IOException, ApiException { + var client = Config.defaultClient(); + var apis = new ApisApi(client).getAPIVersions(); + var crdVersions = apis.getGroups().stream() + .filter(g -> g.getName().equals(VM_OP_GROUP)).findFirst() + .map(V1APIGroup::getVersions).stream().flatMap(l -> l.stream()) + .map(V1GroupVersionForDiscovery::getVersion).toList(); + var coa = new CustomObjectsApi(client); + for (var crdVersion : crdVersions) { + var crdApiRes = coa.getAPIResources(VM_OP_GROUP, + crdVersion).getResources().stream() + .filter(r -> VM_OP_KIND_VM.equals(r.getKind())).findFirst(); + if (crdApiRes.isEmpty()) { + continue; } - var crd = crds.getItems().get(0); - if (crd.getSpec().getVersions().stream() - .filter(v -> v.getSubresources() == null).findAny() - .isPresent()) { - logger.warning(() -> "You are using an old version of the CRD," - + " status will not be updated."); - return; + var crApi = new DynamicKubernetesApi(VM_OP_GROUP, + crdVersion, crdApiRes.get().getName(), client); + var vmCr = crApi.get(namespace, vmName).throwsApiException(); + if (vmCr.isSuccess()) { + vmCrApi = crApi; + observedGeneration + = vmCr.getObject().getMetadata().getGeneration(); + break; } - var crdPlural = crd.getSpec().getNames().getPlural(); - var vmOpApiVersions = crd.getSpec().getVersions().stream() - .map(V1CustomResourceDefinitionVersion::getName).toList(); - for (var apiVer : vmOpApiVersions) { - var api = new DynamicKubernetesApi(VM_OP_GROUP, apiVer, - crdPlural, client); - var res = api.get(namespace, vmName); - if (res.isSuccess()) { - vmCrApi = api; - observedGeneration - = res.getObject().getMetadata().getGeneration(); - break; - } - } - if (vmCrApi == null) { - logger.warning(() -> "VM's CR is unknown, status will not" - + " be updated."); - } - } catch (IOException | ApiException e) { - logger.log(Level.WARNING, e, () -> "Cannot access kubernetes: " - + e.getMessage()); + } + if (vmCrApi == null) { + logger.warning(() -> "Cannot find VM's CR, status will not" + + " be updated."); } } @@ -189,9 +195,8 @@ public class StatusUpdater extends Component { vmCrApi.updateStatus(vmCr, from -> { JsonObject status = currentStatus(from); status.getAsJsonArray("conditions").asList().stream() - .map(cond -> (JsonObject) cond) - .filter( - cond -> "Running".equals(cond.get("type").getAsString())) + .map(cond -> (JsonObject) cond).filter(cond -> "Running" + .equals(cond.get("type").getAsString())) .forEach(cond -> cond.addProperty("observedGeneration", from.getMetadata().getGeneration())); return status; @@ -202,7 +207,7 @@ public class StatusUpdater extends Component { * On runner state changed. * * @param event the event - * @throws ApiException the api exception + * @throws ApiException */ @Handler public void onRunnerStateChanged(RunnerStateChange event) @@ -210,8 +215,8 @@ public class StatusUpdater extends Component { if (vmCrApi == null) { return; } - var vmCr = vmCrApi.get(namespace, vmName) - .throwsApiException().getObject(); + var vmCr = vmCrApi.get(namespace, vmName).throwsApiException() + .getObject(); vmCrApi.updateStatus(vmCr, from -> { JsonObject status = currentStatus(from); status.getAsJsonArray("conditions").asList().stream() @@ -230,7 +235,7 @@ public class StatusUpdater extends Component { status.addProperty("cpus", 0); } return status; - }); + }).throwsApiException(); } private void updateRunningCondition(RunnerStateChange event, @@ -266,15 +271,15 @@ public class StatusUpdater extends Component { if (vmCrApi == null) { return; } - var vmCr - = vmCrApi.get(namespace, vmName).throwsApiException().getObject(); + var vmCr = vmCrApi.get(namespace, vmName).throwsApiException() + .getObject(); vmCrApi.updateStatus(vmCr, from -> { JsonObject status = currentStatus(from); status.addProperty("ram", new Quantity(new BigDecimal(event.size()), Format.BINARY_SI) .toSuffixedString()); return status; - }); + }).throwsApiException(); } /** @@ -288,12 +293,12 @@ public class StatusUpdater extends Component { if (vmCrApi == null) { return; } - var vmCr - = vmCrApi.get(namespace, vmName).throwsApiException().getObject(); + var vmCr = vmCrApi.get(namespace, vmName).throwsApiException() + .getObject(); vmCrApi.updateStatus(vmCr, from -> { JsonObject status = currentStatus(from); status.addProperty("cpus", event.usedCpus().size()); return status; - }); + }).throwsApiException(); } } diff --git a/org.jdrupes.vmoperator.util/src/org/jdrupes/vmoperator/util/Constants.java b/org.jdrupes.vmoperator.util/src/org/jdrupes/vmoperator/util/Constants.java index 14fad65..ba710d1 100644 --- a/org.jdrupes.vmoperator.util/src/org/jdrupes/vmoperator/util/Constants.java +++ b/org.jdrupes.vmoperator.util/src/org/jdrupes/vmoperator/util/Constants.java @@ -29,9 +29,6 @@ public class Constants { /** The Constant VM_OP_GROUP. */ public static final String VM_OP_GROUP = "vmoperator.jdrupes.org"; - /** The Constant VM_OP_CRD_NAME. */ - public static final String VM_OP_CRD_NAME = "vms." + VM_OP_GROUP; - /** The Constant VM_OP_KIND_VM. */ public static final String VM_OP_KIND_VM = "VirtualMachine"; } From f24b6aca5245114385695022b707fea1b9743ab9 Mon Sep 17 00:00:00 2001 From: "Michael N. Lipp" Date: Sat, 16 Sep 2023 11:48:20 +0200 Subject: [PATCH 16/18] Define RBAC for running in cluster. --- deploy/kustomization.yaml | 5 ++++- deploy/vmrunner-role-binding.yaml | 13 ++++++++++++ deploy/vmrunner-role.yaml | 20 +++++++++++++++++++ deploy/vmrunner-service-account.yaml | 6 ++++++ .../vmoperator/manager/runnerSts.ftl.yaml | 1 + 5 files changed, 44 insertions(+), 1 deletion(-) create mode 100644 deploy/vmrunner-role-binding.yaml create mode 100644 deploy/vmrunner-role.yaml create mode 100644 deploy/vmrunner-service-account.yaml diff --git a/deploy/kustomization.yaml b/deploy/kustomization.yaml index 7193e0b..a988f88 100644 --- a/deploy/kustomization.yaml +++ b/deploy/kustomization.yaml @@ -7,4 +7,7 @@ resources: - vmop-role-binding.yaml - vmop-image-repository-pvc.yaml - vmop-config-map.yaml -- vmop-deployment.yaml \ No newline at end of file +- vmop-deployment.yaml +- vmrunner-role.yaml +- vmrunner-service-account.yaml +- vmrunner-role-binding.yaml diff --git a/deploy/vmrunner-role-binding.yaml b/deploy/vmrunner-role-binding.yaml new file mode 100644 index 0000000..6e3f75b --- /dev/null +++ b/deploy/vmrunner-role-binding.yaml @@ -0,0 +1,13 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: vm-runner + labels: + app.kubernetes.io/name: vm-operator +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: vm-runner +subjects: +- kind: ServiceAccount + name: vm-runner diff --git a/deploy/vmrunner-role.yaml b/deploy/vmrunner-role.yaml new file mode 100644 index 0000000..54e8742 --- /dev/null +++ b/deploy/vmrunner-role.yaml @@ -0,0 +1,20 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: vm-runner + labels: + app.kubernetes.io/name: vm-operator +rules: +- apiGroups: + - vmoperator.jdrupes.org + resources: + - vms + verbs: + - list + - get +- apiGroups: + - vmoperator.jdrupes.org + resources: + - vms/status + verbs: + - patch diff --git a/deploy/vmrunner-service-account.yaml b/deploy/vmrunner-service-account.yaml new file mode 100644 index 0000000..c876dad --- /dev/null +++ b/deploy/vmrunner-service-account.yaml @@ -0,0 +1,6 @@ +kind: ServiceAccount +apiVersion: v1 +metadata: + name: vm-runner + labels: + app.kubernetes.io/name: vm-operator diff --git a/org.jdrupes.vmoperator.manager/resources/org/jdrupes/vmoperator/manager/runnerSts.ftl.yaml b/org.jdrupes.vmoperator.manager/resources/org/jdrupes/vmoperator/manager/runnerSts.ftl.yaml index 60834b9..ac1178a 100644 --- a/org.jdrupes.vmoperator.manager/resources/org/jdrupes/vmoperator/manager/runnerSts.ftl.yaml +++ b/org.jdrupes.vmoperator.manager/resources/org/jdrupes/vmoperator/manager/runnerSts.ftl.yaml @@ -140,6 +140,7 @@ spec: <#if cr.spec.affinity??> affinity: ${ cr.spec.affinity.toString() } + serviceAccountName: vm-runner volumeClaimTemplates: - metadata: namespace: ${ cr.metadata.namespace.asString } From 3b0fb02fbe1a3bfdd7357e2f09c71caab181c65c Mon Sep 17 00:00:00 2001 From: "Michael N. Lipp" Date: Sat, 16 Sep 2023 11:53:51 +0200 Subject: [PATCH 17/18] Improve error reporting. --- .../jdrupes/vmoperator/manager/Controller.java | 16 ++++++++++++++++ .../org/jdrupes/vmoperator/manager/Manager.java | 16 ++++++++++++++++ 2 files changed, 32 insertions(+) diff --git a/org.jdrupes.vmoperator.manager/src/org/jdrupes/vmoperator/manager/Controller.java b/org.jdrupes.vmoperator.manager/src/org/jdrupes/vmoperator/manager/Controller.java index 998dc1d..28b078f 100644 --- a/org.jdrupes.vmoperator.manager/src/org/jdrupes/vmoperator/manager/Controller.java +++ b/org.jdrupes.vmoperator.manager/src/org/jdrupes/vmoperator/manager/Controller.java @@ -21,9 +21,11 @@ package org.jdrupes.vmoperator.manager; import io.kubernetes.client.openapi.ApiException; import io.kubernetes.client.openapi.Configuration; import java.io.IOException; +import java.util.logging.Level; import org.jgrapes.core.Channel; import org.jgrapes.core.Component; import org.jgrapes.core.annotation.Handler; +import org.jgrapes.core.events.HandlingError; import org.jgrapes.core.events.Start; /** @@ -74,6 +76,20 @@ public class Controller extends Component { attach(new Reconciler(channel())); } + /** + * Special handling of {@link ApiException} thrown by handlers. + * + * @param event the event + */ + @Handler(channels = Channel.class) + public void onHandlingError(HandlingError event) { + if (event.throwable() instanceof ApiException exc) { + logger.log(Level.WARNING, exc, + () -> "Problem accessing kubernetes: " + exc.getResponseBody()); + event.stop(); + } + } + /** * Handle the start event. Has higher priority because it configures * the default Kubernetes client. diff --git a/org.jdrupes.vmoperator.manager/src/org/jdrupes/vmoperator/manager/Manager.java b/org.jdrupes.vmoperator.manager/src/org/jdrupes/vmoperator/manager/Manager.java index 5b55ca8..a658846 100644 --- a/org.jdrupes.vmoperator.manager/src/org/jdrupes/vmoperator/manager/Manager.java +++ b/org.jdrupes.vmoperator.manager/src/org/jdrupes/vmoperator/manager/Manager.java @@ -32,9 +32,11 @@ import org.apache.commons.cli.Option; import org.apache.commons.cli.Options; import static org.jdrupes.vmoperator.manager.Constants.VM_OP_NAME; import org.jdrupes.vmoperator.util.FsdUtils; +import org.jgrapes.core.Channel; import org.jgrapes.core.Component; import org.jgrapes.core.Components; import org.jgrapes.core.annotation.Handler; +import org.jgrapes.core.events.HandlingError; import org.jgrapes.core.events.Stop; import org.jgrapes.io.NioDispatcher; import org.jgrapes.util.FileSystemWatcher; @@ -95,6 +97,20 @@ public class Manager extends Component { fire(new WatchFile(config.toPath())); } + /** + * Log the exception when a handling error is reported. + * + * @param event the event + */ + @Handler(channels = Channel.class, priority = -10_000) + @SuppressWarnings("PMD.GuardLogStatement") + public void onHandlingError(HandlingError event) { + logger.log(Level.WARNING, event.throwable(), + () -> "Problem invoking handler with " + event.event() + ": " + + event.message()); + event.stop(); + } + /** * On stop. * From 3825a4d3fd303da93860e6e7ba8bd899e4294d8a Mon Sep 17 00:00:00 2001 From: "Michael N. Lipp" Date: Sat, 16 Sep 2023 12:10:21 +0200 Subject: [PATCH 18/18] Remove explicit toolchain setting. --- buildSrc/build.gradle | 6 ------ 1 file changed, 6 deletions(-) diff --git a/buildSrc/build.gradle b/buildSrc/build.gradle index 38f8ffd..8a1e6e1 100644 --- a/buildSrc/build.gradle +++ b/buildSrc/build.gradle @@ -33,12 +33,6 @@ sourceSets { } } -java { - toolchain { - languageVersion = JavaLanguageVersion.of(17) - } -} - eclipse { project {