Generate load balancer service instead of unspecific service.

This commit is contained in:
Michael Lipp 2023-08-17 20:24:10 +02:00
parent f4e8318b6e
commit 4ed5168591
6 changed files with 44 additions and 29 deletions

View file

@ -924,9 +924,9 @@ spec:
update: update:
type: boolean type: boolean
default: true default: true
additionalServiceMetadata: loadBalancerService:
description: >- description: >-
Data to be merged with the additionalServiceMetadata Data to be merged with the loadBalancerService
defined in the manager's configuration. Values defined in the manager's configuration. Values
specified here override values from the manager's specified here override values from the manager's
configuration. If the value of a label or an annotation configuration. If the value of a label or an annotation

View file

@ -9,7 +9,7 @@ spec:
path: vmoperator/org.jdrupes.vmoperator.runner.qemu-arch path: vmoperator/org.jdrupes.vmoperator.runner.qemu-arch
pullPolicy: Always pullPolicy: Always
additionalServiceMetadata: loadBalancerService:
labels: labels:
test2: null test2: null
test3: added test3: added

View file

@ -14,11 +14,13 @@
# the resource properties. # the resource properties.
ramOvercommit: 1.5 ramOvercommit: 1.5
# Additional metadata (labels and annotations) to be merged # If defined, causes a load balancer service to be created.
# into the service. Must be provided as nested YAML # May be a boolean or a string with nested yaml that
# additionalServiceMetadata: | # defines additional labels or annotations to be merged
# into the service.
# loadBalancerService: |
# labels: {} # labels: {}
# annotations: {} # annotations: {}
# Only for development: # Explicitly specify the namespace to be managed (only for development).
# namespace: vmop-dev # namespace: vmop-dev

View file

@ -17,10 +17,10 @@ metadata:
controller: false controller: false
spec: spec:
type: LoadBalancer
ports: ports:
- name: spice - name: spice
port: ${ cr.spec.vm.display.spice.port.asInt?c } port: ${ cr.spec.vm.display.spice.port.asInt?c }
clusterIP: None
selector: selector:
app.kubernetes.io/name: ${ cr.metadata.name.asString } app.kubernetes.io/name: ${ constants.APP_NAME }
app.kubernetes.io/instance: ${ cr.metadata.name.asString } app.kubernetes.io/instance: ${ cr.metadata.name.asString }

View file

