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:
type: boolean
default: true
additionalServiceMetadata:
loadBalancerService:
description: >-
Data to be merged with the additionalServiceMetadata
Data to be merged with the loadBalancerService
defined in the manager's configuration. Values
specified here override values from the manager's
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
pullPolicy: Always
additionalServiceMetadata:
loadBalancerService:
labels:
test2: null
test3: added

View file

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

View file

@ -17,10 +17,10 @@ metadata:
controller: false
spec:
type: LoadBalancer
ports:
- name: spice
port: ${ cr.spec.vm.display.spice.port.asInt?c }
clusterIP: None
selector:
app.kubernetes.io/name: ${ cr.metadata.name.asString }
app.kubernetes.io/name: ${ constants.APP_NAME }
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 java.io.IOException;
import java.io.StringWriter;
import java.util.HashMap;
import java.util.Collections;
import java.util.Map;
import java.util.Optional;
import java.util.logging.Logger;
@ -41,8 +41,9 @@ import org.yaml.snakeyaml.constructor.SafeConstructor;
* Delegee for reconciling the service
*/
@SuppressWarnings("PMD.DataflowAnomalyAnalysis")
/* default */ class ServiceReconciler {
/* default */ class LoadBalancerReconciler {
private static final String LOAD_BALANCER_SERVICE = "loadBalancerService";
private static final String METADATA
= V1APIService.SERIALIZED_NAME_METADATA;
private static final String LABELS = V1ObjectMeta.SERIALIZED_NAME_LABELS;
@ -56,7 +57,7 @@ import org.yaml.snakeyaml.constructor.SafeConstructor;
*
* @param fmConfig the fm config
*/
public ServiceReconciler(Configuration fmConfig) {
public LoadBalancerReconciler(Configuration fmConfig) {
this.fmConfig = fmConfig;
}
@ -73,41 +74,53 @@ import org.yaml.snakeyaml.constructor.SafeConstructor;
public void reconcile(VmDefChanged event,
Map<String, Object> model, VmChannel channel)
throws IOException, TemplateException, ApiException {
// Get API
DynamicKubernetesApi svcApi = new DynamicKubernetesApi("", "v1",
"services", channel.client());
// Check if to be generated
@SuppressWarnings("unchecked")
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
var fmTemplate = fmConfig.getTemplate("runnerService.ftl.yaml");
var fmTemplate = fmConfig.getTemplate("runnerLoadBalancer.ftl.yaml");
StringWriter out = new StringWriter();
fmTemplate.process(model, out);
// Avoid Yaml.load due to
// https://github.com/kubernetes-client/java/issues/2741
var svcDef = Dynamics.newFromYaml(
new Yaml(new SafeConstructor(new LoaderOptions())), out.toString());
mergeMetadata(svcDef, model, channel);
mergeMetadata(svcDef, lbs, channel);
// Apply
DynamicKubernetesApi svcApi = new DynamicKubernetesApi("", "v1",
"services", channel.client());
K8s.apply(svcApi, svcDef, svcDef.getRaw().toString());
}
private void mergeMetadata(DynamicKubernetesObject svcDef,
Map<String, Object> model, VmChannel channel) {
// Get metadata from config
@SuppressWarnings("unchecked")
var asmData = Optional.of(model)
.map(m -> (Map<String, Object>) m.get("config"))
.map(c -> (String) c.get("additionalServiceMetadata"))
.map(y -> (Map<String, Object>) new Yaml(
new SafeConstructor(new LoaderOptions())).load(y))
.orElseGet(() -> new HashMap<String, Object>());
private void mergeMetadata(DynamicKubernetesObject svcDef,
Object lbsConfig, VmChannel channel) {
// Get metadata from config
Map<String, Object> asmData = Collections.emptyMap();
if (lbsConfig instanceof String config) {
asmData = (Map<String, Object>) new Yaml(
new SafeConstructor(new LoaderOptions())).load(config);
}
var json = channel.client().getJSON();
JsonObject cfgMeta
= json.deserialize(json.serialize(asmData), JsonObject.class);
// Get metadata from VM definition
var vmMeta = GsonPtr.to(channel.vmDefinition()).to("spec")
.get(JsonObject.class, "additionalServiceMetadata")
.get(JsonObject.class, LOAD_BALANCER_SERVICE)
.map(JsonObject::deepCopy).orElseGet(() -> new JsonObject());
// 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 CmReconciler cmReconciler;
private final StsReconciler stsReconciler;
private final ServiceReconciler serviceReconciler;
private final LoadBalancerReconciler lbReconciler;
@SuppressWarnings("PMD.UseConcurrentHashMap")
private final Map<String, Object> config = new HashMap<>();
@ -84,7 +84,7 @@ public class Reconciler extends Component {
cmReconciler = new CmReconciler(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);
model.put("cm", configMap.getRaw());
stsReconciler.reconcile(event, model, channel);
serviceReconciler.reconcile(event, model, channel);
lbReconciler.reconcile(event, model, channel);
}
private Map<String, Object> prepareModel(JsonObject vmDef)