Skip to content

Commit fe4680d

Browse files
committed
Merge branch 'main' into testing
2 parents c996cf7 + 084bdd1 commit fe4680d

File tree

11 files changed

+336
-87
lines changed

11 files changed

+336
-87
lines changed

org.jdrupes.vmoperator.manager/resources/org/jdrupes/vmoperator/manager/runnerPod.ftl.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ metadata:
1111
annotations:
1212
# Triggers update of config map mounted in pod
1313
# See https://ahmet.im/blog/kubernetes-secret-volumes-delay/
14-
vmrunner.jdrupes.org/cmVersion: "${ cm.metadata.resourceVersion }"
14+
vmrunner.jdrupes.org/cmVersion: "${ configMapResourceVersion }"
1515
vmoperator.jdrupes.org/version: ${ managerVersion }
1616
ownerReferences:
1717
- apiVersion: ${ cr.apiVersion() }

org.jdrupes.vmoperator.manager/src/org/jdrupes/vmoperator/manager/ConfigMapReconciler.java

Lines changed: 58 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -19,19 +19,29 @@
1919
package org.jdrupes.vmoperator.manager;
2020

2121
import com.google.gson.JsonObject;
22+
import freemarker.template.AdapterTemplateModel;
2223
import freemarker.template.Configuration;
2324
import freemarker.template.TemplateException;
25+
import freemarker.template.TemplateMethodModelEx;
26+
import freemarker.template.TemplateModel;
27+
import freemarker.template.TemplateModelException;
28+
import freemarker.template.utility.DeepUnwrap;
2429
import io.kubernetes.client.custom.V1Patch;
2530
import io.kubernetes.client.openapi.ApiClient;
2631
import io.kubernetes.client.openapi.ApiException;
32+
import io.kubernetes.client.openapi.models.V1ObjectMeta;
2733
import io.kubernetes.client.util.generic.dynamic.DynamicKubernetesApi;
2834
import io.kubernetes.client.util.generic.dynamic.DynamicKubernetesObject;
2935
import io.kubernetes.client.util.generic.dynamic.Dynamics;
3036
import io.kubernetes.client.util.generic.options.ListOptions;
3137
import io.kubernetes.client.util.generic.options.PatchOptions;
3238
import java.io.IOException;
3339
import java.io.StringWriter;
40+
import java.util.HashMap;
41+
import java.util.List;
3442
import java.util.Map;
43+
import java.util.Objects;
44+
import java.util.Optional;
3545
import java.util.logging.Logger;
3646
import org.jdrupes.vmoperator.common.K8s;
3747
import static org.jdrupes.vmoperator.manager.Constants.APP_NAME;
@@ -66,48 +76,59 @@ public ConfigMapReconciler(Configuration fmConfig) {
6676
*
6777
* @param model the model
6878
* @param channel the channel
69-
* @return the dynamic kubernetes object
7079
* @throws IOException Signals that an I/O exception has occurred.
7180
* @throws TemplateException the template exception
7281
* @throws ApiException the api exception
7382
*/
74-
public Map<String, Object> reconcile(Map<String, Object> model,
75-
VmChannel channel)
83+
@SuppressWarnings("PMD.AvoidDuplicateLiterals")
84+
public void reconcile(Map<String, Object> model, VmChannel channel)
7685
throws IOException, TemplateException, ApiException {
7786
// Combine template and data and parse result
87+
model.put("adjustCloudInitMeta", adjustCloudInitMetaModel);
7888
var fmTemplate = fmConfig.getTemplate("runnerConfig.ftl.yaml");
7989
StringWriter out = new StringWriter();
8090
fmTemplate.process(model, out);
8191
// Avoid Yaml.load due to
8292
// https://github.com/kubernetes-client/java/issues/2741
83-
var mapDef = Dynamics.newFromYaml(
93+
var newCm = Dynamics.newFromYaml(
8494
new Yaml(new SafeConstructor(new LoaderOptions())), out.toString());
8595

8696
// Maybe override logging.properties from reconciler configuration.
8797
DataPath.<String> get(model, "reconciler", "loggingProperties")
8898
.ifPresent(props -> {
89-
GsonPtr.to(mapDef.getRaw()).getAs(JsonObject.class, "data")
99+
GsonPtr.to(newCm.getRaw()).getAs(JsonObject.class, "data")
90100
.get().addProperty("logging.properties", props);
91101
});
92102

93103
// Maybe override logging.properties from VM definition.
94104
DataPath.<String> get(model, "cr", "spec", "loggingProperties")
95105
.ifPresent(props -> {
96-
GsonPtr.to(mapDef.getRaw()).getAs(JsonObject.class, "data")
106+
GsonPtr.to(newCm.getRaw()).getAs(JsonObject.class, "data")
97107
.get().addProperty("logging.properties", props);
98108
});
99109

100-
// Get API
110+
// Look for changes
111+
var oldCm = channel
112+
.associated(getClass(), DynamicKubernetesObject.class).orElse(null);
113+
channel.setAssociated(getClass(), newCm);
114+
if (oldCm != null && Objects.equals(oldCm.getRaw().get("data"),
115+
newCm.getRaw().get("data"))) {
116+
logger.finer(() -> "No changes in config map for "
117+
+ DataPath.<String> get(model, "cr", "name").get());
118+
model.put("configMapResourceVersion",
119+
oldCm.getMetadata().getResourceVersion());
120+
return;
121+
}
122+
123+
// Get API and update
101124
DynamicKubernetesApi cmApi = new DynamicKubernetesApi("", "v1",
102125
"configmaps", channel.client());
103126

104127
// Apply and maybe force pod update
105-
var newState = K8s.apply(cmApi, mapDef, mapDef.getRaw().toString());
106-
maybeForceUpdate(channel.client(), newState);
107-
@SuppressWarnings("unchecked")
108-
var res = (Map<String, Object>) channel.client().getJSON().getGson()
109-
.fromJson(newState.getRaw(), Map.class);
110-
return res;
128+
var updatedCm = K8s.apply(cmApi, newCm, newCm.getRaw().toString());
129+
maybeForceUpdate(channel.client(), updatedCm);
130+
model.put("configMapResourceVersion",
131+
updatedCm.getMetadata().getResourceVersion());
111132
}
112133

113134
/**
@@ -153,4 +174,28 @@ private void maybeForceUpdate(ApiClient client,
153174
}
154175
}
155176

177+
private final TemplateMethodModelEx adjustCloudInitMetaModel
178+
= new TemplateMethodModelEx() {
179+
@Override
180+
@SuppressWarnings("PMD.PreserveStackTrace")
181+
public Object exec(@SuppressWarnings("rawtypes") List arguments)
182+
throws TemplateModelException {
183+
@SuppressWarnings("unchecked")
184+
var res = new HashMap<>((Map<String, Object>) DeepUnwrap
185+
.unwrap((TemplateModel) arguments.get(0)));
186+
var metadata
187+
= (V1ObjectMeta) ((AdapterTemplateModel) arguments.get(1))
188+
.getAdaptedObject(Object.class);
189+
if (!res.containsKey("instance-id")) {
190+
res.put("instance-id",
191+
Optional.ofNullable(metadata.getGeneration())
192+
.map(s -> "v" + s).orElse("v1"));
193+
}
194+
if (!res.containsKey("local-hostname")) {
195+
res.put("local-hostname", metadata.getName());
196+
}
197+
return res;
198+
}
199+
};
200+
156201
}

org.jdrupes.vmoperator.manager/src/org/jdrupes/vmoperator/manager/Reconciler.java

Lines changed: 1 addition & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -27,12 +27,9 @@
2727
import freemarker.template.TemplateException;
2828
import freemarker.template.TemplateExceptionHandler;
2929
import freemarker.template.TemplateMethodModelEx;
30-
import freemarker.template.TemplateModel;
3130
import freemarker.template.TemplateModelException;
32-
import freemarker.template.utility.DeepUnwrap;
3331
import io.kubernetes.client.custom.Quantity;
3432
import io.kubernetes.client.openapi.ApiException;
35-
import io.kubernetes.client.openapi.models.V1ObjectMeta;
3633
import io.kubernetes.client.util.generic.options.ListOptions;
3734
import java.io.IOException;
3835
import java.lang.reflect.Modifier;
@@ -226,13 +223,12 @@ public void onVmDefChanged(VmDefChanged event, VmChannel channel)
226223
// Create model for processing templates
227224
Map<String, Object> model
228225
= prepareModel(channel.client(), event.vmDefinition());
229-
var configMap = cmReconciler.reconcile(model, channel);
226+
cmReconciler.reconcile(model, channel);
230227

231228
// The remaining reconcilers depend only on changes of the spec part.
232229
if (!event.specChanged()) {
233230
return;
234231
}
235-
model.put("cm", configMap);
236232
dsReconciler.reconcile(event, model, channel);
237233
// Manage (eventual) removal of stateful set.
238234
stsReconciler.reconcile(event, model, channel);
@@ -279,7 +275,6 @@ private Map<String, Object> prepareModel(K8sClient client,
279275
model.put("parseQuantity", parseQuantityModel);
280276
model.put("formatMemory", formatMemoryModel);
281277
model.put("imageLocation", imgageLocationModel);
282-
model.put("adjustCloudInitMeta", adjustCloudInitMetaModel);
283278
model.put("toJson", toJsonModel);
284279
return model;
285280
}
@@ -422,30 +417,6 @@ public Object exec(@SuppressWarnings("rawtypes") List arguments)
422417
}
423418
};
424419

425-
private final TemplateMethodModelEx adjustCloudInitMetaModel
426-
= new TemplateMethodModelEx() {
427-
@Override
428-
@SuppressWarnings("PMD.PreserveStackTrace")
429-
public Object exec(@SuppressWarnings("rawtypes") List arguments)
430-
throws TemplateModelException {
431-
@SuppressWarnings("unchecked")
432-
var res = new HashMap<>((Map<String, Object>) DeepUnwrap
433-
.unwrap((TemplateModel) arguments.get(0)));
434-
var metadata
435-
= (V1ObjectMeta) ((AdapterTemplateModel) arguments.get(1))
436-
.getAdaptedObject(Object.class);
437-
if (!res.containsKey("instance-id")) {
438-
res.put("instance-id",
439-
Optional.ofNullable(metadata.getResourceVersion())
440-
.map(s -> "v" + s).orElse("v1"));
441-
}
442-
if (!res.containsKey("local-hostname")) {
443-
res.put("local-hostname", metadata.getName());
444-
}
445-
return res;
446-
}
447-
};
448-
449420
private final TemplateMethodModelEx toJsonModel
450421
= new TemplateMethodModelEx() {
451422
@Override

org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/AgentConnector.java

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -92,8 +92,12 @@ protected void configureConnection(List<String> command, String chardev) {
9292
*/
9393
@Handler
9494
public void onVserportChanged(VserportChangeEvent event) {
95-
if (event.id().equals(channelId) && event.isOpen()) {
96-
agentConnected();
95+
if (event.id().equals(channelId)) {
96+
if (event.isOpen()) {
97+
agentConnected();
98+
} else {
99+
agentDisconnected();
100+
}
97101
}
98102
}
99103

@@ -105,4 +109,14 @@ public void onVserportChanged(VserportChangeEvent event) {
105109
protected void agentConnected() {
106110
// Default is to do nothing.
107111
}
112+
113+
/**
114+
* Called when the agent in the VM closes the connection. The
115+
* default implementation does nothing.
116+
*/
117+
@SuppressWarnings("PMD.EmptyMethodInAbstractClassShouldBeAbstract")
118+
protected void agentDisconnected() {
119+
// Default is to do nothing.
120+
}
121+
108122
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
/*
2+
* VM-Operator
3+
* Copyright (C) 2023 Michael N. Lipp
4+
*
5+
* This program is free software: you can redistribute it and/or modify
6+
* it under the terms of the GNU Affero General Public License as
7+
* published by the Free Software Foundation, either version 3 of the
8+
* License, or (at your option) any later version.
9+
*
10+
* This program is distributed in the hope that it will be useful,
11+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13+
* GNU Affero General Public License for more details.
14+
*
15+
* You should have received a copy of the GNU Affero General Public License
16+
* along with this program. If not, see <https://www.gnu.org/licenses/>.
17+
*/
18+
19+
package org.jdrupes.vmoperator.runner.qemu;
20+
21+
/**
22+
* Some constants.
23+
*/
24+
@SuppressWarnings("PMD.DataClass")
25+
public class Constants extends org.jdrupes.vmoperator.common.Constants {
26+
27+
/**
28+
* Process names.
29+
*/
30+
public static class ProcessName {
31+
32+
/** The Constant QEMU. */
33+
public static final String QEMU = "qemu";
34+
35+
/** The Constant SWTPM. */
36+
public static final String SWTPM = "swtpm";
37+
38+
/** The Constant CLOUD_INIT_IMG. */
39+
public static final String CLOUD_INIT_IMG = "cloudInitImg";
40+
}
41+
42+
}

org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/DisplayController.java

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -116,9 +116,11 @@ private void configureLogin() {
116116
@Handler
117117
@SuppressWarnings("PMD.EmptyCatchBlock")
118118
public void onFileChanged(FileChanged event) {
119-
if (event.path().equals(configDir.resolve(DisplaySecret.PASSWORD))
120-
&& canBeUpdated) {
121-
configurePassword();
119+
if (event.path().equals(configDir.resolve(DisplaySecret.PASSWORD))) {
120+
logger.fine(() -> "Display password updated");
121+
if (canBeUpdated) {
122+
configurePassword();
123+
}
122124
}
123125
}
124126

0 commit comments

Comments
 (0)