More resources used by both runner and manager.

This commit is contained in:
Michael Lipp 2023-09-14 09:56:28 +02:00
parent d9c2f6edd3
commit 2b471f3852
12 changed files with 62 additions and 22 deletions

View file

@ -1,5 +1,5 @@
#
#Fri Sep 01 16:56:14 CEST 2023
#Thu Sep 14 09:53:57 CEST 2023
org.eclipse.jdt.core.formatter.insert_space_after_ellipsis=insert
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_declarations=insert
org.eclipse.jdt.core.formatter.insert_new_line_in_empty_annotation_declaration=insert

View file

@ -10,4 +10,5 @@ plugins {
dependencies {
implementation 'org.freemarker:freemarker:[2.3.32,2.4)'
implementation 'io.kubernetes:client-java:[18.0.0,19)'
}

View file

@ -0,0 +1,37 @@
/*
* 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.util;
/**
* Some constants.
*/
public class Constants {
/** The Constant VM_OP_NAME. */
public static final String VM_OP_NAME = "vm-operator";
/** The Constant VM_OP_GROUP. */
public static final String VM_OP_GROUP = "vmoperator.jdrupes.org";
/** The Constant VM_OP_CRD_NAME. */
public static final String VM_OP_CRD_NAME = "vms." + VM_OP_GROUP;
/** The Constant VM_OP_KIND_VM. */
public static final String VM_OP_KIND_VM = "VirtualMachine";
}

View file

@ -0,0 +1,275 @@
/*
* 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.util;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonPrimitive;
import java.util.Optional;
import java.util.function.Supplier;
/**
* Utility class for pointing to elements on a Gson (Json) tree.
*/
@SuppressWarnings({ "PMD.DataflowAnomalyAnalysis",
"PMD.ClassWithOnlyPrivateConstructorsShouldBeFinal" })
public class GsonPtr {
private final JsonElement position;
private GsonPtr(JsonElement root) {
this.position = root;
}
/**
* Create a new instance pointing to the given element.
*
* @param root the root
* @return the Gson pointer
*/
@SuppressWarnings("PMD.ShortMethodName")
public static GsonPtr to(JsonElement root) {
return new GsonPtr(root);
}
/**
* Create a new instance pointing to the {@link JsonElement}
* selected by the given selectors. If a selector of type
* {@link String} denotes a non-existant member of a
* {@link JsonObject}, a new member (of type {@link JsonObject}
* is added.
*
* @param selectors the selectors
* @return the Gson pointer
*/
@SuppressWarnings({ "PMD.ShortMethodName", "PMD.PreserveStackTrace" })
public GsonPtr to(Object... selectors) {
JsonElement element = position;
for (Object sel : selectors) {
if (element instanceof JsonObject obj
&& sel instanceof String member) {
element = Optional.ofNullable(obj.get(member)).orElseGet(() -> {
@SuppressWarnings("PMD.AvoidInstantiatingObjectsInLoops")
var child = new JsonObject();
obj.add(member, child);
return child;
});
continue;
}
if (element instanceof JsonArray arr
&& sel instanceof Integer index) {
try {
element = arr.get(index);
} catch (IndexOutOfBoundsException e) {
throw new IllegalStateException("Selected array index"
+ " may not be empty.");
}
continue;
}
throw new IllegalStateException("Invalid selection");
}
return new GsonPtr(element);
}
/**
* Returns {@link JsonElement} that the pointer points to.
*
* @return the result
*/
public JsonElement get() {
return position;
}
/**
* Returns {@link JsonElement} that the pointer points to,
* casted to the given type.
*
* @param <T> the generic type
* @param cls the cls
* @return the result
*/
@SuppressWarnings({ "PMD.AvoidBranchingStatementAsLastInLoop" })
public <T extends JsonElement> T get(Class<T> cls) {
if (cls.isAssignableFrom(position.getClass())) {
return cls.cast(position);
}
throw new IllegalArgumentException("Not positioned at element"
+ " of desired type.");
}
/**
* Returns the selected {@link JsonElement}, cast to the class
* specified.
*
* @param <T> the generic type
* @param cls the cls
* @param selectors the selectors
* @return the optional
*/
@SuppressWarnings({ "PMD.AvoidBranchingStatementAsLastInLoop" })
public <T extends JsonElement> Optional<T>
get(Class<T> cls, Object... selectors) {
JsonElement element = position;
for (Object sel : selectors) {
if (element instanceof JsonObject obj
&& sel instanceof String member) {
element = obj.get(member);
if (element == null) {
return Optional.empty();
}
continue;
}
if (element instanceof JsonArray arr
&& sel instanceof Integer index) {
try {
element = arr.get(index);
} catch (IndexOutOfBoundsException e) {
return Optional.empty();
}
continue;
}
return Optional.empty();
}
if (cls.isAssignableFrom(element.getClass())) {
return Optional.of(cls.cast(element));
}
return Optional.empty();
}
/**
* Returns the String value of the selected {@link JsonPrimitive}.
*
* @param selectors the selectors
* @return the as string
*/
public Optional<String> getAsString(Object... selectors) {
return get(JsonPrimitive.class, selectors)
.map(JsonPrimitive::getAsString);
}
/**
* Returns the Integer value of the selected {@link JsonPrimitive}.
*
* @param selectors the selectors
* @return the as string
*/
public Optional<Integer> getAsInt(Object... selectors) {
return get(JsonPrimitive.class, selectors)
.map(JsonPrimitive::getAsInt);
}
/**
* Returns the Long value of the selected {@link JsonPrimitive}.
*
* @param selectors the selectors
* @return the as string
*/
public Optional<Long> getAsLong(Object... selectors) {
return get(JsonPrimitive.class, selectors)
.map(JsonPrimitive::getAsLong);
}
/**
* Sets the selected value. This pointer must point to a
* {@link JsonObject} or {@link JsonArray}. The selector must
* be a {@link String} or an integer respectively.
*
* @param selector the selector
* @param value the value
* @return the Gson pointer
*/
public GsonPtr set(Object selector, JsonElement value) {
if (position instanceof JsonObject obj
&& selector instanceof String member) {
obj.add(member, value);
return this;
}
if (position instanceof JsonArray arr
&& selector instanceof Integer index) {
if (index >= arr.size()) {
arr.add(value);
} else {
arr.set(index, value);
}
return this;
}
throw new IllegalStateException("Invalid selection");
}
/**
* Short for `set(selector, new JsonPrimitive(value))`.
*
* @param selector the selector
* @param value the value
* @return the gson ptr
* @see #set(Object, JsonElement)
*/
public GsonPtr set(Object selector, String value) {
return set(selector, new JsonPrimitive(value));
}
/**
* Same as {@link #set(Object, JsonElement)}, but sets the value
* only if it doesn't exist yet, else returns the existing value.
* If this pointer points to a {@link JsonArray} and the selector
* if larger than or equal to the size of the array, the supplied
* value will be appended.
*
* @param <T> the generic type
* @param selector the selector
* @param supplier the supplier of the missing value
* @return the existing or supplied value
*/
@SuppressWarnings("unchecked")
public <T extends JsonElement> T
computeIfAbsent(Object selector, Supplier<T> supplier) {
if (position instanceof JsonObject obj
&& selector instanceof String member) {
return Optional.ofNullable((T) obj.get(member)).orElseGet(() -> {
var res = supplier.get();
obj.add(member, res);
return res;
});
}
if (position instanceof JsonArray arr
&& selector instanceof Integer index) {
if (index >= arr.size()) {
var res = supplier.get();
arr.add(res);
return res;
}
return (T) arr.get(index);
}
throw new IllegalStateException("Invalid selection");
}
/**
* Short for `computeIfAbsent(selector, () -> new JsonPrimitive(value))`.
*
* @param selector the selector
* @param value the value
* @return the Gson pointer
*/
public GsonPtr getOrSet(Object selector, String value) {
computeIfAbsent(selector, () -> new JsonPrimitive(value));
return this;
}
}

View file

@ -0,0 +1,162 @@
/*
* 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.util;
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.DeleteOptions;
import io.kubernetes.client.util.generic.options.PatchOptions;
import java.util.Optional;
/**
* Helpers for K8s API.
*/
@SuppressWarnings({ "PMD.ShortClassName", "PMD.UseUtilityClass" })
public class K8s {
/**
* Given a groupVersion, returns only the version.
*
* @param groupVersion the group version
* @return the string
*/
public static String version(String groupVersion) {
return groupVersion.substring(groupVersion.lastIndexOf('/') + 1);
}
/**
* 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) {
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();
}
/**
* 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,
DeleteOptions options) throws ApiException {
api.delete(object.getMetadata().getNamespace(),
object.getMetadata().getName(), options).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>
T apply(GenericKubernetesApi<T, LT> api, T existing, String update)
throws ApiException {
PatchOptions opts = new PatchOptions();
opts.setForce(true);
opts.setFieldManager("kubernetes-java-kubectl-apply");
var response = api.patch(existing.getMetadata().getNamespace(),
existing.getMetadata().getName(), V1Patch.PATCH_FORMAT_APPLY_YAML,
new V1Patch(update), opts).throwsApiException();
return response.getObject();
}
}