Move automatic login request to CRD.
Also reorganizes constants.
This commit is contained in:
parent
3152ff842b
commit
5366e24092
22 changed files with 259 additions and 206 deletions
|
|
@ -19,8 +19,8 @@
|
|||
|
||||
handlers=java.util.logging.ConsoleHandler
|
||||
|
||||
org.jgrapes.level=FINE
|
||||
org.jgrapes.core.handlerTracking.level=FINER
|
||||
#org.jgrapes.level=FINE
|
||||
#org.jgrapes.core.handlerTracking.level=FINER
|
||||
|
||||
org.jdrupes.vmoperator.runner.qemu.level=FINE
|
||||
|
||||
|
|
|
|||
|
|
@ -248,6 +248,9 @@ public class Configuration implements Dto {
|
|||
/** The number of outputs. */
|
||||
public int outputs = 1;
|
||||
|
||||
/** The logged in user. */
|
||||
public String loggedInUser;
|
||||
|
||||
/** The spice. */
|
||||
public Spice spice;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,8 +25,7 @@ import io.kubernetes.client.openapi.models.EventsV1Event;
|
|||
import java.io.IOException;
|
||||
import java.util.logging.Level;
|
||||
import static org.jdrupes.vmoperator.common.Constants.APP_NAME;
|
||||
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.common.Constants.Crd;
|
||||
import org.jdrupes.vmoperator.common.K8s;
|
||||
import org.jdrupes.vmoperator.common.K8sClient;
|
||||
import org.jdrupes.vmoperator.common.VmDefinitionStub;
|
||||
|
|
@ -74,7 +73,7 @@ public class ConsoleTracker extends VmDefUpdater {
|
|||
}
|
||||
try {
|
||||
vmStub = VmDefinitionStub.get(apiClient,
|
||||
new GroupVersionKind(VM_OP_GROUP, "", VM_OP_KIND_VM),
|
||||
new GroupVersionKind(Crd.GROUP, "", Crd.KIND_VM),
|
||||
namespace, vmName);
|
||||
} catch (ApiException e) {
|
||||
logger.log(Level.SEVERE, e,
|
||||
|
|
@ -115,7 +114,7 @@ public class ConsoleTracker extends VmDefUpdater {
|
|||
|
||||
// Log event
|
||||
var evt = new EventsV1Event()
|
||||
.reportingController(VM_OP_GROUP + "/" + APP_NAME)
|
||||
.reportingController(Crd.GROUP + "/" + APP_NAME)
|
||||
.action("ConsoleConnectionUpdate")
|
||||
.reason("Connection from " + event.clientHost());
|
||||
K8s.createEvent(apiClient, vmStub.model().get(), evt);
|
||||
|
|
@ -150,7 +149,7 @@ public class ConsoleTracker extends VmDefUpdater {
|
|||
|
||||
// Log event
|
||||
var evt = new EventsV1Event()
|
||||
.reportingController(VM_OP_GROUP + "/" + APP_NAME)
|
||||
.reportingController(Crd.GROUP + "/" + APP_NAME)
|
||||
.action("ConsoleConnectionUpdate")
|
||||
.reason("Disconnected from " + event.clientHost());
|
||||
K8s.createEvent(apiClient, vmStub.model().get(), evt);
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
* VM-Operator
|
||||
* Copyright (C) 2023 Michael N. Lipp
|
||||
* Copyright (C) 2023,2025 Michael N. Lipp
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
|
|
@ -24,10 +24,7 @@ import java.nio.file.Path;
|
|||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.logging.Level;
|
||||
import static org.jdrupes.vmoperator.common.Constants.DATA_DISPLAY_LOGIN;
|
||||
import static org.jdrupes.vmoperator.common.Constants.DATA_DISPLAY_PASSWORD;
|
||||
import static org.jdrupes.vmoperator.common.Constants.DATA_DISPLAY_USER;
|
||||
import static org.jdrupes.vmoperator.common.Constants.DATA_PASSWORD_EXPIRY;
|
||||
import org.jdrupes.vmoperator.common.Constants.DisplaySecret;
|
||||
import org.jdrupes.vmoperator.runner.qemu.commands.QmpSetDisplayPassword;
|
||||
import org.jdrupes.vmoperator.runner.qemu.commands.QmpSetPasswordExpiry;
|
||||
import org.jdrupes.vmoperator.runner.qemu.events.ConfigureQemu;
|
||||
|
|
@ -35,9 +32,10 @@ import org.jdrupes.vmoperator.runner.qemu.events.MonitorCommand;
|
|||
import org.jdrupes.vmoperator.runner.qemu.events.RunnerStateChange.RunState;
|
||||
import org.jdrupes.vmoperator.runner.qemu.events.VmopAgentConnected;
|
||||
import org.jdrupes.vmoperator.runner.qemu.events.VmopAgentLogIn;
|
||||
import org.jdrupes.vmoperator.runner.qemu.events.VmopAgentLoggedIn;
|
||||
import org.jdrupes.vmoperator.runner.qemu.events.VmopAgentLogOut;
|
||||
import org.jgrapes.core.Channel;
|
||||
import org.jgrapes.core.Component;
|
||||
import org.jgrapes.core.Event;
|
||||
import org.jgrapes.core.annotation.Handler;
|
||||
import org.jgrapes.util.events.FileChanged;
|
||||
import org.jgrapes.util.events.WatchFile;
|
||||
|
|
@ -52,6 +50,7 @@ public class DisplayController extends Component {
|
|||
private String protocol;
|
||||
private final Path configDir;
|
||||
private boolean vmopAgentConnected;
|
||||
private String loggedInUser;
|
||||
|
||||
/**
|
||||
* Instantiates a new Display controller.
|
||||
|
|
@ -64,17 +63,7 @@ public class DisplayController extends Component {
|
|||
public DisplayController(Channel componentChannel, Path configDir) {
|
||||
super(componentChannel);
|
||||
this.configDir = configDir;
|
||||
fire(new WatchFile(configDir.resolve(DATA_DISPLAY_PASSWORD)));
|
||||
}
|
||||
|
||||
/**
|
||||
* On vmop agent connected.
|
||||
*
|
||||
* @param event the event
|
||||
*/
|
||||
@Handler
|
||||
public void onVmopAgentConnected(VmopAgentConnected event) {
|
||||
vmopAgentConnected = true;
|
||||
fire(new WatchFile(configDir.resolve(DisplaySecret.DISPLAY_PASSWORD)));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -89,7 +78,32 @@ public class DisplayController extends Component {
|
|||
}
|
||||
protocol
|
||||
= event.configuration().vm.display.spice != null ? "spice" : null;
|
||||
configureAccess(false);
|
||||
loggedInUser = event.configuration().vm.display.loggedInUser;
|
||||
configureLogin();
|
||||
if (event.runState() == RunState.STARTING) {
|
||||
configurePassword();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* On vmop agent connected.
|
||||
*
|
||||
* @param event the event
|
||||
*/
|
||||
@Handler
|
||||
public void onVmopAgentConnected(VmopAgentConnected event) {
|
||||
vmopAgentConnected = true;
|
||||
configureLogin();
|
||||
}
|
||||
|
||||
private void configureLogin() {
|
||||
if (!vmopAgentConnected) {
|
||||
return;
|
||||
}
|
||||
Event<?> evt = loggedInUser != null
|
||||
? new VmopAgentLogIn(loggedInUser)
|
||||
: new VmopAgentLogOut();
|
||||
fire(evt);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -100,46 +114,10 @@ public class DisplayController extends Component {
|
|||
@Handler
|
||||
@SuppressWarnings("PMD.EmptyCatchBlock")
|
||||
public void onFileChanged(FileChanged event) {
|
||||
if (event.path().equals(configDir.resolve(DATA_DISPLAY_PASSWORD))) {
|
||||
configureAccess(true);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("PMD.DataflowAnomalyAnalysis")
|
||||
private void configureAccess(boolean passwordChange) {
|
||||
var userLoginConfigured = readFromFile(DATA_DISPLAY_LOGIN)
|
||||
.map(Boolean::parseBoolean).orElse(false);
|
||||
if (!userLoginConfigured) {
|
||||
if (event.path()
|
||||
.equals(configDir.resolve(DisplaySecret.DISPLAY_PASSWORD))) {
|
||||
configurePassword();
|
||||
return;
|
||||
}
|
||||
|
||||
// With user login configured, we have to make sure that the
|
||||
// user is logged in before we set the password and thus allow
|
||||
// access to the display.
|
||||
if (!vmopAgentConnected) {
|
||||
if (passwordChange) {
|
||||
logger.warning(() -> "Request for user login before "
|
||||
+ "VM operator agent has connected");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
var user = readFromFile(DATA_DISPLAY_USER);
|
||||
if (user.isEmpty()) {
|
||||
logger.warning(() -> "Login requested, but no user configured");
|
||||
}
|
||||
fire(new VmopAgentLogIn(user.get()).setAssociated(this, user.get()));
|
||||
}
|
||||
|
||||
/**
|
||||
* On vmop agent logged in.
|
||||
*
|
||||
* @param event the event
|
||||
*/
|
||||
@Handler
|
||||
public void onVmopAgentLoggedIn(VmopAgentLoggedIn event) {
|
||||
configurePassword();
|
||||
}
|
||||
|
||||
private void configurePassword() {
|
||||
|
|
@ -152,7 +130,7 @@ public class DisplayController extends Component {
|
|||
}
|
||||
|
||||
private boolean setDisplayPassword() {
|
||||
return readFromFile(DATA_DISPLAY_PASSWORD).map(password -> {
|
||||
return readFromFile(DisplaySecret.DISPLAY_PASSWORD).map(password -> {
|
||||
if (Objects.equals(this.currentPassword, password)) {
|
||||
return true;
|
||||
}
|
||||
|
|
@ -165,7 +143,7 @@ public class DisplayController extends Component {
|
|||
}
|
||||
|
||||
private void setPasswordExpiry() {
|
||||
readFromFile(DATA_PASSWORD_EXPIRY).ifPresent(expiry -> {
|
||||
readFromFile(DisplaySecret.PASSWORD_EXPIRY).ifPresent(expiry -> {
|
||||
logger.fine(() -> "Updating expiry time to " + expiry);
|
||||
fire(
|
||||
new MonitorCommand(new QmpSetPasswordExpiry(protocol, expiry)));
|
||||
|
|
|
|||
|
|
@ -56,7 +56,7 @@ import org.apache.commons.cli.DefaultParser;
|
|||
import org.apache.commons.cli.Option;
|
||||
import org.apache.commons.cli.Options;
|
||||
import static org.jdrupes.vmoperator.common.Constants.APP_NAME;
|
||||
import static org.jdrupes.vmoperator.common.Constants.DATA_DISPLAY_PASSWORD;
|
||||
import org.jdrupes.vmoperator.common.Constants.DisplaySecret;
|
||||
import org.jdrupes.vmoperator.runner.qemu.commands.QmpCont;
|
||||
import org.jdrupes.vmoperator.runner.qemu.commands.QmpReset;
|
||||
import org.jdrupes.vmoperator.runner.qemu.events.ConfigureQemu;
|
||||
|
|
@ -312,7 +312,7 @@ public class Runner extends Component {
|
|||
|
||||
// Add some values from other sources to configuration
|
||||
newConf.asOf = Instant.ofEpochSecond(configFile.lastModified());
|
||||
Path dsPath = configDir.resolve(DATA_DISPLAY_PASSWORD);
|
||||
Path dsPath = configDir.resolve(DisplaySecret.DISPLAY_PASSWORD);
|
||||
newConf.hasDisplayPassword = dsPath.toFile().canRead();
|
||||
|
||||
// Special actions for initial configuration (startup)
|
||||
|
|
|
|||
|
|
@ -33,8 +33,8 @@ import java.io.IOException;
|
|||
import java.math.BigDecimal;
|
||||
import java.util.logging.Level;
|
||||
import static org.jdrupes.vmoperator.common.Constants.APP_NAME;
|
||||
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.common.Constants.Crd;
|
||||
import org.jdrupes.vmoperator.common.Constants.Status;
|
||||
import org.jdrupes.vmoperator.common.K8s;
|
||||
import org.jdrupes.vmoperator.common.VmDefinition;
|
||||
import org.jdrupes.vmoperator.common.VmDefinitionStub;
|
||||
|
|
@ -48,6 +48,8 @@ import org.jdrupes.vmoperator.runner.qemu.events.RunnerStateChange;
|
|||
import org.jdrupes.vmoperator.runner.qemu.events.RunnerStateChange.RunState;
|
||||
import org.jdrupes.vmoperator.runner.qemu.events.ShutdownEvent;
|
||||
import org.jdrupes.vmoperator.runner.qemu.events.VmopAgentConnected;
|
||||
import org.jdrupes.vmoperator.runner.qemu.events.VmopAgentLoggedIn;
|
||||
import org.jdrupes.vmoperator.runner.qemu.events.VmopAgentLoggedOut;
|
||||
import org.jdrupes.vmoperator.util.GsonPtr;
|
||||
import org.jgrapes.core.Channel;
|
||||
import org.jgrapes.core.annotation.Handler;
|
||||
|
|
@ -110,11 +112,17 @@ public class StatusUpdater extends VmDefUpdater {
|
|||
}
|
||||
try {
|
||||
vmStub = VmDefinitionStub.get(apiClient,
|
||||
new GroupVersionKind(VM_OP_GROUP, "", VM_OP_KIND_VM),
|
||||
new GroupVersionKind(Crd.GROUP, "", Crd.KIND_VM),
|
||||
namespace, vmName);
|
||||
vmStub.model().ifPresent(model -> {
|
||||
observedGeneration = model.getMetadata().getGeneration();
|
||||
});
|
||||
var vmDef = vmStub.updateStatus(from -> {
|
||||
JsonObject status = from.statusJson();
|
||||
status.remove(Status.LOGGED_IN_USER);
|
||||
return status;
|
||||
}).orElse(null);
|
||||
if (vmDef == null) {
|
||||
return;
|
||||
}
|
||||
observedGeneration = vmDef.getMetadata().getGeneration();
|
||||
} catch (ApiException e) {
|
||||
logger.log(Level.SEVERE, e,
|
||||
() -> "Cannot access VM object, terminating.");
|
||||
|
|
@ -152,7 +160,7 @@ public class StatusUpdater extends VmDefUpdater {
|
|||
"displayPasswordSerial").getAsInt() == -1)) {
|
||||
return;
|
||||
}
|
||||
vmStub.updateStatus(vmDef.get(), from -> {
|
||||
vmStub.updateStatus(from -> {
|
||||
JsonObject status = from.statusJson();
|
||||
if (!event.configuration().hasDisplayPassword) {
|
||||
status.addProperty("displayPasswordSerial", -1);
|
||||
|
|
@ -173,15 +181,15 @@ public class StatusUpdater extends VmDefUpdater {
|
|||
* @throws ApiException
|
||||
*/
|
||||
@Handler
|
||||
@SuppressWarnings({ "PMD.AssignmentInOperand",
|
||||
"PMD.AvoidLiteralsInIfCondition" })
|
||||
@SuppressWarnings({ "PMD.AvoidLiteralsInIfCondition",
|
||||
"PMD.AssignmentInOperand", "PMD.AvoidDuplicateLiterals" })
|
||||
public void onRunnerStateChanged(RunnerStateChange event)
|
||||
throws ApiException {
|
||||
VmDefinition vmDef;
|
||||
if (vmStub == null || (vmDef = vmStub.model().orElse(null)) == null) {
|
||||
return;
|
||||
}
|
||||
vmStub.updateStatus(vmDef, from -> {
|
||||
vmStub.updateStatus(from -> {
|
||||
JsonObject status = from.statusJson();
|
||||
boolean running = event.runState().vmRunning();
|
||||
updateCondition(vmDef, vmDef.statusJson(), "Running", running,
|
||||
|
|
@ -196,6 +204,7 @@ public class StatusUpdater extends VmDefUpdater {
|
|||
} else if (event.runState() == RunState.STOPPED) {
|
||||
status.addProperty("ram", "0");
|
||||
status.addProperty("cpus", 0);
|
||||
status.remove(Status.LOGGED_IN_USER);
|
||||
}
|
||||
|
||||
if (!running) {
|
||||
|
|
@ -228,7 +237,7 @@ public class StatusUpdater extends VmDefUpdater {
|
|||
|
||||
// Log event
|
||||
var evt = new EventsV1Event()
|
||||
.reportingController(VM_OP_GROUP + "/" + APP_NAME)
|
||||
.reportingController(Crd.GROUP + "/" + APP_NAME)
|
||||
.action("StatusUpdate").reason(event.reason())
|
||||
.note(event.message());
|
||||
K8s.createEvent(apiClient, vmDef, evt);
|
||||
|
|
@ -344,4 +353,35 @@ public class StatusUpdater extends VmDefUpdater {
|
|||
return status;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @param event the event
|
||||
* @throws ApiException
|
||||
*/
|
||||
@Handler
|
||||
@SuppressWarnings("PMD.AssignmentInOperand")
|
||||
public void onVmopAgentLoggedIn(VmopAgentLoggedIn event)
|
||||
throws ApiException {
|
||||
vmStub.updateStatus(from -> {
|
||||
JsonObject status = from.statusJson();
|
||||
status.addProperty(Status.LOGGED_IN_USER,
|
||||
event.triggering().user());
|
||||
return status;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @param event the event
|
||||
* @throws ApiException
|
||||
*/
|
||||
@Handler
|
||||
@SuppressWarnings("PMD.AssignmentInOperand")
|
||||
public void onVmopAgentLoggedOut(VmopAgentLoggedOut event)
|
||||
throws ApiException {
|
||||
vmStub.updateStatus(from -> {
|
||||
JsonObject status = from.statusJson();
|
||||
status.remove(Status.LOGGED_IN_USER);
|
||||
return status;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue