Working creation and removal.
This commit is contained in:
parent
954780e40f
commit
dc1962af79
5 changed files with 316 additions and 85 deletions
|
|
@ -144,6 +144,7 @@ data:
|
||||||
</#if>
|
</#if>
|
||||||
- type: raw
|
- type: raw
|
||||||
resource: /dev/disk-${ name }
|
resource: /dev/disk-${ name }
|
||||||
|
<#assign drvCounter = drvCounter + 1/>
|
||||||
</#list>
|
</#list>
|
||||||
|
|
||||||
display:
|
display:
|
||||||
|
|
|
||||||
|
|
@ -23,16 +23,23 @@ spec:
|
||||||
# name: dev-tun
|
# name: dev-tun
|
||||||
# - mountPath: /sys/fs/cgroup
|
# - mountPath: /sys/fs/cgroup
|
||||||
# name: cgroup
|
# name: cgroup
|
||||||
- mountPath: /etc/opt/vmrunner
|
- name: config
|
||||||
name: config
|
mountPath: /etc/opt/vmrunner
|
||||||
- mountPath: /var/local/vm-data
|
- name: vm-data
|
||||||
name: vm-data
|
mountPath: /var/local/vm-data
|
||||||
<#--
|
|
||||||
volumeDevices:
|
volumeDevices:
|
||||||
{{- range $index, $disk := .Values.vm.disks }}
|
<#assign diskCounter = 0/>
|
||||||
- devicePath: /dev/disk-{{ $index }}
|
<#list cr.spec.vm.disks.asList() as disk>
|
||||||
name: disk-{{ $index }}
|
<#if disk.volumeClaimTemplate.metadata??
|
||||||
{{- end }}
|
&& disk.volumeClaimTemplate.metadata.name??>
|
||||||
|
<#assign name = disk.volumeClaimTemplate.metadata.name.asString>
|
||||||
|
<#else>
|
||||||
|
<#assign name = "" + diskCounter>
|
||||||
|
</#if>
|
||||||
|
- name: disk-${ name }
|
||||||
|
devicePath: /dev/disk-${ name }
|
||||||
|
<#assign diskCounter = diskCounter + 1/>
|
||||||
|
</#list>
|
||||||
securityContext:
|
securityContext:
|
||||||
privileged: true
|
privileged: true
|
||||||
volumes:
|
volumes:
|
||||||
|
|
@ -50,16 +57,23 @@ spec:
|
||||||
# path: /sys/fs/cgroup
|
# path: /sys/fs/cgroup
|
||||||
- name: config
|
- name: config
|
||||||
configMap:
|
configMap:
|
||||||
name: {{ $.Release.Name }}
|
name: ${ cr.metadata.name.asString }
|
||||||
- name: vm-data
|
- name: vm-data
|
||||||
hostPath:
|
|
||||||
path: /var/local/vmrunner/{{ .Release.Name }}
|
|
||||||
{{- range $index, $disk := .Values.vm.disks }}
|
|
||||||
- name: disk-{{ $index }}
|
|
||||||
persistentVolumeClaim:
|
persistentVolumeClaim:
|
||||||
claimName: {{ $.Release.Name }}-pvc-{{ $index }}
|
claimName: ${ cr.metadata.name.asString }-data
|
||||||
{{- end }}
|
<#assign diskCounter = 0/>
|
||||||
|
<#list cr.spec.vm.disks.asList() as disk>
|
||||||
|
<#if disk.volumeClaimTemplate.metadata??
|
||||||
|
&& disk.volumeClaimTemplate.metadata.name??>
|
||||||
|
<#assign name = disk.volumeClaimTemplate.metadata.name.asString>
|
||||||
|
<#else>
|
||||||
|
<#assign name = "" + diskCounter>
|
||||||
|
</#if>
|
||||||
|
- name: disk-${ name }
|
||||||
|
persistentVolumeClaim:
|
||||||
|
claimName: ${ cr.metadata.name.asString }-disk-${ name }
|
||||||
|
<#assign diskCounter = diskCounter + 1/>
|
||||||
|
</#list>
|
||||||
hostNetwork: true
|
hostNetwork: true
|
||||||
terminationGracePeriodSeconds: 60
|
terminationGracePeriodSeconds: ${ (cr.spec.vm.powerdownTimeout.asInt + 5)?c }
|
||||||
restartPolicy: Never
|
restartPolicy: Never
|
||||||
-->
|
|
||||||
|
|
@ -0,0 +1,16 @@
|
||||||
|
kind: PersistentVolumeClaim
|
||||||
|
apiVersion: v1
|
||||||
|
metadata:
|
||||||
|
namespace: ${ cr.metadata.namespace.asString }
|
||||||
|
name: ${ cr.metadata.name.asString + "-data" }
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/name: ${ constants.APP_NAME }
|
||||||
|
app.kubernetes.io/instance: ${ cr.metadata.name.asString }
|
||||||
|
app.kubernetes.io/managed-by: ${ constants.VM_OP_NAME }
|
||||||
|
|
||||||
|
spec:
|
||||||
|
accessModes:
|
||||||
|
- ReadWriteOnce
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
storage: 1Mi
|
||||||
|
|
@ -0,0 +1,136 @@
|
||||||
|
/*
|
||||||
|
* VM-Operator
|
||||||
|
* Copyright (C) 2023 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
|
||||||
|
* published by the Free Software Foundation, either version 3 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jdrupes.vmoperator.manager;
|
||||||
|
|
||||||
|
import io.kubernetes.client.common.KubernetesListObject;
|
||||||
|
import io.kubernetes.client.common.KubernetesObject;
|
||||||
|
import io.kubernetes.client.custom.V1Patch;
|
||||||
|
import io.kubernetes.client.openapi.ApiClient;
|
||||||
|
import io.kubernetes.client.openapi.ApiException;
|
||||||
|
import io.kubernetes.client.openapi.models.V1ConfigMap;
|
||||||
|
import io.kubernetes.client.openapi.models.V1ConfigMapList;
|
||||||
|
import io.kubernetes.client.openapi.models.V1ObjectMeta;
|
||||||
|
import io.kubernetes.client.openapi.models.V1PersistentVolumeClaim;
|
||||||
|
import io.kubernetes.client.openapi.models.V1PersistentVolumeClaimList;
|
||||||
|
import io.kubernetes.client.openapi.models.V1Pod;
|
||||||
|
import io.kubernetes.client.openapi.models.V1PodList;
|
||||||
|
import io.kubernetes.client.util.generic.GenericKubernetesApi;
|
||||||
|
import io.kubernetes.client.util.generic.options.PatchOptions;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helpers for K8s API.
|
||||||
|
*/
|
||||||
|
@SuppressWarnings({ "PMD.ShortClassName", "PMD.UseUtilityClass" })
|
||||||
|
public class K8s {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get PVC API.
|
||||||
|
*
|
||||||
|
* @param client the client
|
||||||
|
* @return the generic kubernetes api
|
||||||
|
*/
|
||||||
|
public static GenericKubernetesApi<V1PersistentVolumeClaim,
|
||||||
|
V1PersistentVolumeClaimList> pvcApi(ApiClient client) {
|
||||||
|
return new GenericKubernetesApi<>(V1PersistentVolumeClaim.class,
|
||||||
|
V1PersistentVolumeClaimList.class, "", "v1",
|
||||||
|
"persistentvolumeclaims", client);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get config map API.
|
||||||
|
*
|
||||||
|
* @param client the client
|
||||||
|
* @return the generic kubernetes api
|
||||||
|
*/
|
||||||
|
public static GenericKubernetesApi<V1ConfigMap,
|
||||||
|
V1ConfigMapList> cmApi(ApiClient client) {
|
||||||
|
return new GenericKubernetesApi<>(V1ConfigMap.class,
|
||||||
|
V1ConfigMapList.class, "", "v1", "configmaps", client);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get pod API.
|
||||||
|
*
|
||||||
|
* @param client the client
|
||||||
|
* @return the generic kubernetes api
|
||||||
|
*/
|
||||||
|
public static GenericKubernetesApi<V1Pod, V1PodList>
|
||||||
|
podApi(ApiClient client) {
|
||||||
|
return new GenericKubernetesApi<>(V1Pod.class, V1PodList.class, "",
|
||||||
|
"v1", "pods", client);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get an object from its metadata.
|
||||||
|
*
|
||||||
|
* @param <T> the generic type
|
||||||
|
* @param <LT> the generic type
|
||||||
|
* @param api the api
|
||||||
|
* @param meta the meta
|
||||||
|
* @return the object
|
||||||
|
*/
|
||||||
|
public static <T extends KubernetesObject, LT extends KubernetesListObject>
|
||||||
|
Optional<T> get(GenericKubernetesApi<T, LT> api, V1ObjectMeta meta)
|
||||||
|
throws ApiException {
|
||||||
|
var response = api.get(meta.getNamespace(), meta.getName());
|
||||||
|
if (response.isSuccess()) {
|
||||||
|
return Optional.of(response.getObject());
|
||||||
|
}
|
||||||
|
return Optional.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete an object.
|
||||||
|
*
|
||||||
|
* @param <T> the generic type
|
||||||
|
* @param <LT> the generic type
|
||||||
|
* @param api the api
|
||||||
|
* @param object the object
|
||||||
|
*/
|
||||||
|
public static <T extends KubernetesObject, LT extends KubernetesListObject>
|
||||||
|
void delete(GenericKubernetesApi<T, LT> api, T object)
|
||||||
|
throws ApiException {
|
||||||
|
api.delete(object.getMetadata().getNamespace(),
|
||||||
|
object.getMetadata().getName()).throwsApiException();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Apply the given patch data.
|
||||||
|
*
|
||||||
|
* @param <T> the generic type
|
||||||
|
* @param <LT> the generic type
|
||||||
|
* @param api the api
|
||||||
|
* @param existing the existing
|
||||||
|
* @param update the update
|
||||||
|
* @throws ApiException the api exception
|
||||||
|
*/
|
||||||
|
public static <T extends KubernetesObject, LT extends KubernetesListObject>
|
||||||
|
void
|
||||||
|
apply(GenericKubernetesApi<T, LT> api, T existing, String update)
|
||||||
|
throws ApiException {
|
||||||
|
PatchOptions opts = new PatchOptions();
|
||||||
|
opts.setForce(false);
|
||||||
|
opts.setFieldManager("kubernetes-java-kubectl-apply");
|
||||||
|
api.patch(existing.getMetadata().getNamespace(),
|
||||||
|
existing.getMetadata().getName(), V1Patch.PATCH_FORMAT_APPLY_YAML,
|
||||||
|
new V1Patch(update), opts).throwsApiException();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -29,11 +29,11 @@ import freemarker.template.TemplateException;
|
||||||
import freemarker.template.TemplateExceptionHandler;
|
import freemarker.template.TemplateExceptionHandler;
|
||||||
import freemarker.template.TemplateHashModel;
|
import freemarker.template.TemplateHashModel;
|
||||||
import freemarker.template.TemplateNotFoundException;
|
import freemarker.template.TemplateNotFoundException;
|
||||||
import io.kubernetes.client.custom.V1Patch;
|
|
||||||
import io.kubernetes.client.openapi.ApiException;
|
import io.kubernetes.client.openapi.ApiException;
|
||||||
import io.kubernetes.client.util.generic.dynamic.DynamicKubernetesApi;
|
import io.kubernetes.client.util.generic.dynamic.DynamicKubernetesApi;
|
||||||
import io.kubernetes.client.util.generic.dynamic.DynamicKubernetesObject;
|
import io.kubernetes.client.util.generic.dynamic.DynamicKubernetesObject;
|
||||||
import io.kubernetes.client.util.generic.options.PatchOptions;
|
import io.kubernetes.client.util.generic.dynamic.Dynamics;
|
||||||
|
import io.kubernetes.client.util.generic.options.ListOptions;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.StringWriter;
|
import java.io.StringWriter;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
|
@ -43,6 +43,7 @@ import static org.jdrupes.vmoperator.manager.Constants.APP_NAME;
|
||||||
import static org.jdrupes.vmoperator.manager.Constants.VM_OP_GROUP;
|
import static org.jdrupes.vmoperator.manager.Constants.VM_OP_GROUP;
|
||||||
import static org.jdrupes.vmoperator.manager.Constants.VM_OP_NAME;
|
import static org.jdrupes.vmoperator.manager.Constants.VM_OP_NAME;
|
||||||
import static org.jdrupes.vmoperator.manager.Constants.VM_OP_VERSION;
|
import static org.jdrupes.vmoperator.manager.Constants.VM_OP_VERSION;
|
||||||
|
import org.jdrupes.vmoperator.manager.VmDefChanged.Type;
|
||||||
import org.jdrupes.vmoperator.util.ExtendedObjectWrapper;
|
import org.jdrupes.vmoperator.util.ExtendedObjectWrapper;
|
||||||
import org.jgrapes.core.Channel;
|
import org.jgrapes.core.Channel;
|
||||||
import org.jgrapes.core.Component;
|
import org.jgrapes.core.Component;
|
||||||
|
|
@ -90,29 +91,70 @@ public class Reconciler extends Component {
|
||||||
* @throws KubectlException
|
* @throws KubectlException
|
||||||
*/
|
*/
|
||||||
@Handler
|
@Handler
|
||||||
|
@SuppressWarnings("PMD.ConfusingTernary")
|
||||||
public void onVmDefChanged(VmDefChanged event, WatchChannel channel)
|
public void onVmDefChanged(VmDefChanged event, WatchChannel channel)
|
||||||
throws ApiException, TemplateNotFoundException,
|
throws ApiException, TemplateException, IOException {
|
||||||
MalformedTemplateNameException, ParseException, IOException,
|
|
||||||
TemplateException {
|
|
||||||
DynamicKubernetesApi vmDefApi = new DynamicKubernetesApi(VM_OP_GROUP,
|
DynamicKubernetesApi vmDefApi = new DynamicKubernetesApi(VM_OP_GROUP,
|
||||||
VM_OP_VERSION, event.crd().getName(), channel.client());
|
VM_OP_VERSION, event.crd().getName(), channel.client());
|
||||||
var defMeta = event.metadata();
|
var defMeta = event.metadata();
|
||||||
var vmDef = vmDefApi.get(defMeta.getNamespace(), defMeta.getName())
|
|
||||||
.getObject();
|
|
||||||
|
|
||||||
// Prepare Freemarker model
|
// Get common data for all reconciles
|
||||||
@SuppressWarnings("PMD.UseConcurrentHashMap")
|
DynamicKubernetesObject vmDef = null;
|
||||||
Map<String, Object> model = new HashMap<>();
|
Map<String, Object> model = null;
|
||||||
model.put("cr", vmDef.getRaw());
|
if (event.type() != Type.DELETED) {
|
||||||
model.put("constants",
|
vmDef = K8s.get(vmDefApi, defMeta).get();
|
||||||
(TemplateHashModel) new DefaultObjectWrapperBuilder(
|
|
||||||
Configuration.VERSION_2_3_32)
|
// Prepare Freemarker model
|
||||||
.build().getStaticModels().get(Constants.class.getName()));
|
model = new HashMap<>();
|
||||||
|
model.put("cr", vmDef.getRaw());
|
||||||
|
model.put("constants",
|
||||||
|
(TemplateHashModel) new DefaultObjectWrapperBuilder(
|
||||||
|
Configuration.VERSION_2_3_32)
|
||||||
|
.build().getStaticModels()
|
||||||
|
.get(Constants.class.getName()));
|
||||||
|
}
|
||||||
|
|
||||||
// Reconcile
|
// Reconcile
|
||||||
reconcileDisks(vmDef, channel);
|
if (event.type() != Type.DELETED) {
|
||||||
reconcileConfigMap(model, channel);
|
reconcileDataPvc(model, channel);
|
||||||
reconcilePod(model, channel);
|
reconcileDisks(vmDef, channel);
|
||||||
|
reconcileConfigMap(event, model, channel);
|
||||||
|
reconcilePod(event, model, channel);
|
||||||
|
} else {
|
||||||
|
reconcilePod(event, model, channel);
|
||||||
|
reconcileConfigMap(event, model, channel);
|
||||||
|
deletePvcs(event, channel);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("PMD.ConfusingTernary")
|
||||||
|
private void reconcileDataPvc(Map<String, Object> model,
|
||||||
|
WatchChannel channel)
|
||||||
|
throws TemplateException, ApiException, IOException {
|
||||||
|
// Combine template and data and parse result
|
||||||
|
var fmTemplate = fmConfig.getTemplate("vmDataPvc.ftl.yaml");
|
||||||
|
StringWriter out = new StringWriter();
|
||||||
|
fmTemplate.process(model, out);
|
||||||
|
// Avoid Yaml.load due to
|
||||||
|
// https://github.com/kubernetes-client/java/issues/2741
|
||||||
|
var pvcDef = Dynamics.newFromYaml(out.toString());
|
||||||
|
|
||||||
|
// Get API and check if PVC exists
|
||||||
|
DynamicKubernetesApi pvcApi = new DynamicKubernetesApi("", "v1",
|
||||||
|
"persistentvolumeclaims", channel.client());
|
||||||
|
var existing = K8s.get(pvcApi, pvcDef.getMetadata());
|
||||||
|
|
||||||
|
// If PVC does not exist, create. Else patch (apply)
|
||||||
|
if (existing.isEmpty()) {
|
||||||
|
pvcApi.create(pvcDef);
|
||||||
|
} else {
|
||||||
|
// spec is immutable, so mix in existing spec
|
||||||
|
GsonPtr.to(pvcDef.getRaw()).set("spec", GsonPtr
|
||||||
|
.to(existing.get().getRaw()).get(JsonObject.class, "spec")
|
||||||
|
.get().deepCopy());
|
||||||
|
K8s.apply(pvcApi, existing.get(),
|
||||||
|
channel.client().getJSON().serialize(pvcDef));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void reconcileDisks(DynamicKubernetesObject vmDef,
|
private void reconcileDisks(DynamicKubernetesObject vmDef,
|
||||||
|
|
@ -131,22 +173,22 @@ public class Reconciler extends Component {
|
||||||
int index, JsonObject diskDef, WatchChannel channel)
|
int index, JsonObject diskDef, WatchChannel channel)
|
||||||
throws ApiException {
|
throws ApiException {
|
||||||
var pvcObject = new DynamicKubernetesObject();
|
var pvcObject = new DynamicKubernetesObject();
|
||||||
var pvcDef = GsonPtr.to(pvcObject.getRaw());
|
var pvcRaw = GsonPtr.to(pvcObject.getRaw());
|
||||||
var vmDef = GsonPtr.to(vmDefinition.getRaw());
|
var vmRaw = GsonPtr.to(vmDefinition.getRaw());
|
||||||
var pvcTpl = GsonPtr.to(diskDef).to("volumeClaimTemplate");
|
var pvcTpl = GsonPtr.to(diskDef).to("volumeClaimTemplate");
|
||||||
|
|
||||||
// Copy base and metadata from template and add missing/additional data.
|
// Copy base and metadata from template and add missing/additional data.
|
||||||
pvcObject.setApiVersion(pvcTpl.getAsString("apiVersion").get());
|
pvcObject.setApiVersion(pvcTpl.getAsString("apiVersion").get());
|
||||||
pvcObject.setKind(pvcTpl.getAsString("kind").get());
|
pvcObject.setKind(pvcTpl.getAsString("kind").get());
|
||||||
var vmName = vmDef.getAsString("metadata", "name").orElse("default");
|
var vmName = vmRaw.getAsString("metadata", "name").orElse("default");
|
||||||
pvcDef.get(JsonObject.class).add("metadata",
|
pvcRaw.get(JsonObject.class).add("metadata",
|
||||||
pvcTpl.to("metadata").get(JsonObject.class).deepCopy());
|
pvcTpl.to("metadata").get(JsonObject.class).deepCopy());
|
||||||
var defMeta = pvcDef.to("metadata");
|
var defMeta = pvcRaw.to("metadata");
|
||||||
defMeta.computeIfAbsent("namespace", () -> new JsonPrimitive(
|
defMeta.computeIfAbsent("namespace", () -> new JsonPrimitive(
|
||||||
vmDef.getAsString("metadata", "namespace").orElse("default")));
|
vmRaw.getAsString("metadata", "namespace").orElse("default")));
|
||||||
defMeta.computeIfAbsent("name", () -> new JsonPrimitive(
|
defMeta.computeIfAbsent("name", () -> new JsonPrimitive(
|
||||||
vmName + "-disk-" + index));
|
vmName + "-disk-" + index));
|
||||||
var pvcLbls = pvcDef.to("metadata", "labels");
|
var pvcLbls = pvcRaw.to("metadata", "labels");
|
||||||
pvcLbls.set("app.kubernetes.io/name", APP_NAME);
|
pvcLbls.set("app.kubernetes.io/name", APP_NAME);
|
||||||
pvcLbls.set("app.kubernetes.io/instance", vmName);
|
pvcLbls.set("app.kubernetes.io/instance", vmName);
|
||||||
pvcLbls.set("app.kubernetes.io/component", "disk");
|
pvcLbls.set("app.kubernetes.io/component", "disk");
|
||||||
|
|
@ -155,72 +197,94 @@ public class Reconciler extends Component {
|
||||||
// Get API and check if PVC exists
|
// Get API and check if PVC exists
|
||||||
DynamicKubernetesApi pvcApi = new DynamicKubernetesApi("", "v1",
|
DynamicKubernetesApi pvcApi = new DynamicKubernetesApi("", "v1",
|
||||||
"persistentvolumeclaims", channel.client());
|
"persistentvolumeclaims", channel.client());
|
||||||
var existing = pvcApi.get(defMeta.getAsString("namespace").get(),
|
var existing = K8s.get(pvcApi, pvcObject.getMetadata());
|
||||||
defMeta.getAsString("name").get());
|
|
||||||
|
|
||||||
// If PVC does not exist, create. Else patch (apply)
|
// If PVC does not exist, create. Else patch (apply)
|
||||||
if (!existing.isSuccess()) {
|
if (existing.isEmpty()) {
|
||||||
// PVC does not exist yet, copy spec from template
|
// PVC does not exist yet, copy spec from template
|
||||||
pvcDef.get(JsonObject.class).add("spec",
|
pvcRaw.get(JsonObject.class).add("spec",
|
||||||
pvcTpl.to("spec").get(JsonObject.class).deepCopy());
|
pvcTpl.to("spec").get(JsonObject.class).deepCopy());
|
||||||
pvcApi.create(pvcObject);
|
pvcApi.create(pvcObject);
|
||||||
} else {
|
} else {
|
||||||
// spec is immutable, so mix in existing spec
|
// spec is immutable, so mix in existing spec
|
||||||
pvcDef.set("spec", GsonPtr.to(existing.getObject().getRaw())
|
pvcRaw.set("spec", GsonPtr.to(existing.get().getRaw())
|
||||||
.to("spec").get().deepCopy());
|
.to("spec").get().deepCopy());
|
||||||
PatchOptions opts = new PatchOptions();
|
K8s.apply(pvcApi, existing.get(),
|
||||||
opts.setForce(false);
|
channel.client().getJSON().serialize(pvcObject));
|
||||||
opts.setFieldManager("kubernetes-java-kubectl-apply");
|
|
||||||
pvcApi.patch(pvcObject.getMetadata().getNamespace(),
|
|
||||||
pvcObject.getMetadata().getName(),
|
|
||||||
V1Patch.PATCH_FORMAT_APPLY_YAML,
|
|
||||||
new V1Patch(channel.client().getJSON().serialize(pvcObject)),
|
|
||||||
opts).throwsApiException();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void reconcileConfigMap(Map<String, Object> model,
|
private void deletePvcs(VmDefChanged event, WatchChannel channel)
|
||||||
WatchChannel channel)
|
throws ApiException {
|
||||||
throws TemplateNotFoundException, MalformedTemplateNameException,
|
// Get API and check and list related
|
||||||
ParseException, IOException, TemplateException, ApiException {
|
var pvcApi = K8s.pvcApi(channel.client());
|
||||||
|
var pvcs = pvcApi.list(event.metadata().getNamespace(),
|
||||||
|
new ListOptions().labelSelector(
|
||||||
|
"app.kubernetes.io/managed-by=" + VM_OP_NAME
|
||||||
|
+ ",app.kubernetes.io/name=" + APP_NAME
|
||||||
|
+ ",app.kubernetes.io/instance="
|
||||||
|
+ event.metadata().getName()));
|
||||||
|
for (var pvc : pvcs.getObject().getItems()) {
|
||||||
|
K8s.delete(pvcApi, pvc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void reconcileConfigMap(VmDefChanged event,
|
||||||
|
Map<String, Object> model, WatchChannel channel)
|
||||||
|
throws IOException, TemplateException, ApiException {
|
||||||
|
// Get API and check if exists
|
||||||
|
DynamicKubernetesApi cmApi = new DynamicKubernetesApi("", "v1",
|
||||||
|
"configmaps", channel.client());
|
||||||
|
var existing = K8s.get(cmApi, event.metadata());
|
||||||
|
|
||||||
|
// If deleted, delete
|
||||||
|
if (event.type() == Type.DELETED) {
|
||||||
|
if (existing.isPresent()) {
|
||||||
|
K8s.delete(cmApi, existing.get());
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Combine template and data and parse result
|
// Combine template and data and parse result
|
||||||
var fmTemplate = fmConfig.getTemplate("runnerConfig.ftl.yaml");
|
var fmTemplate = fmConfig.getTemplate("runnerConfig.ftl.yaml");
|
||||||
StringWriter out = new StringWriter();
|
StringWriter out = new StringWriter();
|
||||||
fmTemplate.process(model, out);
|
fmTemplate.process(model, out);
|
||||||
|
// Avoid Yaml.load due to
|
||||||
|
// https://github.com/kubernetes-client/java/issues/2741
|
||||||
|
var mapDef = Dynamics.newFromYaml(out.toString());
|
||||||
|
|
||||||
// Apply
|
// Apply
|
||||||
PatchOptions opts = new PatchOptions();
|
K8s.apply(cmApi, mapDef, out.toString());
|
||||||
opts.setForce(false);
|
|
||||||
opts.setFieldManager("kubernetes-java-kubectl-apply");
|
|
||||||
DynamicKubernetesApi pvcApi = new DynamicKubernetesApi("", "v1",
|
|
||||||
"configmaps", channel.client());
|
|
||||||
var vmDef = GsonPtr.to((JsonObject) model.get("cr"));
|
|
||||||
pvcApi.patch(vmDef.getAsString("metadata", "namespace").get(),
|
|
||||||
vmDef.getAsString("metadata", "name").get(),
|
|
||||||
V1Patch.PATCH_FORMAT_APPLY_YAML, new V1Patch(out.toString()),
|
|
||||||
opts).throwsApiException();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void reconcilePod(Map<String, Object> model, WatchChannel channel)
|
private void reconcilePod(VmDefChanged event, Map<String, Object> model,
|
||||||
throws TemplateNotFoundException, MalformedTemplateNameException,
|
WatchChannel channel)
|
||||||
ParseException, IOException, TemplateException, ApiException {
|
throws IOException, TemplateException, ApiException {
|
||||||
|
// Check if exists
|
||||||
|
DynamicKubernetesApi podApi = new DynamicKubernetesApi("", "v1",
|
||||||
|
"pods", channel.client());
|
||||||
|
var existing = K8s.get(podApi, event.metadata());
|
||||||
|
|
||||||
|
// If deleted, delete
|
||||||
|
if (event.type() == Type.DELETED) {
|
||||||
|
if (existing.isPresent()) {
|
||||||
|
K8s.delete(podApi, existing.get());
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Combine template and data and parse result
|
// Combine template and data and parse result
|
||||||
var fmTemplate = fmConfig.getTemplate("runnerPod.ftl.yaml");
|
var fmTemplate = fmConfig.getTemplate("runnerPod.ftl.yaml");
|
||||||
StringWriter out = new StringWriter();
|
StringWriter out = new StringWriter();
|
||||||
fmTemplate.process(model, out);
|
fmTemplate.process(model, out);
|
||||||
out = null;
|
// Avoid Yaml.load due to
|
||||||
|
// https://github.com/kubernetes-client/java/issues/2741
|
||||||
|
var podDef = Dynamics.newFromYaml(out.toString());
|
||||||
|
|
||||||
// // Apply
|
// Nothing can be updated here
|
||||||
// PatchOptions opts = new PatchOptions();
|
if (existing.isEmpty()) {
|
||||||
// opts.setForce(false);
|
podApi.create(podDef);
|
||||||
// opts.setFieldManager("kubernetes-java-kubectl-apply");
|
}
|
||||||
// DynamicKubernetesApi pvcApi = new DynamicKubernetesApi("", "v1",
|
|
||||||
// "pod", channel.client());
|
|
||||||
// var vmDef = GsonPtr.to((JsonObject) model.get("cr"));
|
|
||||||
// pvcApi.patch(vmDef.getAsString("metadata", "namespace").get(),
|
|
||||||
// vmDef.getAsString("metadata", "name").get(),
|
|
||||||
// V1Patch.PATCH_FORMAT_APPLY_YAML, new V1Patch(out.toString()),
|
|
||||||
// opts).throwsApiException();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue