Viewer ACL (#26)
Some checks failed
Java CI with Gradle / build (push) Has been cancelled

Provide ACLs (together with general improvements) for the viewer conlet.
This commit is contained in:
Michael N. Lipp 2024-06-01 11:12:15 +02:00 committed by GitHub
parent a6525a2289
commit 659463b3b4
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
42 changed files with 1664 additions and 679 deletions

View file

@ -0,0 +1,197 @@
/*
* VM-Operator
* Copyright (C) 2024 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.common;
import com.google.gson.Gson;
import com.google.gson.InstanceCreator;
import com.google.gson.JsonObject;
import com.google.gson.TypeAdapter;
import com.google.gson.TypeAdapterFactory;
import com.google.gson.reflect.TypeToken;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonWriter;
import io.kubernetes.client.openapi.ApiClient;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Type;
/**
* A factory for creating objects.
*
* @param <O> the generic type
* @param <L> the generic type
*/
public class DynamicTypeAdapterFactory<O extends K8sDynamicModel,
L extends K8sDynamicModelsBase<O>> implements TypeAdapterFactory {
private final Class<O> objectClass;
private final Class<L> objectListClass;
/**
* Make sure that this adapter is registered.
*
* @param client the client
*/
public void register(ApiClient client) {
if (!ModelCreator.class
.equals(client.getJSON().getGson().getAdapter(objectClass)
.getClass())
|| !ModelsCreator.class.equals(client.getJSON().getGson()
.getAdapter(objectListClass).getClass())) {
Gson gson = client.getJSON().getGson();
client.getJSON().setGson(gson.newBuilder()
.registerTypeAdapterFactory(this).create());
}
}
/**
* Instantiates a new generic type adapter factory.
*
* @param objectClass the object class
* @param objectListClass the object list class
*/
public DynamicTypeAdapterFactory(Class<O> objectClass,
Class<L> objectListClass) {
this.objectClass = objectClass;
this.objectListClass = objectListClass;
}
/**
* Creates a type adapter for the given type.
*
* @param <T> the generic type
* @param gson the gson
* @param typeToken the type token
* @return the type adapter or null if the type is not handles by
* this factory
*/
@SuppressWarnings("unchecked")
@Override
public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> typeToken) {
if (TypeToken.get(objectClass).equals(typeToken)) {
return (TypeAdapter<T>) new ModelCreator(gson);
}
if (TypeToken.get(objectListClass).equals(typeToken)) {
return (TypeAdapter<T>) new ModelsCreator(gson);
}
return null;
}
/**
* The Class ModelCreator.
*/
private class ModelCreator extends TypeAdapter<O>
implements InstanceCreator<O> {
private final Gson delegate;
/**
* Instantiates a new object state creator.
*
* @param delegate the delegate
*/
public ModelCreator(Gson delegate) {
this.delegate = delegate;
}
@Override
public O createInstance(Type type) {
try {
return objectClass.getConstructor(Gson.class, JsonObject.class)
.newInstance(delegate, null);
} catch (InstantiationException | IllegalAccessException
| IllegalArgumentException | InvocationTargetException
| NoSuchMethodException | SecurityException e) {
return null;
}
}
@Override
public void write(JsonWriter jsonWriter, O state)
throws IOException {
jsonWriter.jsonValue(delegate.toJson(state.data()));
}
@Override
public O read(JsonReader jsonReader)
throws IOException {
try {
return objectClass.getConstructor(Gson.class, JsonObject.class)
.newInstance(delegate,
delegate.fromJson(jsonReader, JsonObject.class));
} catch (InstantiationException | IllegalAccessException
| IllegalArgumentException | InvocationTargetException
| NoSuchMethodException | SecurityException e) {
return null;
}
}
}
/**
* The Class ModelsCreator.
*/
private class ModelsCreator extends TypeAdapter<L>
implements InstanceCreator<L> {
private final Gson delegate;
/**
* Instantiates a new object states creator.
*
* @param delegate the delegate
*/
public ModelsCreator(Gson delegate) {
this.delegate = delegate;
}
@Override
public L createInstance(Type type) {
try {
return objectListClass
.getConstructor(Gson.class, JsonObject.class)
.newInstance(delegate, null);
} catch (InstantiationException | IllegalAccessException
| IllegalArgumentException | InvocationTargetException
| NoSuchMethodException | SecurityException e) {
return null;
}
}
@Override
public void write(JsonWriter jsonWriter, L states)
throws IOException {
jsonWriter.jsonValue(delegate.toJson(states.data()));
}
@Override
public L read(JsonReader jsonReader)
throws IOException {
try {
return objectListClass
.getConstructor(Gson.class, JsonObject.class)
.newInstance(delegate,
delegate.fromJson(jsonReader, JsonObject.class));
} catch (InstantiationException | IllegalAccessException
| IllegalArgumentException | InvocationTargetException
| NoSuchMethodException | SecurityException e) {
return null;
}
}
}
}

View file

@ -1,131 +0,0 @@
/*
* VM-Operator
* Copyright (C) 2024 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.common;
import com.google.gson.Gson;
import com.google.gson.InstanceCreator;
import com.google.gson.JsonObject;
import com.google.gson.TypeAdapter;
import com.google.gson.TypeAdapterFactory;
import com.google.gson.reflect.TypeToken;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonWriter;
import java.io.IOException;
import java.lang.reflect.Type;
/**
* A factory for creating K8sDynamicModel(s) objects.
*/
public class K8sDynamicModelTypeAdapterFactory implements TypeAdapterFactory {
/**
* Creates a type adapter for the given type.
*
* @param <T> the generic type
* @param gson the gson
* @param typeToken the type token
* @return the type adapter or null if the type is not handles by
* this factory
*/
@SuppressWarnings("unchecked")
@Override
public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> typeToken) {
if (TypeToken.get(K8sDynamicModel.class).equals(typeToken)) {
return (TypeAdapter<T>) new K8sDynamicModelCreator(gson);
}
if (TypeToken.get(K8sDynamicModels.class).equals(typeToken)) {
return (TypeAdapter<T>) new K8sDynamicModelsCreator(gson);
}
return null;
}
/**
* The Class K8sDynamicModelCreator.
*/
/* default */ class K8sDynamicModelCreator
extends TypeAdapter<K8sDynamicModel>
implements InstanceCreator<K8sDynamicModel> {
private final Gson delegate;
/**
* Instantiates a new object state creator.
*
* @param delegate the delegate
*/
public K8sDynamicModelCreator(Gson delegate) {
this.delegate = delegate;
}
@Override
public K8sDynamicModel createInstance(Type type) {
return new K8sDynamicModel(delegate, null);
}
@Override
public void write(JsonWriter jsonWriter, K8sDynamicModel state)
throws IOException {
jsonWriter.jsonValue(delegate.toJson(state.data()));
}
@Override
public K8sDynamicModel read(JsonReader jsonReader)
throws IOException {
return new K8sDynamicModel(delegate,
delegate.fromJson(jsonReader, JsonObject.class));
}
}
/**
* The Class K8sDynamicModelsCreator.
*/
/* default */class K8sDynamicModelsCreator
extends TypeAdapter<K8sDynamicModels>
implements InstanceCreator<K8sDynamicModels> {
private final Gson delegate;
/**
* Instantiates a new object states creator.
*
* @param delegate the delegate
*/
public K8sDynamicModelsCreator(Gson delegate) {
this.delegate = delegate;
}
@Override
public K8sDynamicModels createInstance(Type type) {
return new K8sDynamicModels(delegate, null);
}
@Override
public void write(JsonWriter jsonWriter, K8sDynamicModels states)
throws IOException {
jsonWriter.jsonValue(delegate.toJson(states.data()));
}
@Override
public K8sDynamicModels read(JsonReader jsonReader)
throws IOException {
return new K8sDynamicModels(delegate,
delegate.fromJson(jsonReader, JsonObject.class));
}
}
}

View file