@ -29,7 +29,7 @@ import io.kubernetes.client.util.generic.dynamic.DynamicKubernetesObject;
import io.kubernetes.client.util.generic.dynamic.Dynamics; import io.kubernetes.client.util.generic.dynamic.Dynamics;
import java.io.IOException; import java.io.IOException;
import java.io.StringWriter; import java.io.StringWriter;
import java.util.HashMap; import java.util.Collections;
import java.util.Map; import java.util.Map;
import java.util.Optional; import java.util.Optional;
import java.util.logging.Logger; import java.util.logging.Logger;
@ -41,8 +41,9 @@ import org.yaml.snakeyaml.constructor.SafeConstructor;
* Delegee for reconciling the service * Delegee for reconciling the service
*/ */
@SuppressWarnings("PMD.DataflowAnomalyAnalysis") @SuppressWarnings("PMD.DataflowAnomalyAnalysis")
/* default */ class ServiceReconciler { /* default */ class LoadBalancerReconciler {
private static final String LOAD_BALANCER_SERVICE = "loadBalancerService";
private static final String METADATA private static final String METADATA
= V1APIService.SERIALIZED_NAME_METADATA; = V1APIService.SERIALIZED_NAME_METADATA;
private static final String LABELS = V1ObjectMeta.SERIALIZED_NAME_LABELS; private static final String LABELS = V1ObjectMeta.SERIALIZED_NAME_LABELS;
@ -56,7 +57,7 @@ import org.yaml.snakeyaml.constructor.SafeConstructor;
* *
* @param fmConfig the fm config * @param fmConfig the fm config
*/ */
public ServiceReconciler(Configuration fmConfig) { public LoadBalancerReconciler(Configuration fmConfig) {
this.fmConfig = fmConfig; this.fmConfig = fmConfig;
} }
@ -73,41 +74,53 @@ import org.yaml.snakeyaml.constructor.SafeConstructor;
public void reconcile(VmDefChanged event, public void reconcile(VmDefChanged event,
Map<String, Object> model, VmChannel channel) Map<String, Object> model, VmChannel channel)
throws IOException, TemplateException, ApiException { throws IOException, TemplateException, ApiException {
// Get API // Check if to be generated
DynamicKubernetesApi svcApi = new DynamicKubernetesApi("", "v1", @SuppressWarnings("unchecked")
"services", channel.client()); var lbs = Optional.of(model)
.map(m -> (Map<String, Object>) m.get("config"))
.map(c -> c.get(LOAD_BALANCER_SERVICE)).orElse(Boolean.FALSE);
if (lbs instanceof Boolean isOn && !isOn) {
return;
}
if (!(lbs instanceof String)) {
logger.warning(() -> "\"" + LOAD_BALANCER_SERVICE
+ "\" in configuration must be boolean or string but is "
+ lbs.getClass() + ".");
return;
}
// Combine template and data and parse result // Combine template and data and parse result
var fmTemplate = fmConfig.getTemplate("runnerService.ftl.yaml"); var fmTemplate = fmConfig.getTemplate("runnerLoadBalancer.ftl.yaml");
StringWriter out = new StringWriter(); StringWriter out = new StringWriter();
fmTemplate.process(model, out); fmTemplate.process(model, out);
// Avoid Yaml.load due to // Avoid Yaml.load due to
// https://github.com/kubernetes-client/java/issues/2741 // https://github.com/kubernetes-client/java/issues/2741
var svcDef = Dynamics.newFromYaml( var svcDef = Dynamics.newFromYaml(
new Yaml(new SafeConstructor(new LoaderOptions())), out.toString()); new Yaml(new SafeConstructor(new LoaderOptions())), out.toString());
mergeMetadata(svcDef, model, channel); mergeMetadata(svcDef, lbs, channel);
// Apply // Apply
DynamicKubernetesApi svcApi = new DynamicKubernetesApi("", "v1",
"services", channel.client());
K8s.apply(svcApi, svcDef, svcDef.getRaw().toString()); K8s.apply(svcApi, svcDef, svcDef.getRaw().toString());
} }
private void mergeMetadata(DynamicKubernetesObject svcDef,
Map<String, Object> model, VmChannel channel) {
// Get metadata from config
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
var asmData = Optional.of(model) private void mergeMetadata(DynamicKubernetesObject svcDef,
.map(m -> (Map<String, Object>) m.get("config")) Object lbsConfig, VmChannel channel) {
.map(c -> (String) c.get("additionalServiceMetadata")) // Get metadata from config
.map(y -> (Map<String, Object>) new Yaml( Map<String, Object> asmData = Collections.emptyMap();
new SafeConstructor(new LoaderOptions())).load(y)) if (lbsConfig instanceof String config) {
.orElseGet(() -> new HashMap<String, Object>()); asmData = (Map<String, Object>) new Yaml(
new SafeConstructor(new LoaderOptions())).load(config);
}
var json = channel.client().getJSON(); var json = channel.client().getJSON();
JsonObject cfgMeta JsonObject cfgMeta
= json.deserialize(json.serialize(asmData), JsonObject.class); = json.deserialize(json.serialize(asmData), JsonObject.class);
// Get metadata from VM definition // Get metadata from VM definition
var vmMeta = GsonPtr.to(channel.vmDefinition()).to("spec") var vmMeta = GsonPtr.to(channel.vmDefinition()).to("spec")
.get(JsonObject.class, "additionalServiceMetadata") .get(JsonObject.class, LOAD_BALANCER_SERVICE)
.map(JsonObject::deepCopy).orElseGet(() -> new JsonObject()); .map(JsonObject::deepCopy).orElseGet(() -> new JsonObject());
// Merge Data from VM definition into config data // Merge Data from VM definition into config data

View file

@ -60,7 +60,7 @@ public class Reconciler extends Component {
private final Configuration fmConfig; private final Configuration fmConfig;
private final CmReconciler cmReconciler; private final CmReconciler cmReconciler;
private final StsReconciler stsReconciler; private final StsReconciler stsReconciler;
private final ServiceReconciler serviceReconciler; private final LoadBalancerReconciler lbReconciler;
@SuppressWarnings("PMD.UseConcurrentHashMap") @SuppressWarnings("PMD.UseConcurrentHashMap")
private final Map<String, Object> config = new HashMap<>(); private final Map<String, Object> config = new HashMap<>();
@ -84,7 +84,7 @@ public class Reconciler extends Component {
cmReconciler = new CmReconciler(fmConfig); cmReconciler = new CmReconciler(fmConfig);
stsReconciler = new StsReconciler(fmConfig); stsReconciler = new StsReconciler(fmConfig);
serviceReconciler = new ServiceReconciler(fmConfig); lbReconciler = new LoadBalancerReconciler(fmConfig);
} }
/** /**
@ -137,7 +137,7 @@ public class Reconciler extends Component {
var configMap = cmReconciler.reconcile(event, model, channel); var configMap = cmReconciler.reconcile(event, model, channel);
model.put("cm", configMap.getRaw()); model.put("cm", configMap.getRaw());
stsReconciler.reconcile(event, model, channel); stsReconciler.reconcile(event, model, channel);
serviceReconciler.reconcile(event, model, channel); lbReconciler.reconcile(event, model, channel);
} }
private Map<String, Object> prepareModel(JsonObject vmDef) private Map<String, Object> prepareModel(JsonObject vmDef)