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

@ -32,6 +32,9 @@ import java.io.IOException;
import java.io.StringWriter;
import java.util.Map;
import java.util.logging.Logger;
import static org.jdrupes.vmoperator.manager.Constants.APP_NAME;
import static org.jdrupes.vmoperator.util.Constants.VM_OP_NAME;
import org.jdrupes.vmoperator.util.K8s;
import org.yaml.snakeyaml.LoaderOptions;
import org.yaml.snakeyaml.Yaml;
import org.yaml.snakeyaml.constructor.SafeConstructor;
@ -98,8 +101,8 @@ import org.yaml.snakeyaml.constructor.SafeConstructor;
DynamicKubernetesObject newCm) {
ListOptions listOpts = new ListOptions();
listOpts.setLabelSelector(
"app.kubernetes.io/managed-by=" + Constants.VM_OP_NAME + ","
+ "app.kubernetes.io/name=" + Constants.APP_NAME);
"app.kubernetes.io/managed-by=" + VM_OP_NAME + ","
+ "app.kubernetes.io/name=" + APP_NAME);
// Get pod, selected by label
var podApi = new DynamicKubernetesApi("", "v1", "pods", client);
var pods = podApi

View file

@ -23,15 +23,6 @@ package org.jdrupes.vmoperator.manager;
*/
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_KIND_VM. */
public static final String VM_OP_KIND_VM = "VirtualMachine";
/** The Constant APP_NAME. */
public static final String APP_NAME = "vm-runner";

View file

@ -1,275 +0,0 @@
/*
* 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 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

@ -1,162 +0,0 @@
/*
* 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.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();
}
}

View file

@ -33,6 +33,8 @@ import java.util.Collections;
import java.util.Map;
import java.util.Optional;
import java.util.logging.Logger;
import org.jdrupes.vmoperator.util.GsonPtr;
import org.jdrupes.vmoperator.util.K8s;
import org.yaml.snakeyaml.LoaderOptions;
import org.yaml.snakeyaml.Yaml;
import org.yaml.snakeyaml.constructor.SafeConstructor;

View file

@ -30,7 +30,7 @@ import org.apache.commons.cli.CommandLineParser;
import org.apache.commons.cli.DefaultParser;
import org.apache.commons.cli.Option;
import org.apache.commons.cli.Options;
import static org.jdrupes.vmoperator.manager.Constants.VM_OP_NAME;
import static org.jdrupes.vmoperator.util.Constants.VM_OP_NAME;
import org.jdrupes.vmoperator.util.FsdUtils;
import org.jgrapes.core.Component;
import org.jgrapes.core.Components;

View file

@ -43,10 +43,12 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import static org.jdrupes.vmoperator.manager.Constants.VM_OP_GROUP;
import org.jdrupes.vmoperator.manager.VmDefChanged.Type;
import static org.jdrupes.vmoperator.util.Constants.VM_OP_GROUP;
import org.jdrupes.vmoperator.util.Convertions;
import org.jdrupes.vmoperator.util.ExtendedObjectWrapper;
import org.jdrupes.vmoperator.util.GsonPtr;
import org.jdrupes.vmoperator.util.K8s;
import org.jgrapes.core.Channel;
import org.jgrapes.core.Component;
import org.jgrapes.core.annotation.Handler;

View file

@ -29,6 +29,8 @@ import java.io.IOException;
import java.io.StringWriter;
import java.util.Map;
import java.util.logging.Logger;
import org.jdrupes.vmoperator.util.GsonPtr;
import org.jdrupes.vmoperator.util.K8s;
import org.yaml.snakeyaml.LoaderOptions;
import org.yaml.snakeyaml.Yaml;
import org.yaml.snakeyaml.constructor.SafeConstructor;

View file

@ -44,9 +44,11 @@ import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Level;
import static org.jdrupes.vmoperator.manager.Constants.VM_OP_GROUP;
import static org.jdrupes.vmoperator.manager.Constants.VM_OP_KIND_VM;
import static org.jdrupes.vmoperator.manager.Constants.APP_NAME;
import org.jdrupes.vmoperator.manager.VmDefChanged.Type;
import static org.jdrupes.vmoperator.util.Constants.VM_OP_GROUP;
import static org.jdrupes.vmoperator.util.Constants.VM_OP_KIND_VM;
import static org.jdrupes.vmoperator.util.Constants.VM_OP_NAME;
import org.jgrapes.core.Channel;
import org.jgrapes.core.Component;
import org.jgrapes.core.Components;
@ -132,7 +134,7 @@ public class VmWatcher extends Component {
for (var version : vmOpApiVersions) {
coa.getAPIResources(VM_OP_GROUP, version)
.getResources().stream()
.filter(r -> Constants.VM_OP_KIND_VM.equals(r.getKind()))
.filter(r -> VM_OP_KIND_VM.equals(r.getKind()))
.findFirst()
.ifPresent(crd -> watchVmDefs(crd, version));
}
@ -148,15 +150,15 @@ public class VmWatcher extends Component {
// Get all known CR instances.
coa.getAPIResources(VM_OP_GROUP, version)
.getResources().stream()
.filter(r -> Constants.VM_OP_KIND_VM.equals(r.getKind()))
.filter(r -> VM_OP_KIND_VM.equals(r.getKind()))
.findFirst()
.ifPresent(crd -> known.addAll(getKnown(client, crd, version)));
}
ListOptions opts = new ListOptions();
opts.setLabelSelector(
"app.kubernetes.io/managed-by=" + Constants.VM_OP_NAME + ","
+ "app.kubernetes.io/name=" + Constants.APP_NAME);
"app.kubernetes.io/managed-by=" + VM_OP_NAME + ","
+ "app.kubernetes.io/name=" + APP_NAME);
for (String resource : List.of("apps/v1/statefulsets",
"v1/configmaps", "v1/secrets")) {
var resParts = new LinkedList<>(List.of(resource.split("/")));