@ -19,14 +19,8 @@
package org.jdrupes.vmoperator.common;
import com.google.gson.Gson;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import io.kubernetes.client.common.KubernetesListObject;
import io.kubernetes.client.openapi.Configuration;
import io.kubernetes.client.openapi.models.V1ListMeta;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
/**
* Represents a list of Kubernetes objects each of which is
@ -35,11 +29,7 @@ import java.util.Objects;
* notably the metadata, is made available through the methods
* defined by {@link KubernetesListObject}.
*/
public class K8sDynamicModels implements KubernetesListObject {
private final JsonObject data;
private final V1ListMeta metadata;
private final List<K8sDynamicModel> items;
public class K8sDynamicModels extends K8sDynamicModelsBase<K8sDynamicModel> {
/**
* Initialize the object list using the given JSON data.
@ -48,116 +38,7 @@ public class K8sDynamicModels implements KubernetesListObject {
* @param data the data
*/
public K8sDynamicModels(Gson delegate, JsonObject data) {
this.data = data;
metadata = delegate.fromJson(data.get("metadata"), V1ListMeta.class);
items = new ArrayList<>();
for (JsonElement e : data.get("items").getAsJsonArray()) {
items.add(new K8sDynamicModel(delegate, e.getAsJsonObject()));
}
super(K8sDynamicModel.class, delegate, data);
}
@Override
public String getApiVersion() {
return apiVersion();
}
/**
* Gets the API version. (Abbreviated method name for convenience.)
*
* @return the API version
*/
public String apiVersion() {
return data.get("apiVersion").getAsString();
}
@Override
public String getKind() {
return kind();
}
/**
* Gets the kind. (Abbreviated method name for convenience.)
*
* @return the kind
*/
public String kind() {
return data.get("kind").getAsString();
}
@Override
public V1ListMeta getMetadata() {
return metadata;
}
/**
* Gets the metadata. (Abbreviated method name for convenience.)
*
* @return the metadata
*/
public V1ListMeta metadata() {
return metadata;
}
/**
* Returns the JSON representation of this object.
*
* @return the JOSN representation
*/
public JsonObject data() {
return data;
}
@Override
public List<K8sDynamicModel> getItems() {
return items;
}
/**
* Sets the api version.
*
* @param apiVersion the new api version
*/
public void setApiVersion(String apiVersion) {
data.addProperty("apiVersion", apiVersion);
}
/**
* Sets the kind.
*
* @param kind the new kind
*/
public void setKind(String kind) {
data.addProperty("kind", kind);
}
/**
* Sets the metadata.
*
* @param objectMeta the new metadata
*/
public void setMetadata(V1ListMeta objectMeta) {
data.add("metadata",
Configuration.getDefaultApiClient().getJSON().getGson()
.toJsonTree(objectMeta));
}
@Override
public int hashCode() {
return Objects.hash(data);
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
K8sDynamicModels other = (K8sDynamicModels) obj;
return Objects.equals(data, other.data);
}
}

View file

@ -0,0 +1,174 @@
/*
* VM-Operator
* Copyright (C) 2024 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.common;
import com.google.gson.Gson;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import io.kubernetes.client.common.KubernetesListObject;
import io.kubernetes.client.openapi.Configuration;
import io.kubernetes.client.openapi.models.V1ListMeta;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
/**
* Represents a list of Kubernetes objects each of which is
* represented using a JSON data structure.
* Some information that is common to all Kubernetes objects,
* notably the metadata, is made available through the methods
* defined by {@link KubernetesListObject}.
*/
public class K8sDynamicModelsBase<T extends K8sDynamicModel>
implements KubernetesListObject {
private final JsonObject data;
private final V1ListMeta metadata;
private final List<T> items;
/**
* Initialize the object list using the given JSON data.
*
* @param itemClass the item class
* @param delegate the gson instance to use for extracting structured data
* @param data the data
*/
public K8sDynamicModelsBase(Class<T> itemClass, Gson delegate,
JsonObject data) {
this.data = data;
metadata = delegate.fromJson(data.get("metadata"), V1ListMeta.class);
items = new ArrayList<>();
for (JsonElement e : data.get("items").getAsJsonArray()) {
try {
items.add(itemClass.getConstructor(Gson.class, JsonObject.class)
.newInstance(delegate, e.getAsJsonObject()));
} catch (InstantiationException | IllegalAccessException
| IllegalArgumentException | InvocationTargetException
| NoSuchMethodException | SecurityException exc) {
throw new IllegalArgumentException(exc);
}
}
}
@Override
public String getApiVersion() {
return apiVersion();
}
/**
* Gets the API version. (Abbreviated method name for convenience.)
*
* @return the API version
*/
public String apiVersion() {
return data.get("apiVersion").getAsString();
}
@Override
public String getKind() {
return kind();
}
/**
* Gets the kind. (Abbreviated method name for convenience.)
*
* @return the kind
*/
public String kind() {
return data.get("kind").getAsString();
}
@Override
public V1ListMeta getMetadata() {
return metadata;
}
/**
* Gets the metadata. (Abbreviated method name for convenience.)
*
* @return the metadata
*/
public V1ListMeta metadata() {
return metadata;
}
/**
* Returns the JSON representation of this object.
*
* @return the JOSN representation
*/
public JsonObject data() {
return data;
}
@Override
public List<T> getItems() {
return items;
}
/**
* Sets the api version.
*
* @param apiVersion the new api version
*/
public void setApiVersion(String apiVersion) {
data.addProperty("apiVersion", apiVersion);
}
/**
* Sets the kind.
*
* @param kind the new kind
*/
public void setKind(String kind) {
data.addProperty("kind", kind);
}
/**
* Sets the metadata.
*
* @param objectMeta the new metadata
*/
public void setMetadata(V1ListMeta objectMeta) {
data.add("metadata",
Configuration.getDefaultApiClient().getJSON().getGson()
.toJsonTree(objectMeta));
}
@Override
public int hashCode() {
return Objects.hash(data);
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
K8sDynamicModelsBase<?> other = (K8sDynamicModelsBase<?>) obj;
return Objects.equals(data, other.data);
}
}

View file

@ -18,10 +18,8 @@
package org.jdrupes.vmoperator.common;
import com.google.gson.Gson;
import io.kubernetes.client.Discovery.APIResource;
import io.kubernetes.client.apimachinery.GroupVersionKind;
import io.kubernetes.client.openapi.ApiClient;
import io.kubernetes.client.openapi.ApiException;
import io.kubernetes.client.util.generic.options.ListOptions;
import java.io.Reader;
@ -35,40 +33,23 @@ import java.util.Collection;
*/
@SuppressWarnings("PMD.DataflowAnomalyAnalysis")
public class K8sDynamicStub
extends K8sGenericStub<K8sDynamicModel, K8sDynamicModels> {
extends K8sDynamicStubBase<K8sDynamicModel, K8sDynamicModels> {
private static DynamicTypeAdapterFactory<K8sDynamicModel,
K8sDynamicModels> taf = new K8sDynamicModelTypeAdapterFactory();
/**
* Instantiates a new dynamic stub.
*
* @param objectClass the object class
* @param objectListClass the object list class
* @param client the client
* @param context the context
* @param namespace the namespace
* @param name the name
*/
public K8sDynamicStub(Class<K8sDynamicModel> objectClass,
Class<K8sDynamicModels> objectListClass, K8sClient client,
public K8sDynamicStub(K8sClient client,
APIResource context, String namespace, String name) {
super(objectClass, objectListClass, client, context, namespace, name);
// Make sure that we have an adapter for our type
Gson gson = client.getJSON().getGson();
if (!checkAdapters(client)) {
client.getJSON().setGson(gson.newBuilder()
.registerTypeAdapterFactory(
new K8sDynamicModelTypeAdapterFactory())
.create());
}
}
private boolean checkAdapters(ApiClient client) {
return K8sDynamicModelTypeAdapterFactory.K8sDynamicModelCreator.class
.equals(client.getJSON().getGson().getAdapter(K8sDynamicModel.class)
.getClass())
&& K8sDynamicModelTypeAdapterFactory.K8sDynamicModelsCreator.class
.equals(client.getJSON().getGson()
.getAdapter(K8sDynamicModels.class).getClass());
super(K8sDynamicModel.class, K8sDynamicModels.class, taf, client,
context, namespace, name);
}
/**
@ -88,8 +69,8 @@ public class K8sDynamicStub
public static K8sDynamicStub get(K8sClient client,
GroupVersionKind gvk, String namespace, String name)
throws ApiException {
return K8sGenericStub.get(K8sDynamicModel.class, K8sDynamicModels.class,
client, gvk, namespace, name, K8sDynamicStub::new);
return new K8sDynamicStub(client, apiResource(client, gvk), namespace,
name);
}
/**
@ -106,8 +87,7 @@ public class K8sDynamicStub
"PMD.AvoidInstantiatingObjectsInLoops", "PMD.UseObjectForClearerAPI" })
public static K8sDynamicStub get(K8sClient client,
APIResource context, String namespace, String name) {
return K8sGenericStub.get(K8sDynamicModel.class, K8sDynamicModels.class,
client, context, namespace, name, K8sDynamicStub::new);
return new K8sDynamicStub(client, context, namespace, name);
}
/**
@ -125,7 +105,7 @@ public class K8sDynamicStub
K8s.yamlToJson(client, yaml));
return K8sGenericStub.create(K8sDynamicModel.class,
K8sDynamicModels.class, client, context, model,
K8sDynamicStub::new);
(c, ns, n) -> new K8sDynamicStub(c, context, ns, n));
}
/**
@ -143,7 +123,7 @@ public class K8sDynamicStub
throws ApiException {
return K8sGenericStub.list(K8sDynamicModel.class,
K8sDynamicModels.class, client, context, namespace, options,
K8sDynamicStub::new);
(c, ns, n) -> new K8sDynamicStub(c, context, ns, n));
}
/**
@ -160,4 +140,18 @@ public class K8sDynamicStub
return list(client, context, namespace, new ListOptions());
}
/**
* A factory for creating K8sDynamicModel(s) objects.
*/
public static class K8sDynamicModelTypeAdapterFactory extends
DynamicTypeAdapterFactory<K8sDynamicModel, K8sDynamicModels> {
/**
* Instantiates a new dynamic model type adapter factory.
*/
public K8sDynamicModelTypeAdapterFactory() {
super(K8sDynamicModel.class, K8sDynamicModels.class);
}
}
}

View file

@ -0,0 +1,51 @@
/*
* VM-Operator
* Copyright (C) 2024 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.common;
import io.kubernetes.client.Discovery.APIResource;
/**
* A stub for namespaced custom objects. It uses a dynamic model
* (see {@link K8sDynamicModel}) for representing the object's
* state and can therefore be used for any kind of object, especially
* custom objects.
*/
@SuppressWarnings("PMD.DataflowAnomalyAnalysis")
public abstract class K8sDynamicStubBase<O extends K8sDynamicModel,
L extends K8sDynamicModelsBase<O>> extends K8sGenericStub<O, L> {
/**
* Instantiates a new dynamic stub.
*
* @param objectClass the object class
* @param objectListClass the object list class
* @param client the client
* @param context the context
* @param namespace the namespace
* @param name the name
*/
@SuppressWarnings("PMD.ConstructorCallsOverridableMethod")
public K8sDynamicStubBase(Class<O> objectClass,
Class<L> objectListClass, DynamicTypeAdapterFactory<O, L> taf,
K8sClient client, APIResource context, String namespace,
String name) {
super(objectClass, objectListClass, client, context, namespace, name);
taf.register(client);
}
}

View file

@ -26,9 +26,11 @@ import io.kubernetes.client.custom.V1Patch;
import io.kubernetes.client.openapi.ApiException;
import io.kubernetes.client.util.Strings;
import io.kubernetes.client.util.generic.GenericKubernetesApi;
import io.kubernetes.client.util.generic.KubernetesApiResponse;
import io.kubernetes.client.util.generic.options.GetOptions;
import io.kubernetes.client.util.generic.options.ListOptions;
import io.kubernetes.client.util.generic.options.PatchOptions;
import io.kubernetes.client.util.generic.options.UpdateOptions;
import java.net.HttpURLConnection;
import java.util.ArrayList;
import java.util.Collection;
@ -228,7 +230,8 @@ public class K8sGenericStub<O extends KubernetesObject,
public Optional<O> patch(String patchType, V1Patch patch,
PatchOptions options) throws ApiException {
return K8s
.optional(api.patch(namespace, name, patchType, patch, options));
.optional(api.patch(namespace, name, patchType, patch, options)
.throwsApiException());
}
/**
@ -245,6 +248,30 @@ public class K8sGenericStub<O extends KubernetesObject,
return patch(patchType, patch, opts);
}
/**
* Update the object.
*
* @param object the object
* @return the kubernetes api response
* @throws ApiException the api exception
*/
public KubernetesApiResponse<O> update(O object) throws ApiException {
return api.update(object).throwsApiException();
}
/**
* Update the object.
*
* @param object the object
* @param options the options
* @return the kubernetes api response
* @throws ApiException the api exception
*/
public KubernetesApiResponse<O> update(O object, UpdateOptions options)
throws ApiException {
return api.update(object, options).throwsApiException();
}
/**
* A supplier for generic stubs.
*
@ -258,17 +285,13 @@ public class K8sGenericStub<O extends KubernetesObject,
/**
* Gets a new stub.
*
* @param objectClass the object class
* @param objectListClass the object list class
* @param client the client
* @param context the API resource
* @param namespace the namespace
* @param name the name
* @return the result
*/
@SuppressWarnings("PMD.UseObjectForClearerAPI")
R get(Class<O> objectClass, Class<L> objectListClass, K8sClient client,
APIResource context, String namespace, String name);
R get(K8sClient client, String namespace, String name);
}
@Override
@ -278,68 +301,6 @@ public class K8sGenericStub<O extends KubernetesObject,
+ version().toUpperCase() + kind() + " " + namespace + ":" + name;
}
/**
* Get a namespaced object stub. If the version in parameter
* `gvk` is an empty string, the stub refers to the first object
* found with matching group and kind.
*
* @param <O> the object type
* @param <L> the object list type
* @param <R> the stub type
* @param objectClass the object class
* @param objectListClass the object list class
* @param client the client
* @param gvk the group, version and kind
* @param namespace the namespace
* @param name the name
* @param provider the provider
* @return the stub if the object exists
* @throws ApiException the api exception
*/
@SuppressWarnings({ "PMD.AvoidBranchingStatementAsLastInLoop" })
public static <O extends KubernetesObject, L extends KubernetesListObject,
R extends K8sGenericStub<O, L>>
R get(Class<O> objectClass, Class<L> objectListClass,
K8sClient client, GroupVersionKind gvk, String namespace,
String name, GenericSupplier<O, L, R> provider)
throws ApiException {
var context = K8s.context(client, gvk.getGroup(), gvk.getVersion(),
gvk.getKind());
if (context.isEmpty()) {
throw new ApiException("No known API for " + gvk.getGroup()
+ "/" + gvk.getVersion() + " " + gvk.getKind());
}
return provider.get(objectClass, objectListClass, client, context.get(),
namespace, name);
}
/**
* Get a namespaced object stub.
*
* @param <O> the object type
* @param <L> the object list type
* @param <R> the stub type
* @param objectClass the object class
* @param objectListClass the object list class
* @param client the client
* @param context the context
* @param namespace the namespace
* @param name the name
* @param provider the provider
* @return the stub if the object exists
* @throws ApiException the api exception
*/
@SuppressWarnings({ "PMD.AvoidBranchingStatementAsLastInLoop",
"PMD.UseObjectForClearerAPI" })
public static <O extends KubernetesObject, L extends KubernetesListObject,
R extends K8sGenericStub<O, L>>
R get(Class<O> objectClass, Class<L> objectListClass,
K8sClient client, APIResource context, String namespace,
String name, GenericSupplier<O, L, R> provider) {
return provider.get(objectClass, objectListClass, client,
context, namespace, name);
}
/**
* Get a namespaced object stub for a newly created object.
*
@ -366,8 +327,7 @@ public class K8sGenericStub<O extends KubernetesObject,
context.getGroup(), context.getPreferredVersion(),
context.getResourcePlural(), client);
api.create(model).throwsApiException();
return provider.get(objectClass, objectListClass, client,
context, model.getMetadata().getNamespace(),
return provider.get(client, model.getMetadata().getNamespace(),
model.getMetadata().getName());
}
@ -402,8 +362,8 @@ public class K8sGenericStub<O extends KubernetesObject,
client);
var objs = api.list(namespace, options).throwsApiException();
for (var item : objs.getObject().getItems()) {
result.add(provider.get(objectClass, objectListClass, client,
context, namespace, item.getMetadata().getName()));
result.add(provider.get(client, namespace,
item.getMetadata().getName()));
}
}
return result;
@ -416,4 +376,23 @@ public class K8sGenericStub<O extends KubernetesObject,
return result;
}
/**
* Api resource.
*
* @param client the client
* @param gvk the gvk
* @return the API resource
* @throws ApiException the api exception
*/
public static APIResource apiResource(K8sClient client,
GroupVersionKind gvk) throws ApiException {
var context = K8s.context(client, gvk.getGroup(), gvk.getVersion(),
gvk.getKind());
if (context.isEmpty()) {
throw new ApiException("No known API for " + gvk.getGroup()
+ "/" + gvk.getVersion() + " " + gvk.getKind());
}
return context.get();
}
}

