Merge branch 'main' into testing

This commit is contained in:
Michael Lipp 2025-03-28 18:04:15 +01:00
commit 0868f3540f
8 changed files with 77 additions and 41 deletions

View file

@ -133,3 +133,4 @@ spec:
affinity: ${ toJson(spec.affinity) }
</#if>
serviceAccountName: vm-runner
restartPolicy: Never

View file

@ -86,16 +86,20 @@ import org.yaml.snakeyaml.constructor.SafeConstructor;
boolean modelChanged)
throws IOException, TemplateException, ApiException {
// Check if an update is needed
Object prevInputs
= channel.associated(PrevData.class, Object.class).orElse(null);
var prevData = channel.associated(PrevData.class)
.orElseGet(() -> new PrevData(null, new HashMap<>()));
Object newInputs = model.get("loginRequestedFor");
if (!modelChanged && Objects.equals(prevInputs, newInputs)) {
if (!modelChanged && Objects.equals(prevData.inputs, newInputs)) {
// Make added data available in new model
model.putAll(prevData.added);
return;
}
channel.setAssociated(PrevData.class, newInputs);
prevData = new PrevData(newInputs, prevData.added);
channel.setAssociated(PrevData.class, prevData);
// Combine template and data and parse result
model.put("adjustCloudInitMeta", adjustCloudInitMetaModel);
prevData.added.put("adjustCloudInitMeta", adjustCloudInitMetaModel);
var fmTemplate = fmConfig.getTemplate("runnerConfig.ftl.yaml");
StringWriter out = new StringWriter();
fmTemplate.process(model, out);
@ -127,12 +131,14 @@ import org.yaml.snakeyaml.constructor.SafeConstructor;
maybeForceUpdate(channel.client(), updatedCm);
model.put("configMapResourceVersion",
updatedCm.getMetadata().getResourceVersion());
prevData.added.put("configMapResourceVersion",
updatedCm.getMetadata().getResourceVersion());
}
/**
* Key for association.
*/
private final class PrevData {
private record PrevData(Object inputs, Map<String, Object> added) {
}
/**

View file

@ -120,25 +120,24 @@ public class DisplaySecretReconciler extends Component {
* secret with a random password and immediate expiration, thus
* preventing access to the display.
*
* @param event the event
* @param vmDef the VM definition
* @param model the model
* @param channel the channel
* @throws IOException Signals that an I/O exception has occurred.
* @throws TemplateException the template exception
* @throws ApiException the api exception
*/
public void reconcile(VmDefChanged event,
Map<String, Object> model, VmChannel channel)
public void reconcile(VmDefinition vmDef, Map<String, Object> model,
VmChannel channel)
throws IOException, TemplateException, ApiException {
// Secret needed at all?
var display = event.vmDefinition().fromVm("display").get();
var display = vmDef.fromVm("display").get();
if (!DataPath.<Boolean> get(display, "spice", "generateSecret")
.orElse(true)) {
return;
}
// Check if exists
var vmDef = event.vmDefinition();
ListOptions options = new ListOptions();
options.setLabelSelector("app.kubernetes.io/name=" + APP_NAME + ","
+ "app.kubernetes.io/component=" + DisplaySecret.NAME + ","

View file

@ -36,7 +36,6 @@ import java.util.logging.Logger;
import org.jdrupes.vmoperator.common.K8sV1ServiceStub;
import org.jdrupes.vmoperator.common.VmDefinition;
import org.jdrupes.vmoperator.manager.events.VmChannel;
import org.jdrupes.vmoperator.manager.events.VmDefChanged;
import org.jdrupes.vmoperator.util.GsonPtr;
import org.yaml.snakeyaml.LoaderOptions;
import org.yaml.snakeyaml.Yaml;
@ -69,14 +68,14 @@ import org.yaml.snakeyaml.constructor.SafeConstructor;
/**
* Reconcile.
*
* @param event the event
* @param vmDef the VM definition
* @param model the model
* @param channel the channel
* @throws IOException Signals that an I/O exception has occurred.
* @throws TemplateException the template exception
* @throws ApiException the api exception
*/
public void reconcile(VmDefChanged event,
public void reconcile(VmDefinition vmDef,
Map<String, Object> model, VmChannel channel)
throws IOException, TemplateException, ApiException {
// Check if to be generated
@ -95,7 +94,6 @@ import org.yaml.snakeyaml.constructor.SafeConstructor;
}
// Load balancer can also be turned off for VM
var vmDef = event.vmDefinition();
if (vmDef
.<Map<String, Map<String, String>>> fromSpec(LOAD_BALANCER_SERVICE)
.map(m -> m.isEmpty()).orElse(false)) {

View file

@ -36,7 +36,6 @@ import org.jdrupes.vmoperator.common.K8sV1SecretStub;
import org.jdrupes.vmoperator.common.VmDefinition;
import org.jdrupes.vmoperator.common.VmDefinition.RequestedVmState;
import org.jdrupes.vmoperator.manager.events.VmChannel;
import org.jdrupes.vmoperator.manager.events.VmDefChanged;
import org.yaml.snakeyaml.LoaderOptions;
import org.yaml.snakeyaml.Yaml;
import org.yaml.snakeyaml.constructor.SafeConstructor;
@ -62,14 +61,14 @@ import org.yaml.snakeyaml.constructor.SafeConstructor;
/**
* Reconcile the pod.
*
* @param event the event
* @param vmDef the vm def
* @param model the model
* @param channel the channel
* @throws IOException Signals that an I/O exception has occurred.
* @throws TemplateException the template exception
* @throws ApiException the api exception
*/
public void reconcile(VmDefChanged event, Map<String, Object> model,
public void reconcile(VmDefinition vmDef, Map<String, Object> model,
VmChannel channel)
throws IOException, TemplateException, ApiException {
// Don't do anything if stateful set is still in use (pre v3.4)
@ -78,7 +77,6 @@ import org.yaml.snakeyaml.constructor.SafeConstructor;
}
// Get pod stub.
var vmDef = event.vmDefinition();
var podStub = K8sV1PodStub.get(channel.client(), vmDef.namespace(),
vmDef.name());

View file

@ -38,8 +38,8 @@ import java.util.stream.Collectors;
import static org.jdrupes.vmoperator.common.Constants.APP_NAME;
import static org.jdrupes.vmoperator.common.Constants.VM_OP_NAME;
import org.jdrupes.vmoperator.common.K8sV1PvcStub;
import org.jdrupes.vmoperator.common.VmDefinition;
import org.jdrupes.vmoperator.manager.events.VmChannel;
import org.jdrupes.vmoperator.manager.events.VmDefChanged;
import org.jdrupes.vmoperator.util.DataPath;
import org.jdrupes.vmoperator.util.GsonPtr;
import org.yaml.snakeyaml.LoaderOptions;
@ -67,7 +67,7 @@ import org.yaml.snakeyaml.constructor.SafeConstructor;
/**
* Reconcile the PVCs.
*
* @param event the event
* @param vmDef the vm def
* @param model the model
* @param channel the channel
* @throws IOException Signals that an I/O exception has occurred.
@ -75,11 +75,9 @@ import org.yaml.snakeyaml.constructor.SafeConstructor;
* @throws ApiException the api exception
*/
@SuppressWarnings("PMD.AvoidDuplicateLiterals")
public void reconcile(VmDefChanged event, Map<String, Object> model,
public void reconcile(VmDefinition vmDef, Map<String, Object> model,
VmChannel channel)
throws IOException, TemplateException, ApiException {
var vmDef = event.vmDefinition();
// Existing disks
ListOptions listOpts = new ListOptions();
listOpts.setLabelSelector(
@ -92,7 +90,7 @@ import org.yaml.snakeyaml.constructor.SafeConstructor;
.collect(Collectors.toSet());
// Reconcile runner data pvc
reconcileRunnerDataPvc(event, model, channel, knownPvcs);
reconcileRunnerDataPvc(vmDef, model, channel, knownPvcs);
// Reconcile pvcs for defined disks
var diskDefs = vmDef.<List<Map<String, Object>>> fromVm("disks")
@ -117,17 +115,16 @@ import org.yaml.snakeyaml.constructor.SafeConstructor;
// Update PVC
model.put("disk", diskDef);
reconcileRunnerDiskPvc(event, model, channel);
reconcileRunnerDiskPvc(vmDef, model, channel);
}
model.remove("disk");
}
private void reconcileRunnerDataPvc(VmDefChanged event,
private void reconcileRunnerDataPvc(VmDefinition vmDef,
Map<String, Object> model, VmChannel channel,
Set<String> knownPvcs)
throws TemplateNotFoundException, MalformedTemplateNameException,
ParseException, IOException, TemplateException, ApiException {
var vmDef = event.vmDefinition();
// Look for old (sts generated) name.
var stsRunnerDataPvcName
@ -161,12 +158,10 @@ import org.yaml.snakeyaml.constructor.SafeConstructor;
}
}
private void reconcileRunnerDiskPvc(VmDefChanged event,
private void reconcileRunnerDiskPvc(VmDefinition vmDef,
Map<String, Object> model, VmChannel channel)
throws TemplateNotFoundException, MalformedTemplateNameException,
ParseException, IOException, TemplateException, ApiException {
var vmDef = event.vmDefinition();
// Generate PVC
@SuppressWarnings("unchecked")
var diskDef = (Map<String, Object>) model.get("disk");

View file

@ -44,10 +44,12 @@ import java.util.Optional;
import java.util.logging.Level;
import org.jdrupes.vmoperator.common.Convertions;
import org.jdrupes.vmoperator.common.K8sObserver;
import org.jdrupes.vmoperator.common.K8sObserver.ResponseType;
import org.jdrupes.vmoperator.common.VmDefinition;
import org.jdrupes.vmoperator.common.VmDefinition.Assignment;
import org.jdrupes.vmoperator.common.VmPool;
import org.jdrupes.vmoperator.manager.events.GetPools;
import org.jdrupes.vmoperator.manager.events.PodChanged;
import org.jdrupes.vmoperator.manager.events.ResetVm;
import org.jdrupes.vmoperator.manager.events.VmChannel;
import org.jdrupes.vmoperator.manager.events.VmDefChanged;
@ -213,20 +215,57 @@ public class Reconciler extends Component {
return;
}
// Reconcile
reconcile(event, channel);
}
private void reconcile(VmDefChanged event, VmChannel channel)
throws TemplateModelException, ApiException, IOException,
TemplateException {
// Create model for processing templates
Map<String, Object> model = prepareModel(event.vmDefinition());
var vmDef = event.vmDefinition();
Map<String, Object> model = prepareModel(vmDef);
cmReconciler.reconcile(model, channel, event.specChanged());
// The remaining reconcilers depend only on changes of the spec part.
if (!event.specChanged()) {
return;
}
dsReconciler.reconcile(event, model, channel);
dsReconciler.reconcile(vmDef, model, channel);
// Manage (eventual) removal of stateful set.
stsReconciler.reconcile(event, model, channel);
pvcReconciler.reconcile(event, model, channel);
podReconciler.reconcile(event, model, channel);
lbReconciler.reconcile(event, model, channel);
stsReconciler.reconcile(vmDef, model, channel);
pvcReconciler.reconcile(vmDef, model, channel);
podReconciler.reconcile(vmDef, model, channel);
lbReconciler.reconcile(vmDef, model, channel);
}
/**
* On pod changed.
*
* @param event the event
* @param channel the channel
* @throws ApiException the api exception
* @throws IOException Signals that an I/O exception has occurred.
* @throws TemplateException the template exception
*/
@Handler
public void onPodChanged(PodChanged event, VmChannel channel)
throws ApiException, IOException, TemplateException {
if (event.type() != ResponseType.DELETED) {
// Nothing to reconcile
return;
}
// If the pod was deleted, it may be necessary to recreate it
var vmDef = channel.vmDefinition();
Map<String, Object> model = prepareModel(vmDef);
// Call all steps because they may augment the model
cmReconciler.reconcile(model, channel, false);
dsReconciler.reconcile(vmDef, model, channel);
stsReconciler.reconcile(vmDef, model, channel);
pvcReconciler.reconcile(vmDef, model, channel);
podReconciler.reconcile(vmDef, model, channel);
}
/**

View file

@ -27,9 +27,9 @@ import java.io.IOException;
import java.util.Map;
import java.util.logging.Logger;
import org.jdrupes.vmoperator.common.K8sV1StatefulSetStub;
import org.jdrupes.vmoperator.common.VmDefinition;
import org.jdrupes.vmoperator.common.VmDefinition.RequestedVmState;
import org.jdrupes.vmoperator.manager.events.VmChannel;
import org.jdrupes.vmoperator.manager.events.VmDefChanged;
/**
* Before version 3.4, the pod running the VM was created by a stateful set.
@ -54,7 +54,7 @@ import org.jdrupes.vmoperator.manager.events.VmDefChanged;
/**
* Reconcile stateful set.
*
* @param event the event
* @param vmDef the VM definition
* @param model the model
* @param channel the channel
* @throws IOException Signals that an I/O exception has occurred.
@ -62,14 +62,14 @@ import org.jdrupes.vmoperator.manager.events.VmDefChanged;
* @throws ApiException the api exception
*/
@SuppressWarnings("PMD.AvoidLiteralsInIfCondition")
public void reconcile(VmDefChanged event, Map<String, Object> model,
public void reconcile(VmDefinition vmDef, Map<String, Object> model,
VmChannel channel)
throws IOException, TemplateException, ApiException {
model.put("usingSts", false);
// If exists, delete when not running or supposed to be not running.
var stsStub = K8sV1StatefulSetStub.get(channel.client(),
event.vmDefinition().namespace(), event.vmDefinition().name());
vmDef.namespace(), vmDef.name());
if (stsStub.model().isEmpty()) {
return;
}
@ -88,7 +88,7 @@ import org.jdrupes.vmoperator.manager.events.VmDefChanged;
// Check if VM is supposed to be stopped. If so,
// set replicas to 0. This is the first step of the transition,
// the stateful set will be deleted when the VM is restarted.
if (event.vmDefinition().vmState() == RequestedVmState.RUNNING) {
if (vmDef.vmState() == RequestedVmState.RUNNING) {
return;
}