Create ConfigMap.
This commit is contained in:
parent
ee1a460960
commit
076f86bbe4
4 changed files with 313 additions and 70 deletions
|
|
@ -78,20 +78,18 @@ spec:
|
||||||
maximumCpus:
|
maximumCpus:
|
||||||
description: >-
|
description: >-
|
||||||
Either maximumCpus or cpuTopology may be specified.
|
Either maximumCpus or cpuTopology may be specified.
|
||||||
If currentCpus is greater than maximumCpus, the
|
If neither is specified, maximum cpus is set to 4.
|
||||||
latter is adjusted. Setting maximumCpus to 1 omits
|
Setting maximumCpus to 1 omits the "-smp" options.
|
||||||
the "-smp" options.
|
|
||||||
type: integer
|
type: integer
|
||||||
default: 4
|
|
||||||
cpuTopology:
|
cpuTopology:
|
||||||
description: >-
|
description: >-
|
||||||
The defaults (0) cause the corresponding property
|
Values of 0 cause the corresponding property
|
||||||
to be omitted from the "-smp" option.
|
to be omitted from the "-smp" option.
|
||||||
type: object
|
type: object
|
||||||
properties:
|
properties:
|
||||||
sockets:
|
sockets:
|
||||||
type: integer
|
type: integer
|
||||||
default: 0
|
default: 1
|
||||||
diesPerSocket:
|
diesPerSocket:
|
||||||
type: integer
|
type: integer
|
||||||
default: 0
|
default: 0
|
||||||
|
|
@ -102,8 +100,9 @@ spec:
|
||||||
type: integer
|
type: integer
|
||||||
default: 0
|
default: 0
|
||||||
currentCpus:
|
currentCpus:
|
||||||
|
description: >-
|
||||||
|
Defaults to maximumCpus.
|
||||||
type: integer
|
type: integer
|
||||||
default: 2
|
|
||||||
maximumRam:
|
maximumRam:
|
||||||
type: string
|
type: string
|
||||||
default: "1G"
|
default: "1G"
|
||||||
|
|
@ -113,6 +112,10 @@ spec:
|
||||||
description: Passed to Qemu unmodified.
|
description: Passed to Qemu unmodified.
|
||||||
type: string
|
type: string
|
||||||
default: "utc"
|
default: "utc"
|
||||||
|
rtcClock:
|
||||||
|
description: Passed to Qemu unmodified.
|
||||||
|
type: string
|
||||||
|
default: "rt"
|
||||||
networks:
|
networks:
|
||||||
type: array
|
type: array
|
||||||
items:
|
items:
|
||||||
|
|
@ -136,12 +139,17 @@ spec:
|
||||||
user:
|
user:
|
||||||
type: object
|
type: object
|
||||||
properties:
|
properties:
|
||||||
|
device:
|
||||||
|
description: The device to use.
|
||||||
|
type: string
|
||||||
|
default: "virtio-net"
|
||||||
net:
|
net:
|
||||||
type: string
|
type: string
|
||||||
oneOf:
|
oneOf:
|
||||||
- properties:
|
- properties:
|
||||||
tap:
|
tap:
|
||||||
user:
|
user:
|
||||||
|
default: []
|
||||||
disks:
|
disks:
|
||||||
description: >-
|
description: >-
|
||||||
Disks make persistent storage available. The
|
Disks make persistent storage available. The
|
||||||
|
|
@ -151,8 +159,6 @@ spec:
|
||||||
items:
|
items:
|
||||||
type: object
|
type: object
|
||||||
properties:
|
properties:
|
||||||
hostDevice:
|
|
||||||
type: string
|
|
||||||
volumeClaimTemplate:
|
volumeClaimTemplate:
|
||||||
description: >-
|
description: >-
|
||||||
A PVC spec to be used to provide the disk. The easiest
|
A PVC spec to be used to provide the disk. The easiest
|
||||||
|
|
@ -180,36 +186,8 @@ spec:
|
||||||
description: >-
|
description: >-
|
||||||
EmbeddedMetadata contains metadata relevant to
|
EmbeddedMetadata contains metadata relevant to
|
||||||
an EmbeddedResource.
|
an EmbeddedResource.
|
||||||
|
type: object
|
||||||
properties:
|
properties:
|
||||||
annotations:
|
|
||||||
additionalProperties:
|
|
||||||
type: string
|
|
||||||
description: >-
|
|
||||||
Annotations is an unstructured key value
|
|
||||||
map stored with a resource that may be set by external
|
|
||||||
tools to store and retrieve arbitrary metadata. They
|
|
||||||
are not queryable and should be preserved when modifying
|
|
||||||
objects. More info: http://kubernetes.io/docs/user-guide/annotations
|
|
||||||
type: object
|
|
||||||
labels:
|
|
||||||
additionalProperties:
|
|
||||||
type: string
|
|
||||||
description: >-
|
|
||||||
Map of string keys and values that can be
|
|
||||||
used to organize and categorize (scope and select) objects.
|
|
||||||
May match selectors of replication controllers and services.
|
|
||||||
More info: http://kubernetes.io/docs/user-guide/labels
|
|
||||||
type: object
|
|
||||||
name:
|
|
||||||
description: >-
|
|
||||||
Name must be unique within a namespace.
|
|
||||||
Is required when creating resources, although some resources
|
|
||||||
may allow a client to request the generation of an appropriate
|
|
||||||
name automatically. Name is primarily intended for creation
|
|
||||||
idempotence and configuration definition. Cannot be
|
|
||||||
updated. More info: http://kubernetes.io/docs/user-guide/identifiers#names.
|
|
||||||
The name is generated automatically but can be overriden.
|
|
||||||
type: string
|
|
||||||
namespace:
|
namespace:
|
||||||
description: >-
|
description: >-
|
||||||
Namespace defines the space within which each
|
Namespace defines the space within which each
|
||||||
|
|
@ -221,7 +199,35 @@ spec:
|
||||||
More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces.
|
More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces.
|
||||||
The default value is the VM's namespace.
|
The default value is the VM's namespace.
|
||||||
type: string
|
type: string
|
||||||
|
name:
|
||||||
|
description: >-
|
||||||
|
Name must be unique within a namespace.
|
||||||
|
Is required when creating resources, although some resources
|
||||||
|
may allow a client to request the generation of an appropriate
|
||||||
|
name automatically. Name is primarily intended for creation
|
||||||
|
idempotence and configuration definition. Cannot be
|
||||||
|
updated. More info: http://kubernetes.io/docs/user-guide/identifiers#names.
|
||||||
|
The name is generated automatically but can be overriden.
|
||||||
|
type: string
|
||||||
|
labels:
|
||||||
|
description: >-
|
||||||
|
Map of string keys and values that can be
|
||||||
|
used to organize and categorize (scope and select) objects.
|
||||||
|
May match selectors of replication controllers and services.
|
||||||
|
More info: http://kubernetes.io/docs/user-guide/labels
|
||||||
type: object
|
type: object
|
||||||
|
additionalProperties:
|
||||||
|
type: string
|
||||||
|
annotations:
|
||||||
|
description: >-
|
||||||
|
Annotations is an unstructured key value
|
||||||
|
map stored with a resource that may be set by external
|
||||||
|
tools to store and retrieve arbitrary metadata. They
|
||||||
|
are not queryable and should be preserved when modifying
|
||||||
|
objects. More info: http://kubernetes.io/docs/user-guide/annotations
|
||||||
|
type: object
|
||||||
|
additionalProperties:
|
||||||
|
type: string
|
||||||
spec:
|
spec:
|
||||||
description: >-
|
description: >-
|
||||||
Spec defines the desired characteristics of
|
Spec defines the desired characteristics of
|
||||||
|
|
@ -420,9 +426,10 @@ spec:
|
||||||
type: object
|
type: object
|
||||||
bootindex:
|
bootindex:
|
||||||
type: integer
|
type: integer
|
||||||
displays:
|
required:
|
||||||
type: array
|
- volumeClaimTemplate
|
||||||
items:
|
default: []
|
||||||
|
display:
|
||||||
type: object
|
type: object
|
||||||
properties:
|
properties:
|
||||||
spice:
|
spice:
|
||||||
|
|
@ -438,6 +445,7 @@ spec:
|
||||||
usbRedirects:
|
usbRedirects:
|
||||||
type: integer
|
type: integer
|
||||||
default: 2
|
default: 2
|
||||||
|
default: { spice: { port: 5900, usbRedirects: 2 } }
|
||||||
oneOf:
|
oneOf:
|
||||||
- properties:
|
- properties:
|
||||||
maximumCpus:
|
maximumCpus:
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,6 @@ spec:
|
||||||
storage: 40Gi
|
storage: 40Gi
|
||||||
|
|
||||||
# - hostDevice: /dev/vgmain/test-vm
|
# - hostDevice: /dev/vgmain/test-vm
|
||||||
displays:
|
display:
|
||||||
- spice:
|
spice:
|
||||||
port: 5910
|
port: 5910
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,172 @@
|
||||||
|
apiVersion: v1
|
||||||
|
kind: ConfigMap
|
||||||
|
metadata:
|
||||||
|
namespace: ${ metadata.namespace.asString }
|
||||||
|
name: ${ metadata.name.asString }
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/name: ${ constants.APP_NAME }
|
||||||
|
app.kubernetes.io/instance: ${ metadata.name.asString }
|
||||||
|
app.kubernetes.io/managed-by: ${ constants.VM_OP_NAME }
|
||||||
|
|
||||||
|
data:
|
||||||
|
config.yaml: |
|
||||||
|
"/Runner":
|
||||||
|
# The directory used to store data files. Defaults to (depending on
|
||||||
|
# values available):
|
||||||
|
# * $XDG_DATA_HOME/vmrunner/${ metadata.name.asString }
|
||||||
|
# * $HOME/.local/share/vmrunner/${ metadata.name.asString }
|
||||||
|
# * ./${ metadata.name.asString }
|
||||||
|
dataDir: /var/local/vm-data
|
||||||
|
|
||||||
|
# The directory used to store runtime files. Defaults to (depending on
|
||||||
|
# values available):
|
||||||
|
# * $XDG_RUNTIME_DIR/vmrunner/${ metadata.name.asString }
|
||||||
|
# * /tmp/$USER/vmrunner/${ metadata.name.asString }
|
||||||
|
# * /tmp/vmrunner/${ metadata.name.asString }
|
||||||
|
# runtimeDir: "$XDG_RUNTIME_DIR/vmrunner/${ metadata.name.asString }"
|
||||||
|
|
||||||
|
# The template to use. Resolved relative to /usr/share/vmrunner/templates.
|
||||||
|
# template: "Standard-VM-latest.ftl.yaml"
|
||||||
|
|
||||||
|
# The template is copied to the data diretory when the VM starts for
|
||||||
|
# the first time. Subsequent starts use the copy unless this option is set.
|
||||||
|
updateTemplate: true
|
||||||
|
|
||||||
|
# Define the VM (required)
|
||||||
|
vm:
|
||||||
|
# The VM's name (required)
|
||||||
|
name: ${ metadata.name.asString }
|
||||||
|
|
||||||
|
# The machine's uuid. If none is specified, a uuid is generated
|
||||||
|
# and stored in the data directory. If the uuid is important
|
||||||
|
# (e.g. because licenses depend on it) it is recommaned to specify
|
||||||
|
# it here explicitly or to carefully backup the data directory.
|
||||||
|
# uuid: "generated uuid"
|
||||||
|
<#if spec.vm.machineUuid??>
|
||||||
|
uuid: "${ spec.vm.machineUuid.asString }"
|
||||||
|
</#if>
|
||||||
|
|
||||||
|
# Whether to provide a software TPM (defaults to false)
|
||||||
|
# useTpm: false
|
||||||
|
useTpm: ${ spec.vm.useTpm.asBoolean?c }
|
||||||
|
|
||||||
|
# How to boot (see https://github.com/mnlipp/VM-Operator/blob/main/org.jdrupes.vmoperator.runner.qemu/resources/org/jdrupes/vmoperator/runner/qemu/defaults.yaml):
|
||||||
|
# * bios
|
||||||
|
# * uefi[-4m]
|
||||||
|
# * secure[-4m]
|
||||||
|
firmware: ${ spec.vm.firmware.asString }
|
||||||
|
|
||||||
|
# Whether to show a boot menu.
|
||||||
|
# bootMenu: false
|
||||||
|
bootMenu: ${ spec.vm.bootMenu.asBoolean?c }
|
||||||
|
|
||||||
|
# When terminating, a graceful powerdown is attempted. If it
|
||||||
|
# doesn't succeed within the given timeout (seconds) SIGTERM
|
||||||
|
# is sent to Qemu.
|
||||||
|
# powerdownTimeout: 900
|
||||||
|
powerdownTimeout: ${ spec.vm.powerdownTimeout.asLong?c }
|
||||||
|
|
||||||
|
# CPU settings
|
||||||
|
cpuModel: ${ spec.vm.cpuModel.asString }
|
||||||
|
# Setting maximumCpus to 1 omits the "-smp" options. The defaults (0)
|
||||||
|
# cause the corresponding property to be omitted from the "-smp" option.
|
||||||
|
# If currentCpus is greater than maximumCpus, the latter is adjusted.
|
||||||
|
<#if spec.vm.maximumCpus?? >
|
||||||
|
maximumCpus: ${ spec.vm.maximumCpus.asInt?c }
|
||||||
|
</#if>
|
||||||
|
<#if spec.vm.cpuTopology?? >
|
||||||
|
cpuSockets: ${ spec.vm.cpuTopology.cpuSockets.asInt?c }
|
||||||
|
diesPerSocket: ${ spec.vm.cpuTopology.diesPerSocket.asInt?c }
|
||||||
|
coresPerSocket: ${ spec.vm.cpuTopology.coresPerSocket.asInt?c }
|
||||||
|
threadsPerCore: ${ spec.vm.cpuTopology.threadsPerCore.asInt?c }
|
||||||
|
</#if>
|
||||||
|
<#if spec.vm.currentCpus?? >
|
||||||
|
currentCpus: ${ spec.vm.currentCpus.asInt?c }
|
||||||
|
</#if>
|
||||||
|
|
||||||
|
# RAM settings
|
||||||
|
# Maximum defaults to 1G
|
||||||
|
maximumRam: "${ spec.vm.maximumRam.asString }"
|
||||||
|
<#if spec.vm.currentRam?? >
|
||||||
|
currentRam: "${ spec.vm.currentRam.asString }"
|
||||||
|
</#if>
|
||||||
|
|
||||||
|
# RTC settings.
|
||||||
|
# rtcBase: utc
|
||||||
|
# rtcClock: rt
|
||||||
|
rtcBase: ${ spec.vm.rtcBase.asString }
|
||||||
|
rtcClock: ${ spec.vm.rtcClock.asString }
|
||||||
|
|
||||||
|
# Network settings
|
||||||
|
# Supported types are "tap" and "user" (for debugging). Type "user"
|
||||||
|
# supports only the property "net".
|
||||||
|
# network:
|
||||||
|
# - type: tap
|
||||||
|
# bridge: br0
|
||||||
|
# device: virtio-net
|
||||||
|
# mac: (undefined)
|
||||||
|
network:
|
||||||
|
<#assign nwCounter = 0/>
|
||||||
|
<#list spec.vm.networks.asList() as itf>
|
||||||
|
<#if itf.tap??>
|
||||||
|
- type: tap
|
||||||
|
device: ${ itf.tap.device.asString }
|
||||||
|
bridge: ${ itf.tap.bridge.asString }
|
||||||
|
<#if itf.tap.mac??>
|
||||||
|
mac: "${ itf.tap.mac.asString }"
|
||||||
|
</#if>
|
||||||
|
<#elseif itf.user??>
|
||||||
|
- type: user
|
||||||
|
device: ${ itf.tap.device.asString }
|
||||||
|
<#if itf.user.net??>
|
||||||
|
net: "${ itf.user.net.asString }"
|
||||||
|
</#if>
|
||||||
|
</#if>
|
||||||
|
<#assign nwCounter += 1/>
|
||||||
|
</#list>
|
||||||
|
|
||||||
|
# There are no default drives. The supported types are "ide-cd"
|
||||||
|
# and "raw". All types support a "bootindex" property.
|
||||||
|
# Type "raw" can have a property "file" (if backed by a file on
|
||||||
|
# the host) or a property "device" (if backed by a device).
|
||||||
|
# drives:
|
||||||
|
# - type: ide-cd
|
||||||
|
# bootindex: (undefined)
|
||||||
|
# file: (undefined)
|
||||||
|
drives:
|
||||||
|
<#assign drvCounter = 0/>
|
||||||
|
<#list spec.vm.disks.asList() as disk>
|
||||||
|
<#if disk.volumeClaimTemplate.metadata??
|
||||||
|
&& disk.volumeClaimTemplate.metadata.name??>
|
||||||
|
<#assign name = disk.volumeClaimTemplate.metadata.name.asString>
|
||||||
|
<#else>
|
||||||
|
<#assign name = "" + drvCounter>
|
||||||
|
</#if>
|
||||||
|
- type: raw
|
||||||
|
resource: /dev/disk-${ name }
|
||||||
|
</#list>
|
||||||
|
|
||||||
|
display:
|
||||||
|
<#if spec.vm.display.spice??>
|
||||||
|
spice:
|
||||||
|
port: ${ spec.vm.display.spice.port.asInt?c }
|
||||||
|
<#if spec.vm.display.spice.ticket??>
|
||||||
|
ticket: "${ spec.vm.display.spice.ticket.asString }"
|
||||||
|
</#if>
|
||||||
|
<#if spec.vm.display.spice.streamingVideo??>
|
||||||
|
ticket: "${ spec.vm.display.spice.streamingVideo.asString }"
|
||||||
|
</#if>
|
||||||
|
usbRedirects: ${ spec.vm.display.spice.usbRedirects.asInt?c }
|
||||||
|
</#if>
|
||||||
|
|
||||||
|
logging.properties: |
|
||||||
|
handlers=java.util.logging.ConsoleHandler
|
||||||
|
|
||||||
|
#org.jgrapes.level=FINE
|
||||||
|
#org.jgrapes.core.handlerTracking.level=FINER
|
||||||
|
|
||||||
|
org.jdrupes.vmoperator.runner.qemu.level=FINE
|
||||||
|
|
||||||
|
java.util.logging.ConsoleHandler.level=ALL
|
||||||
|
java.util.logging.ConsoleHandler.formatter=java.util.logging.SimpleFormatter
|
||||||
|
java.util.logging.SimpleFormatter.format=%1$tb %1$td %1$tT %4$s %5$s%6$s%n
|
||||||
|
|
@ -21,16 +21,29 @@ package org.jdrupes.vmoperator.manager;
|
||||||
import com.google.gson.JsonArray;
|
import com.google.gson.JsonArray;
|
||||||
import com.google.gson.JsonObject;
|
import com.google.gson.JsonObject;
|
||||||
import com.google.gson.JsonPrimitive;
|
import com.google.gson.JsonPrimitive;
|
||||||
|
import freemarker.core.ParseException;
|
||||||
|
import freemarker.template.Configuration;
|
||||||
|
import freemarker.template.DefaultObjectWrapperBuilder;
|
||||||
|
import freemarker.template.MalformedTemplateNameException;
|
||||||
|
import freemarker.template.TemplateException;
|
||||||
|
import freemarker.template.TemplateExceptionHandler;
|
||||||
|
import freemarker.template.TemplateHashModel;
|
||||||
|
import freemarker.template.TemplateNotFoundException;
|
||||||
import io.kubernetes.client.custom.V1Patch;
|
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.options.PatchOptions;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.StringWriter;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
import static org.jdrupes.vmoperator.manager.Constants.APP_NAME;
|
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.util.ExtendedObjectWrapper;
|
||||||
import org.jgrapes.core.Channel;
|
import org.jgrapes.core.Channel;
|
||||||
import org.jgrapes.core.Component;
|
import org.jgrapes.core.Component;
|
||||||
import org.jgrapes.core.annotation.Handler;
|
import org.jgrapes.core.annotation.Handler;
|
||||||
|
|
@ -38,9 +51,12 @@ import org.jgrapes.core.annotation.Handler;
|
||||||
/**
|
/**
|
||||||
* Adapts Kubenetes resources to changes in VM definitions (CRs).
|
* Adapts Kubenetes resources to changes in VM definitions (CRs).
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("PMD.DataflowAnomalyAnalysis")
|
@SuppressWarnings({ "PMD.DataflowAnomalyAnalysis",
|
||||||
|
"PMD.AvoidDuplicateLiterals" })
|
||||||
public class Reconciler extends Component {
|
public class Reconciler extends Component {
|
||||||
|
|
||||||
|
private final Configuration fmConfig;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Instantiates a new reconciler.
|
* Instantiates a new reconciler.
|
||||||
*
|
*
|
||||||
|
|
@ -48,6 +64,16 @@ public class Reconciler extends Component {
|
||||||
*/
|
*/
|
||||||
public Reconciler(Channel componentChannel) {
|
public Reconciler(Channel componentChannel) {
|
||||||
super(componentChannel);
|
super(componentChannel);
|
||||||
|
|
||||||
|
// Configure freemarker library
|
||||||
|
fmConfig = new Configuration(Configuration.VERSION_2_3_32);
|
||||||
|
fmConfig.setDefaultEncoding("utf-8");
|
||||||
|
fmConfig.setObjectWrapper(new ExtendedObjectWrapper(
|
||||||
|
fmConfig.getIncompatibleImprovements()));
|
||||||
|
fmConfig.setTemplateExceptionHandler(
|
||||||
|
TemplateExceptionHandler.RETHROW_HANDLER);
|
||||||
|
fmConfig.setLogTemplateExceptions(false);
|
||||||
|
fmConfig.setClassForTemplateLoading(Reconciler.class, "");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -56,18 +82,30 @@ public class Reconciler extends Component {
|
||||||
* @param event the event
|
* @param event the event
|
||||||
* @param channel the channel
|
* @param channel the channel
|
||||||
* @throws ApiException the api exception
|
* @throws ApiException the api exception
|
||||||
|
* @throws IOException
|
||||||
|
* @throws ParseException
|
||||||
|
* @throws MalformedTemplateNameException
|
||||||
|
* @throws TemplateNotFoundException
|
||||||
|
* @throws TemplateException
|
||||||
* @throws KubectlException
|
* @throws KubectlException
|
||||||
*/
|
*/
|
||||||
@Handler
|
@Handler
|
||||||
public void onVmDefChanged(VmDefChanged event, WatchChannel channel)
|
public void onVmDefChanged(VmDefChanged event, WatchChannel channel)
|
||||||
throws ApiException {
|
throws ApiException, TemplateNotFoundException,
|
||||||
|
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())
|
var vmDef = vmDefApi.get(defMeta.getNamespace(), defMeta.getName())
|
||||||
.getObject();
|
.getObject();
|
||||||
|
|
||||||
@SuppressWarnings("PMD.AvoidDuplicateLiterals")
|
reconcileDisks(vmDef, channel);
|
||||||
|
reconcileConfigMap(vmDef, channel);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void reconcileDisks(DynamicKubernetesObject vmDef,
|
||||||
|
WatchChannel channel) throws ApiException {
|
||||||
var disks = GsonPtr.to(vmDef.getRaw())
|
var disks = GsonPtr.to(vmDef.getRaw())
|
||||||
.get(JsonArray.class, "spec", "vm", "disks")
|
.get(JsonArray.class, "spec", "vm", "disks")
|
||||||
.map(JsonArray::asList).orElse(Collections.emptyList());
|
.map(JsonArray::asList).orElse(Collections.emptyList());
|
||||||
|
|
@ -82,13 +120,13 @@ 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();
|
||||||
pvcObject.setApiVersion("v1");
|
|
||||||
pvcObject.setKind("PersistentVolumeClaim");
|
|
||||||
var pvcDef = GsonPtr.to(pvcObject.getRaw());
|
var pvcDef = GsonPtr.to(pvcObject.getRaw());
|
||||||
var vmDef = GsonPtr.to(vmDefinition.getRaw());
|
var vmDef = GsonPtr.to(vmDefinition.getRaw());
|
||||||
var pvcTpl = GsonPtr.to(diskDef).to("volumeClaimTemplate");
|
var pvcTpl = GsonPtr.to(diskDef).to("volumeClaimTemplate");
|
||||||
|
|
||||||
// Copy 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.setKind(pvcTpl.getAsString("kind").get());
|
||||||
var vmName = vmDef.getAsString("metadata", "name").orElse("default");
|
var vmName = vmDef.getAsString("metadata", "name").orElse("default");
|
||||||
pvcDef.get(JsonObject.class).add("metadata",
|
pvcDef.get(JsonObject.class).add("metadata",
|
||||||
pvcTpl.to("metadata").get(JsonObject.class).deepCopy());
|
pvcTpl.to("metadata").get(JsonObject.class).deepCopy());
|
||||||
|
|
@ -114,11 +152,6 @@ public class Reconciler extends Component {
|
||||||
// PVC does not exist yet, copy spec from template
|
// PVC does not exist yet, copy spec from template
|
||||||
pvcDef.get(JsonObject.class).add("spec",
|
pvcDef.get(JsonObject.class).add("spec",
|
||||||
pvcTpl.to("spec").get(JsonObject.class).deepCopy());
|
pvcTpl.to("spec").get(JsonObject.class).deepCopy());
|
||||||
// Add missing
|
|
||||||
pvcDef.to("spec").computeIfAbsent("accessModes",
|
|
||||||
() -> GsonPtr.to(new JsonArray()).set(0, "ReadWriteOnce")
|
|
||||||
.get());
|
|
||||||
pvcDef.to("spec").getOrSet("volumeMode", "Block");
|
|
||||||
pvcApi.create(pvcObject);
|
pvcApi.create(pvcObject);
|
||||||
} else {
|
} else {
|
||||||
// spec is immutable, so mix in existing spec
|
// spec is immutable, so mix in existing spec
|
||||||
|
|
@ -135,4 +168,34 @@ public class Reconciler extends Component {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void reconcileConfigMap(DynamicKubernetesObject vmDefinition,
|
||||||
|
WatchChannel channel) throws TemplateNotFoundException,
|
||||||
|
MalformedTemplateNameException, ParseException, IOException,
|
||||||
|
TemplateException, ApiException {
|
||||||
|
// Combine template and data and parse result
|
||||||
|
// (tempting, but no need to use a pipe here)
|
||||||
|
var fmTemplate = fmConfig.getTemplate("etcConfig.ftl.yaml");
|
||||||
|
StringWriter out = new StringWriter();
|
||||||
|
@SuppressWarnings("PMD.UseConcurrentHashMap")
|
||||||
|
Map<String, Object> model = new HashMap<>();
|
||||||
|
model.putAll(vmDefinition.getRaw().asMap());
|
||||||
|
model.put("constants",
|
||||||
|
(TemplateHashModel) new DefaultObjectWrapperBuilder(
|
||||||
|
Configuration.VERSION_2_3_32)
|
||||||
|
.build().getStaticModels().get(Constants.class.getName()));
|
||||||
|
fmTemplate.process(model, out);
|
||||||
|
|
||||||
|
// Apply
|
||||||
|
PatchOptions opts = new PatchOptions();
|
||||||
|
opts.setForce(false);
|
||||||
|
opts.setFieldManager("kubernetes-java-kubectl-apply");
|
||||||
|
DynamicKubernetesApi pvcApi = new DynamicKubernetesApi("", "v1",
|
||||||
|
"configmaps", channel.client());
|
||||||
|
var vmDef = GsonPtr.to(vmDefinition.getRaw());
|
||||||
|
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