View file

@ -73,18 +73,7 @@ public class K8sV1PodStub extends K8sGenericStub<V1Pod, V1PodList> {
public static Collection<K8sV1PodStub> list(K8sClient client,
String namespace, ListOptions options) throws ApiException {
return K8sGenericStub.list(V1Pod.class, V1PodList.class, client,
CONTEXT, namespace, options, K8sV1PodStub::getGeneric);
CONTEXT, namespace, options, (clnt, nscp,
name) -> new K8sV1PodStub(clnt, nscp, name));
}
/**
* Provide {@link GenericSupplier}.
*/
@SuppressWarnings({ "PMD.UnusedFormalParameter",
"PMD.UnusedPrivateMethod" })
private static K8sV1PodStub getGeneric(Class<V1Pod> objectClass,
Class<V1PodList> objectListClass, K8sClient client,
APIResource context, String namespace, String name) {
return new K8sV1PodStub(client, namespace, name);
}
}

View file

@ -25,7 +25,6 @@ import io.kubernetes.client.openapi.models.V1SecretList;
import io.kubernetes.client.util.generic.options.ListOptions;
import java.util.Collection;
import java.util.List;
import org.jdrupes.vmoperator.common.K8sGenericStub.GenericSupplier;
/**
* A stub for secrets (v1).
@ -62,6 +61,20 @@ public class K8sV1SecretStub extends K8sGenericStub<V1Secret, V1SecretList> {
return new K8sV1SecretStub(client, namespace, name);
}
/**
* Creates an object stub from a model.
*
* @param client the client
* @param model the model
* @return the k 8 s dynamic stub
* @throws ApiException the api exception
*/
public static K8sV1SecretStub create(K8sClient client, V1Secret model)
throws ApiException {
return K8sGenericStub.create(V1Secret.class,
V1SecretList.class, client, CONTEXT, model, K8sV1SecretStub::new);
}
/**
* Get the stubs for the objects in the given namespace that match
* the criteria from the given options.
@ -75,18 +88,6 @@ public class K8sV1SecretStub extends K8sGenericStub<V1Secret, V1SecretList> {
public static Collection<K8sV1SecretStub> list(K8sClient client,
String namespace, ListOptions options) throws ApiException {
return K8sGenericStub.list(V1Secret.class, V1SecretList.class, client,
CONTEXT, namespace, options, K8sV1SecretStub::getGeneric);
CONTEXT, namespace, options, K8sV1SecretStub::new);
}
/**
* Provide {@link GenericSupplier}.
*/
@SuppressWarnings({ "PMD.UnusedFormalParameter",
"PMD.UnusedPrivateMethod" })
private static K8sV1SecretStub getGeneric(Class<V1Secret> objectClass,
Class<V1SecretList> objectListClass, K8sClient client,
APIResource context, String namespace, String name) {
return new K8sV1SecretStub(client, namespace, name);
}
}

View file

@ -25,7 +25,6 @@ import io.kubernetes.client.openapi.models.V1ServiceList;
import io.kubernetes.client.util.generic.options.ListOptions;
import java.util.Collection;
import java.util.List;
import org.jdrupes.vmoperator.common.K8sGenericStub.GenericSupplier;
/**
* A stub for secrets (v1).
@ -75,18 +74,7 @@ public class K8sV1ServiceStub extends K8sGenericStub<V1Service, V1ServiceList> {
public static Collection<K8sV1ServiceStub> list(K8sClient client,
String namespace, ListOptions options) throws ApiException {
return K8sGenericStub.list(V1Service.class, V1ServiceList.class, client,
CONTEXT, namespace, options, K8sV1ServiceStub::getGeneric);
CONTEXT, namespace, options,
(clnt, nscp, name) -> new K8sV1ServiceStub(clnt, nscp, name));
}
/**
* Provide {@link GenericSupplier}.
*/
@SuppressWarnings({ "PMD.UnusedFormalParameter",
"PMD.UnusedPrivateMethod" })
private static K8sV1ServiceStub getGeneric(Class<V1Service> objectClass,
Class<V1ServiceList> objectListClass, K8sClient client,
APIResource context, String namespace, String name) {
return new K8sV1ServiceStub(client, namespace, name);
}
}

