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 4e4ba18..b93ffd6 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 @@ -52,6 +52,7 @@ import org.apache.commons.cli.Option; import org.apache.commons.cli.Options; import static org.jdrupes.vmoperator.common.Constants.APP_NAME; import org.jdrupes.vmoperator.runner.qemu.commands.QmpCont; +import org.jdrupes.vmoperator.runner.qemu.events.Exit; import org.jdrupes.vmoperator.runner.qemu.events.MonitorCommand; import org.jdrupes.vmoperator.runner.qemu.events.QmpConfigured; import org.jdrupes.vmoperator.runner.qemu.events.RunnerConfigurationUpdate; @@ -185,6 +186,7 @@ public class Runner extends Component { = "Standard-VM-latest.ftl.yaml"; private static final String SAVED_TEMPLATE = "VM.ftl.yaml"; private static final String FW_VARS = "fw-vars.fd"; + private static int exitStatus; private EventPipeline rep; private final ObjectMapper yamlMapper = new ObjectMapper(new YAMLFactory()); @@ -582,6 +584,16 @@ public class Runner extends Component { "The VM has been shut down")); } + /** + * On exit. + * + * @param event the event + */ + @Handler + public void onExit(Exit event) { + exitStatus = event.exitStatus(); + } + private void shutdown() { if (state != State.TERMINATING) { fire(new Stop()); @@ -650,6 +662,11 @@ public class Runner extends Component { // Start the application Components.start(app); + + // Wait for (regular) termination + Components.awaitExhaustion(); + System.exit(exitStatus); + } catch (IOException | InterruptedException | org.apache.commons.cli.ParseException e) { Logger.getLogger(Runner.class.getName()).log(Level.SEVERE, e, 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 19e252f..4edcb46 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 @@ -41,6 +41,7 @@ import java.util.logging.Level; import static org.jdrupes.vmoperator.common.Constants.VM_OP_GROUP; import static org.jdrupes.vmoperator.common.Constants.VM_OP_KIND_VM; import org.jdrupes.vmoperator.runner.qemu.events.BalloonChangeEvent; +import org.jdrupes.vmoperator.runner.qemu.events.Exit; import org.jdrupes.vmoperator.runner.qemu.events.HotpluggableCpuStatus; import org.jdrupes.vmoperator.runner.qemu.events.RunnerConfigurationUpdate; import org.jdrupes.vmoperator.runner.qemu.events.RunnerStateChange; @@ -57,6 +58,7 @@ import org.jgrapes.util.events.InitialConfiguration; /** * Updates the CR status. */ +@SuppressWarnings("PMD.DataflowAnomalyAnalysis") public class StatusUpdater extends Component { private static final Set RUNNING_STATES @@ -135,12 +137,21 @@ public class StatusUpdater extends Component { * @throws ApiException */ @Handler - @SuppressWarnings({ "PMD.DataflowAnomalyAnalysis", - "PMD.AvoidInstantiatingObjectsInLoops", "PMD.AvoidDuplicateLiterals" }) - public void onStart(Start event) throws IOException, ApiException { + public void onStart(Start event) { if (namespace == null) { return; } + try { + initVmCrApi(event); + } catch (IOException | ApiException e) { + logger.log(Level.SEVERE, e, + () -> "Cannot access VM's CR, terminating."); + event.cancel(true); + fire(new Exit(1)); + } + } + + private void initVmCrApi(Start event) throws IOException, ApiException { var client = Config.defaultClient(); var apis = new ApisApi(client).getAPIVersions(); var crdVersions = apis.getGroups().stream() @@ -155,6 +166,7 @@ public class StatusUpdater extends Component { if (crdApiRes.isEmpty()) { continue; } + @SuppressWarnings("PMD.AvoidInstantiatingObjectsInLoops") var crApi = new DynamicKubernetesApi(VM_OP_GROUP, crdVersion, crdApiRes.get().getName(), client); var vmCr = crApi.get(namespace, vmName); @@ -166,8 +178,9 @@ public class StatusUpdater extends Component { } } if (vmCrApi == null) { - logger.warning(() -> "Cannot find VM's CR, status will not" - + " be updated."); + logger.severe(() -> "Cannot find VM's CR, terminating."); + event.cancel(true); + fire(new Exit(1)); } } diff --git a/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/events/Exit.java b/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/events/Exit.java new file mode 100644 index 0000000..bb608f6 --- /dev/null +++ b/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/events/Exit.java @@ -0,0 +1,43 @@ +/* + * VM-Operator + * Copyright (C) 2024 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 org.jgrapes.core.events.Stop; + +/** + * Like {@link Stop}, but sets an exit status. + */ +@SuppressWarnings("PMD.ShortClassName") +public class Exit extends Stop { + + private final int exitStatus; + + /** + * Instantiates a new exit. + * + * @param exitStatus the exit status + */ + public Exit(int exitStatus) { + this.exitStatus = exitStatus; + } + + public int exitStatus() { + return exitStatus; + } +}