View file

@ -0,0 +1,123 @@
/*
* VM-Operator
* Copyright (C) 2024 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.common;
import com.google.gson.Gson;
import com.google.gson.JsonObject;
import com.google.gson.JsonPrimitive;
import java.util.Collection;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;
import org.jdrupes.vmoperator.util.GsonPtr;
/**
* Represents a VM definition.
*/
@SuppressWarnings("PMD.DataClass")
public class VmDefinitionModel extends K8sDynamicModel {
/**
* Permissions for accessing and manipulating the VM.
*/
public enum Permission {
START("start"), STOP("stop"), ACCESS_CONSOLE("accessConsole");
@SuppressWarnings("PMD.UseConcurrentHashMap")
private static Map<String, Permission> reprs = new HashMap<>();
static {
for (var value : EnumSet.allOf(Permission.class)) {
reprs.put(value.repr, value);
}
}
private final String repr;
Permission(String repr) {
this.repr = repr;
}
/**
* Create permission from representation in CRD.
*
* @param value the value
* @return the permission
*/
@SuppressWarnings("PMD.AvoidLiteralsInIfCondition")
public static Set<Permission> parse(String value) {
if ("*".equals(value)) {
return EnumSet.allOf(Permission.class);
}
return Set.of(reprs.get(value));
}
@Override
public String toString() {
return repr;
}
}
/**
* Instantiates a new model from the JSON representation.
*
* @param delegate the gson instance to use for extracting structured data
* @param json the JSON
*/
public VmDefinitionModel(Gson delegate, JsonObject json) {
super(delegate, json);
}
/**
* Collect all permissions for the given user with the given roles.
*
* @param user the user
* @param roles the roles
* @return the sets the
*/
public Set<Permission> permissionsFor(String user,
Collection<String> roles) {
return GsonPtr.to(data())
.getAsListOf(JsonObject.class, "spec", "permissions")
.stream().filter(p -> GsonPtr.to(p).getAsString("user")
.map(u -> u.equals(user)).orElse(false)
|| GsonPtr.to(p).getAsString("role").map(roles::contains)
.orElse(false))
.map(p -> GsonPtr.to(p).getAsListOf(JsonPrimitive.class, "may")
.stream())
.flatMap(Function.identity()).map(p -> p.getAsString())
.map(Permission::parse).map(Set::stream)
.flatMap(Function.identity()).collect(Collectors.toSet());
}
/**
* Get the display password serial.
*
* @return the optional
*/
public Optional<Long> displayPasswordSerial() {
return GsonPtr.to(status())
.get(JsonPrimitive.class, "displayPasswordSerial")
.map(JsonPrimitive::getAsLong);
}
}

View file

@ -0,0 +1,39 @@
/*
* VM-Operator
* Copyright (C) 2024 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.common;
import com.google.gson.Gson;
import com.google.gson.JsonObject;
/**
* Represents a list of {@link VmDefinitionModel}s.
*/
public class VmDefinitionModels
extends K8sDynamicModelsBase<VmDefinitionModel> {
/**
* Initialize the object list using the given JSON data.
*
* @param delegate the gson instance to use for extracting structured data
* @param data the data
*/
public VmDefinitionModels(Gson delegate, JsonObject data) {
super(VmDefinitionModel.class, delegate, data);
}
}

View file

@ -0,0 +1,157 @@
/*
* VM-Operator
* Copyright (C) 2024 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.common;
import io.kubernetes.client.Discovery.APIResource;
import io.kubernetes.client.apimachinery.GroupVersionKind;
import io.kubernetes.client.openapi.ApiException;
import io.kubernetes.client.util.generic.options.ListOptions;
import java.io.Reader;
import java.util.Collection;
/**
* A stub for namespaced custom objects. It uses a dynamic model
* (see {@link K8sDynamicModel}) for representing the object's
* state and can therefore be used for any kind of object, especially
* custom objects.
*/
@SuppressWarnings("PMD.DataflowAnomalyAnalysis")
public class VmDefinitionStub
extends K8sDynamicStubBase<VmDefinitionModel, VmDefinitionModels> {
private static DynamicTypeAdapterFactory<VmDefinitionModel,
VmDefinitionModels> taf = new VmDefintionModelTypeAdapterFactory();
/**
* Instantiates a new stub for VM defintions.
*
* @param client the client
* @param context the context
* @param namespace the namespace
* @param name the name
*/
public VmDefinitionStub(K8sClient client, APIResource context,
String namespace, String name) {
super(VmDefinitionModel.class, VmDefinitionModels.class, taf, client,
context, namespace, name);
}
/**
* Get a dynamic object stub. If the version in parameter
* `gvk` is an empty string, the stub refers to the first object with
* matching group and kind.
*
* @param client the client
* @param gvk the group, version and kind
* @param namespace the namespace
* @param name the name
* @return the stub if the object exists
* @throws ApiException the api exception
*/
@SuppressWarnings({ "PMD.AvoidBranchingStatementAsLastInLoop",
"PMD.AvoidInstantiatingObjectsInLoops", "PMD.UseObjectForClearerAPI" })
public static VmDefinitionStub get(K8sClient client,
GroupVersionKind gvk, String namespace, String name)
throws ApiException {
return new VmDefinitionStub(client, apiResource(client, gvk), namespace,
name);
}
/**
* Get a dynamic object stub.
*
* @param client the client
* @param context the context
* @param namespace the namespace
* @param name the name
* @return the stub if the object exists
* @throws ApiException the api exception
*/
@SuppressWarnings({ "PMD.AvoidBranchingStatementAsLastInLoop",
"PMD.AvoidInstantiatingObjectsInLoops", "PMD.UseObjectForClearerAPI" })
public static VmDefinitionStub get(K8sClient client,
APIResource context, String namespace, String name) {
return new VmDefinitionStub(client, context, namespace, name);
}
/**
* Creates a stub from yaml.
*
* @param client the client
* @param context the context
* @param yaml the yaml
* @return the k 8 s dynamic stub
* @throws ApiException the api exception
*/
public static VmDefinitionStub createFromYaml(K8sClient client,
APIResource context, Reader yaml) throws ApiException {
var model = new VmDefinitionModel(client.getJSON().getGson(),
K8s.yamlToJson(client, yaml));
return K8sGenericStub.create(VmDefinitionModel.class,
VmDefinitionModels.class, client, context, model,
(c, ns, n) -> new VmDefinitionStub(c, context, ns, n));
}
/**
* Get the stubs for the objects in the given namespace that match
* the criteria from the given options.
*
* @param client the client
* @param namespace the namespace
* @param options the options
* @return the collection
* @throws ApiException the api exception
*/
public static Collection<VmDefinitionStub> list(K8sClient client,
APIResource context, String namespace, ListOptions options)
throws ApiException {
return K8sGenericStub.list(VmDefinitionModel.class,
VmDefinitionModels.class, client, context, namespace, options,
(c, ns, n) -> new VmDefinitionStub(c, context, ns, n));
}
/**
* Get the stubs for the objects in the given namespace.
*
* @param client the client
* @param namespace the namespace
* @return the collection
* @throws ApiException the api exception
*/
public static Collection<VmDefinitionStub> list(K8sClient client,
APIResource context, String namespace)
throws ApiException {
return list(client, context, namespace, new ListOptions());
}
/**
* A factory for creating VmDefinitionModel(s) objects.
*/
public static class VmDefintionModelTypeAdapterFactory extends
DynamicTypeAdapterFactory<VmDefinitionModel, VmDefinitionModels> {
/**
* Instantiates a new dynamic model type adapter factory.
*/
public VmDefintionModelTypeAdapterFactory() {
super(VmDefinitionModel.class, VmDefinitionModels.class);
}
}
}