Support for display secrets (#21)
Some checks failed
Java CI with Gradle / build (push) Has been cancelled
Some checks failed
Java CI with Gradle / build (push) Has been cancelled
This commit is contained in:
parent
85b0a160f3
commit
3103452170
38 changed files with 2081 additions and 658 deletions
|
|
@ -26,6 +26,9 @@ public class Constants {
|
|||
/** The Constant APP_NAME. */
|
||||
public static final String APP_NAME = "vm-runner";
|
||||
|
||||
/** The Constant COMP_DISPLAY_SECRETS. */
|
||||
public static final String COMP_DISPLAY_SECRET = "display-secret";
|
||||
|
||||
/** The Constant VM_OP_NAME. */
|
||||
public static final String VM_OP_NAME = "vm-operator";
|
||||
|
||||
|
|
|
|||
|
|
@ -44,6 +44,7 @@ import org.yaml.snakeyaml.LoaderOptions;
|
|||
import org.yaml.snakeyaml.Yaml;
|
||||
import org.yaml.snakeyaml.constructor.SafeConstructor;
|
||||
|
||||
// TODO: Auto-generated Javadoc
|
||||
/**
|
||||
* Helpers for K8s API.
|
||||
*/
|
||||
|
|
@ -74,6 +75,35 @@ public class K8s {
|
|||
return Optional.empty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new context with the given version as preferred version.
|
||||
*
|
||||
* @param context the context
|
||||
* @param version the version
|
||||
* @return the API resource
|
||||
*/
|
||||
public static APIResource preferred(APIResource context, String version) {
|
||||
assert context.getVersions().contains(version);
|
||||
return new APIResource(context.getGroup(),
|
||||
context.getVersions(), version, context.getKind(),
|
||||
context.getNamespaced(), context.getResourcePlural(),
|
||||
context.getResourceSingular());
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a string representation of the context (API resource).
|
||||
*
|
||||
* @param context the context
|
||||
* @return the string
|
||||
*/
|
||||
@SuppressWarnings("PMD.UseLocaleWithCaseConversions")
|
||||
public static String toString(APIResource context) {
|
||||
return (Strings.isNullOrEmpty(context.getGroup()) ? ""
|
||||
: context.getGroup() + "/")
|
||||
+ context.getPreferredVersion().toUpperCase()
|
||||
+ context.getKind();
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert Yaml to Json.
|
||||
*
|
||||
|
|
@ -156,6 +186,7 @@ public class K8s {
|
|||
* @param api the api
|
||||
* @param existing the existing
|
||||
* @param update the update
|
||||
* @return the t
|
||||
* @throws ApiException the api exception
|
||||
*/
|
||||
public static <T extends KubernetesObject, LT extends KubernetesListObject>
|
||||
|
|
@ -199,8 +230,10 @@ public class K8s {
|
|||
* * If `type` is not set, set it to "Normal"
|
||||
* * If `regarding` is not set, set it to the given object.
|
||||
*
|
||||
* @param client the client
|
||||
* @param object the object
|
||||
* @param event the event
|
||||
* @throws ApiException
|
||||
* @throws ApiException the api exception
|
||||
*/
|
||||
@SuppressWarnings("PMD.NPathComplexity")
|
||||
public static void createEvent(ApiClient client,
|
||||
|
|
|
|||
|
|
@ -18,10 +18,14 @@
|
|||
|
||||
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;
|
||||
import java.util.Collection;
|
||||
|
||||
/**
|
||||
* A stub for namespaced custom objects. It uses a dynamic model
|
||||
|
|
@ -47,6 +51,24 @@ public class K8sDynamicStub
|
|||
Class<K8sDynamicModels> objectListClass, 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());
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -83,8 +105,7 @@ public class K8sDynamicStub
|
|||
@SuppressWarnings({ "PMD.AvoidBranchingStatementAsLastInLoop",
|
||||
"PMD.AvoidInstantiatingObjectsInLoops", "PMD.UseObjectForClearerAPI" })
|
||||
public static K8sDynamicStub get(K8sClient client,
|
||||
APIResource context, String namespace, String name)
|
||||
throws ApiException {
|
||||
APIResource context, String namespace, String name) {
|
||||
return K8sGenericStub.get(K8sDynamicModel.class, K8sDynamicModels.class,
|
||||
client, context, namespace, name, K8sDynamicStub::new);
|
||||
}
|
||||
|
|
@ -106,4 +127,37 @@ public class K8sDynamicStub
|
|||
K8sDynamicModels.class, client, context, model,
|
||||
K8sDynamicStub::new);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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<K8sDynamicStub> list(K8sClient client,
|
||||
APIResource context, String namespace, ListOptions options)
|
||||
throws ApiException {
|
||||
return K8sGenericStub.list(K8sDynamicModel.class,
|
||||
K8sDynamicModels.class, client, context, namespace, options,
|
||||
K8sDynamicStub::new);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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<K8sDynamicStub> list(K8sClient client,
|
||||
APIResource context, String namespace)
|
||||
throws ApiException {
|
||||
return list(client, context, namespace, new ListOptions());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -18,21 +18,22 @@
|
|||
|
||||
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.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.util.Strings;
|
||||
import io.kubernetes.client.util.generic.GenericKubernetesApi;
|
||||
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 java.net.HttpURLConnection;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Function;
|
||||
|
||||
|
|
@ -49,145 +50,16 @@ public class K8sGenericStub<O extends KubernetesObject,
|
|||
L extends KubernetesListObject> {
|
||||
protected final K8sClient client;
|
||||
private final GenericKubernetesApi<O, L> api;
|
||||
protected final String group;
|
||||
protected final String version;
|
||||
protected final String kind;
|
||||
protected final String plural;
|
||||
protected final APIResource context;
|
||||
protected final String namespace;
|
||||
protected final String 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",
|
||||
"PMD.AvoidInstantiatingObjectsInLoops" })
|
||||
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.AvoidInstantiatingObjectsInLoops", "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)
|
||||
throws ApiException {
|
||||
return provider.get(objectClass, objectListClass, client,
|
||||
context, namespace, name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a namespaced object stub for a newly created object.
|
||||
*
|
||||
* @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 model the model
|
||||
* @param provider the provider
|
||||
* @return the stub if the object exists
|
||||
* @throws ApiException the api exception
|
||||
*/
|
||||
@SuppressWarnings({ "PMD.AvoidBranchingStatementAsLastInLoop",
|
||||
"PMD.AvoidInstantiatingObjectsInLoops", "PMD.UseObjectForClearerAPI" })
|
||||
public static <O extends KubernetesObject, L extends KubernetesListObject,
|
||||
R extends K8sGenericStub<O, L>>
|
||||
R create(Class<O> objectClass, Class<L> objectListClass,
|
||||
K8sClient client, APIResource context, O model,
|
||||
GenericSupplier<O, L, R> provider) throws ApiException {
|
||||
var api = new GenericKubernetesApi<>(objectClass, objectListClass,
|
||||
context.getGroup(), context.getPreferredVersion(),
|
||||
context.getResourcePlural(), client);
|
||||
api.create(model).throwsApiException();
|
||||
return provider.get(objectClass, objectListClass, client,
|
||||
context, model.getMetadata().getNamespace(),
|
||||
model.getMetadata().getName());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the stubs for the objects in the given namespace that match
|
||||
* the criteria from the given options.
|
||||
*
|
||||
* @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 options the options
|
||||
* @param provider the provider
|
||||
* @return the collection
|
||||
* @throws ApiException the api exception
|
||||
*/
|
||||
public static <O extends KubernetesObject, L extends KubernetesListObject,
|
||||
R extends K8sGenericStub<O, L>>
|
||||
Collection<R> list(Class<O> objectClass, Class<L> objectListClass,
|
||||
K8sClient client, APIResource context, String namespace,
|
||||
ListOptions options, SpecificSupplier<O, L, R> provider)
|
||||
throws ApiException {
|
||||
var api = new GenericKubernetesApi<>(objectClass, objectListClass,
|
||||
context.getGroup(), context.getPreferredVersion(),
|
||||
context.getResourcePlural(), client);
|
||||
var objs = api.list(namespace, options).throwsApiException();
|
||||
var result = new ArrayList<R>();
|
||||
for (var item : objs.getObject().getItems()) {
|
||||
result.add(
|
||||
provider.get(client, namespace, item.getMetadata().getName()));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiates a new namespaced custom object stub.
|
||||
* Instantiates a new stub for the object specified. If the object
|
||||
* exists in the context specified, the version (see
|
||||
* {@link #version()} is bound to the existing object's version.
|
||||
* Else the stub is dangling with the version set to the context's
|
||||
* preferred version.
|
||||
*
|
||||
* @param objectClass the object class
|
||||
* @param objectListClass the object list class
|
||||
|
|
@ -196,35 +68,47 @@ public class K8sGenericStub<O extends KubernetesObject,
|
|||
* @param namespace the namespace
|
||||
* @param name the name
|
||||
*/
|
||||
@SuppressWarnings("PMD.AvoidInstantiatingObjectsInLoops")
|
||||
protected K8sGenericStub(Class<O> objectClass, Class<L> objectListClass,
|
||||
K8sClient client, APIResource context, String namespace,
|
||||
String name) {
|
||||
this.client = client;
|
||||
group = context.getGroup();
|
||||
version = context.getPreferredVersion();
|
||||
kind = context.getKind();
|
||||
plural = context.getResourcePlural();
|
||||
this.namespace = namespace;
|
||||
this.name = name;
|
||||
|
||||
Gson gson = client.getJSON().getGson();
|
||||
if (!checkAdapters(client)) {
|
||||
client.getJSON().setGson(gson.newBuilder()
|
||||
.registerTypeAdapterFactory(
|
||||
new K8sDynamicModelTypeAdapterFactory())
|
||||
.create());
|
||||
// Bind version
|
||||
var foundVersion = context.getPreferredVersion();
|
||||
GenericKubernetesApi<O, L> testApi = null;
|
||||
GetOptions mdOpts
|
||||
= new GetOptions().isPartialObjectMetadataRequest(true);
|
||||
for (var version : candidateVersions(context)) {
|
||||
testApi = new GenericKubernetesApi<>(objectClass, objectListClass,
|
||||
context.getGroup(), version, context.getResourcePlural(),
|
||||
client);
|
||||
if (testApi.get(namespace, name, mdOpts)
|
||||
.isSuccess()) {
|
||||
foundVersion = version;
|
||||
break;
|
||||
}
|
||||
}
|
||||
api = new GenericKubernetesApi<>(objectClass,
|
||||
objectListClass, group, version, plural, client);
|
||||
if (foundVersion.equals(context.getPreferredVersion())) {
|
||||
this.context = context;
|
||||
} else {
|
||||
this.context = K8s.preferred(context, foundVersion);
|
||||
}
|
||||
|
||||
api = Optional.ofNullable(testApi)
|
||||
.orElseGet(() -> new GenericKubernetesApi<>(objectClass,
|
||||
objectListClass, group(), version(), plural(), client));
|
||||
}
|
||||
|
||||
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());
|
||||
/**
|
||||
* Gets the context.
|
||||
*
|
||||
* @return the context
|
||||
*/
|
||||
public APIResource context() {
|
||||
return context;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -233,7 +117,7 @@ public class K8sGenericStub<O extends KubernetesObject,
|
|||
* @return the group
|
||||
*/
|
||||
public String group() {
|
||||
return group;
|
||||
return context.getGroup();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -242,7 +126,7 @@ public class K8sGenericStub<O extends KubernetesObject,
|
|||
* @return the version
|
||||
*/
|
||||
public String version() {
|
||||
return version;
|
||||
return context.getPreferredVersion();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -251,7 +135,7 @@ public class K8sGenericStub<O extends KubernetesObject,
|
|||
* @return the kind
|
||||
*/
|
||||
public String kind() {
|
||||
return kind;
|
||||
return context.getKind();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -260,7 +144,7 @@ public class K8sGenericStub<O extends KubernetesObject,
|
|||
* @return the plural
|
||||
*/
|
||||
public String plural() {
|
||||
return plural;
|
||||
return context.getResourcePlural();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -387,32 +271,149 @@ public class K8sGenericStub<O extends KubernetesObject,
|
|||
APIResource context, String namespace, String name);
|
||||
}
|
||||
|
||||
/**
|
||||
* A supplier for specific stubs.
|
||||
*
|
||||
* @param <O> the object type
|
||||
* @param <L> the object list type
|
||||
* @param <R> the result type
|
||||
*/
|
||||
public interface SpecificSupplier<O extends KubernetesObject,
|
||||
L extends KubernetesListObject, R extends K8sGenericStub<O, L>> {
|
||||
|
||||
/**
|
||||
* Gets a new stub.
|
||||
*
|
||||
* @param client the client
|
||||
* @param namespace the namespace
|
||||
* @param name the name
|
||||
* @return the result
|
||||
*/
|
||||
R get(K8sClient client, String namespace, String name);
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("PMD.UseLocaleWithCaseConversions")
|
||||
public String toString() {
|
||||
return (Strings.isNullOrEmpty(group) ? "" : group + "/")
|
||||
+ version.toUpperCase() + kind + " " + namespace + ":" + name;
|
||||
return (Strings.isNullOrEmpty(group()) ? "" : group() + "/")
|
||||
+ 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.
|
||||
*
|
||||
* @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 model the model
|
||||
* @param provider the provider
|
||||
* @return the stub if the object exists
|
||||
* @throws ApiException the api exception
|
||||
*/
|
||||
@SuppressWarnings({ "PMD.AvoidBranchingStatementAsLastInLoop",
|
||||
"PMD.AvoidInstantiatingObjectsInLoops", "PMD.UseObjectForClearerAPI" })
|
||||
public static <O extends KubernetesObject, L extends KubernetesListObject,
|
||||
R extends K8sGenericStub<O, L>>
|
||||
R create(Class<O> objectClass, Class<L> objectListClass,
|
||||
K8sClient client, APIResource context, O model,
|
||||
GenericSupplier<O, L, R> provider) throws ApiException {
|
||||
var api = new GenericKubernetesApi<>(objectClass, objectListClass,
|
||||
context.getGroup(), context.getPreferredVersion(),
|
||||
context.getResourcePlural(), client);
|
||||
api.create(model).throwsApiException();
|
||||
return provider.get(objectClass, objectListClass, client,
|
||||
context, model.getMetadata().getNamespace(),
|
||||
model.getMetadata().getName());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the stubs for the objects in the given namespace that match
|
||||
* the criteria from the given options.
|
||||
*
|
||||
* @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 options the options
|
||||
* @param provider the provider
|
||||
* @return the collection
|
||||
* @throws ApiException the api exception
|
||||
*/
|
||||
public static <O extends KubernetesObject, L extends KubernetesListObject,
|
||||
R extends K8sGenericStub<O, L>>
|
||||
Collection<R> list(Class<O> objectClass, Class<L> objectListClass,
|
||||
K8sClient client, APIResource context, String namespace,
|
||||
ListOptions options, GenericSupplier<O, L, R> provider)
|
||||
throws ApiException {
|
||||
var result = new ArrayList<R>();
|
||||
for (var version : candidateVersions(context)) {
|
||||
@SuppressWarnings("PMD.AvoidInstantiatingObjectsInLoops")
|
||||
var api = new GenericKubernetesApi<>(objectClass, objectListClass,
|
||||
context.getGroup(), version, context.getResourcePlural(),
|
||||
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()));
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private static List<String> candidateVersions(APIResource context) {
|
||||
var result = new LinkedList<>(context.getVersions());
|
||||
result.remove(context.getPreferredVersion());
|
||||
result.add(0, context.getPreferredVersion());
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,230 @@
|
|||
/*
|
||||
* 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.common.KubernetesListObject;
|
||||
import io.kubernetes.client.common.KubernetesObject;
|
||||
import io.kubernetes.client.openapi.ApiException;
|
||||
import io.kubernetes.client.util.Watch.Response;
|
||||
import io.kubernetes.client.util.generic.GenericKubernetesApi;
|
||||
import io.kubernetes.client.util.generic.options.ListOptions;
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
/**
|
||||
* An observer that watches namespaced resources in a given context and
|
||||
* invokes a handler on changes.
|
||||
*
|
||||
* @param <O> the object type for the context
|
||||
* @param <L> the object list type for the context
|
||||
*/
|
||||
public class K8sObserver<O extends KubernetesObject,
|
||||
L extends KubernetesListObject> {
|
||||
|
||||
/**
|
||||
* The type of change reported by {@link Response} as enum.
|
||||
*/
|
||||
public enum ResponseType {
|
||||
ADDED, MODIFIED, DELETED
|
||||
}
|
||||
|
||||
@SuppressWarnings("PMD.FieldNamingConventions")
|
||||
protected final Logger logger = Logger.getLogger(getClass().getName());
|
||||
|
||||
protected final K8sClient client;
|
||||
protected final GenericKubernetesApi<O, L> api;
|
||||
protected final APIResource context;
|
||||
protected final String namespace;
|
||||
protected final ListOptions options;
|
||||
protected final Thread thread;
|
||||
protected BiConsumer<K8sClient, Response<O>> handler;
|
||||
protected BiConsumer<K8sObserver<O, L>, Throwable> onTerminated;
|
||||
|
||||
/**
|
||||
* Create and start a new observer for objects in the given context
|
||||
* (using preferred version) and namespace with the given options.
|
||||
*
|
||||
* @param objectClass the object class
|
||||
* @param objectListClass the object list class
|
||||
* @param client the client
|
||||
* @param context the context
|
||||
* @param namespace the namespace
|
||||
* @param options the options
|
||||
*/
|
||||
@SuppressWarnings({ "PMD.AvoidBranchingStatementAsLastInLoop",
|
||||
"PMD.UseObjectForClearerAPI", "PMD.AvoidCatchingThrowable",
|
||||
"PMD.CognitiveComplexity" })
|
||||
public K8sObserver(Class<O> objectClass, Class<L> objectListClass,
|
||||
K8sClient client, APIResource context, String namespace,
|
||||
ListOptions options) {
|
||||
this.client = client;
|
||||
this.context = context;
|
||||
this.namespace = namespace;
|
||||
this.options = options;
|
||||
|
||||
api = new GenericKubernetesApi<>(objectClass, objectListClass,
|
||||
context.getGroup(), context.getPreferredVersion(),
|
||||
context.getResourcePlural(), client);
|
||||
thread = new Thread(() -> {
|
||||
try {
|
||||
logger.info(() -> "Watching " + context.getResourcePlural()
|
||||
+ " (" + context.getPreferredVersion() + ")"
|
||||
+ " in " + namespace);
|
||||
|
||||
// Watch sometimes terminates without apparent reason.
|
||||
while (!Thread.currentThread().isInterrupted()) {
|
||||
Instant startedAt = Instant.now();
|
||||
try {
|
||||
@SuppressWarnings("PMD.AvoidInstantiatingObjectsInLoops")
|
||||
var changed = api.watch(namespace, options).iterator();
|
||||
while (changed.hasNext()) {
|
||||
handler.accept(client, changed.next());
|
||||
}
|
||||
} catch (ApiException e) {
|
||||
logger.log(Level.FINE, e, () -> "Problem watching"
|
||||
+ " (will retry): " + e.getMessage());
|
||||
delayRestart(startedAt);
|
||||
}
|
||||
}
|
||||
if (onTerminated != null) {
|
||||
onTerminated.accept(this, null);
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
logger.log(Level.SEVERE, e, () -> "Probem watching: "
|
||||
+ e.getMessage());
|
||||
if (onTerminated != null) {
|
||||
onTerminated.accept(this, e);
|
||||
}
|
||||
}
|
||||
});
|
||||
thread.setDaemon(true);
|
||||
}
|
||||
|
||||
@SuppressWarnings("PMD.AvoidLiteralsInIfCondition")
|
||||
private void delayRestart(Instant started) {
|
||||
var runningFor = Duration
|
||||
.between(started, Instant.now()).toMillis();
|
||||
if (runningFor < 5000) {
|
||||
logger.log(Level.FINE, () -> "Waiting... ");
|
||||
try {
|
||||
Thread.sleep(5000 - runningFor);
|
||||
} catch (InterruptedException e1) { // NOPMD
|
||||
// Retry
|
||||
}
|
||||
logger.log(Level.FINE, () -> "Retrying");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the handler.
|
||||
*
|
||||
* @param handler the handler
|
||||
* @return the observer
|
||||
*/
|
||||
public K8sObserver<O, L>
|
||||
handler(BiConsumer<K8sClient, Response<O>> handler) {
|
||||
this.handler = handler;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a function to invoke if the observer terminates. First argument
|
||||
* is this observer, the second is the throwable that caused the
|
||||
* abnormal termination or `null` if the observer was terminated
|
||||
* by {@link #stop()}.
|
||||
*
|
||||
* @param onTerminated the on terminated
|
||||
* @return the observer
|
||||
*/
|
||||
public K8sObserver<O, L> onTerminated(
|
||||
BiConsumer<K8sObserver<O, L>, Throwable> onTerminated) {
|
||||
this.onTerminated = onTerminated;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Start the observer.
|
||||
*
|
||||
* @return the observer
|
||||
*/
|
||||
public K8sObserver<O, L> start() {
|
||||
if (handler == null) {
|
||||
throw new IllegalStateException("No handler defined");
|
||||
}
|
||||
thread.start();
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Stops the observer.
|
||||
*
|
||||
* @return the observer
|
||||
*/
|
||||
public K8sObserver<O, L> stop() {
|
||||
thread.interrupt();
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the client.
|
||||
*
|
||||
* @return the client
|
||||
*/
|
||||
public K8sClient client() {
|
||||
return client;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the context.
|
||||
*
|
||||
* @return the context
|
||||
*/
|
||||
public APIResource context() {
|
||||
return context;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the observed namespace.
|
||||
*
|
||||
* @return the namespace
|
||||
*/
|
||||
public String getNamespace() {
|
||||
return namespace;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the options for object selection.
|
||||
*
|
||||
* @return the list options
|
||||
*/
|
||||
public ListOptions options() {
|
||||
return options;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("PMD.UseLocaleWithCaseConversions")
|
||||
public String toString() {
|
||||
return "Observer for " + K8s.toString(context) + " " + namespace;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -30,6 +30,9 @@ import java.util.List;
|
|||
public class K8sV1ConfigMapStub
|
||||
extends K8sGenericStub<V1ConfigMap, V1ConfigMapList> {
|
||||
|
||||
public static final APIResource CONTEXT = new APIResource("", List.of("v1"),
|
||||
"v1", "ConfigMap", true, "configmaps", "configmap");
|
||||
|
||||
/**
|
||||
* Instantiates a new stub.
|
||||
*
|
||||
|
|
@ -40,9 +43,7 @@ public class K8sV1ConfigMapStub
|
|||
protected K8sV1ConfigMapStub(K8sClient client, String namespace,
|
||||
String name) {
|
||||
super(V1ConfigMap.class, V1ConfigMapList.class, client,
|
||||
new APIResource("", List.of("v1"), "v1", "ConfigMap", true,
|
||||
"configmaps", "configmap"),
|
||||
namespace, name);
|
||||
CONTEXT, namespace, name);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -33,6 +33,10 @@ import java.util.Optional;
|
|||
public class K8sV1DeploymentStub
|
||||
extends K8sGenericStub<V1Deployment, V1DeploymentList> {
|
||||
|
||||
/** The deployment's context. */
|
||||
public static final APIResource CONTEXT = new APIResource("apps",
|
||||
List.of("v1"), "v1", "Pod", true, "deployments", "deployment");
|
||||
|
||||
/**
|
||||
* Instantiates a new stub.
|
||||
*
|
||||
|
|
@ -43,22 +47,7 @@ public class K8sV1DeploymentStub
|
|||
protected K8sV1DeploymentStub(K8sClient client, String namespace,
|
||||
String name) {
|
||||
super(V1Deployment.class, V1DeploymentList.class, client,
|
||||
new APIResource("apps", List.of("v1"), "v1", "Pod", true,
|
||||
"deployments", "deployment"),
|
||||
namespace, name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the stub for the given namespace and name.
|
||||
*
|
||||
* @param client the client
|
||||
* @param namespace the namespace
|
||||
* @param name the name
|
||||
* @return the deployment stub
|
||||
*/
|
||||
public static K8sV1DeploymentStub get(K8sClient client, String namespace,
|
||||
String name) {
|
||||
return new K8sV1DeploymentStub(client, namespace, name);
|
||||
CONTEXT, namespace, name);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -74,4 +63,17 @@ public class K8sV1DeploymentStub
|
|||
+ "\", \"value\": " + replicas + "}]"),
|
||||
client.defaultPatchOptions());
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the stub for the given namespace and name.
|
||||
*
|
||||
* @param client the client
|
||||
* @param namespace the namespace
|
||||
* @param name the name
|
||||
* @return the deployment stub
|
||||
*/
|
||||
public static K8sV1DeploymentStub get(K8sClient client, String namespace,
|
||||
String name) {
|
||||
return new K8sV1DeploymentStub(client, namespace, name);
|
||||
}
|
||||
}
|
||||
|
|
@ -32,6 +32,7 @@ import java.util.List;
|
|||
@SuppressWarnings("PMD.DataflowAnomalyAnalysis")
|
||||
public class K8sV1PodStub extends K8sGenericStub<V1Pod, V1PodList> {
|
||||
|
||||
/** The pods' context. */
|
||||
public static final APIResource CONTEXT
|
||||
= new APIResource("", List.of("v1"), "v1", "Pod", true, "pods", "pod");
|
||||
|
||||
|
|
@ -72,7 +73,17 @@ 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::new);
|
||||
CONTEXT, namespace, options, K8sV1PodStub::getGeneric);
|
||||
}
|
||||
|
||||
/**
|
||||
* Provide {@link GenericSupplier}.
|
||||
*/
|
||||
@SuppressWarnings("PMD.UnusedFormalParameter")
|
||||
private static K8sV1PodStub getGeneric(Class<V1Pod> objectClass,
|
||||
Class<V1PodList> objectListClass, K8sClient client,
|
||||
APIResource context, String namespace, String name) {
|
||||
return new K8sV1PodStub(client, namespace, name);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,60 @@
|
|||
/*
|
||||
* 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.openapi.models.V1Secret;
|
||||
import io.kubernetes.client.openapi.models.V1SecretList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* A stub for secrets (v1).
|
||||
*/
|
||||
@SuppressWarnings("PMD.DataflowAnomalyAnalysis")
|
||||
public class K8sV1SecretStub extends K8sGenericStub<V1Secret, V1SecretList> {
|
||||
|
||||
public static final APIResource CONTEXT = new APIResource("", List.of("v1"),
|
||||
"v1", "Secret", true, "secrets", "secret");
|
||||
|
||||
/**
|
||||
* Instantiates a new stub.
|
||||
*
|
||||
* @param client the client
|
||||
* @param namespace the namespace
|
||||
* @param name the name
|
||||
*/
|
||||
protected K8sV1SecretStub(K8sClient client, String namespace,
|
||||
String name) {
|
||||
super(V1Secret.class, V1SecretList.class, client,
|
||||
CONTEXT, namespace, name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the stub for the given namespace and name.
|
||||
*
|
||||
* @param client the client
|
||||
* @param namespace the namespace
|
||||
* @param name the name
|
||||
* @return the config map stub
|
||||
*/
|
||||
public static K8sV1SecretStub get(K8sClient client, String namespace,
|
||||
String name) {
|
||||
return new K8sV1SecretStub(client, namespace, name);
|
||||
}
|
||||
}
|
||||
|
|
@ -30,6 +30,11 @@ import java.util.List;
|
|||
public class K8sV1StatefulSetStub
|
||||
extends K8sGenericStub<V1StatefulSet, V1StatefulSetList> {
|
||||
|
||||
/** The stateful sets' context */
|
||||
public static final APIResource CONTEXT
|
||||
= new APIResource("apps", List.of("v1"), "v1", "StatefulSet", true,
|
||||
"statefulsets", "statefulset");
|
||||
|
||||
/**
|
||||
* Instantiates a new stub.
|
||||
*
|
||||
|
|
@ -39,9 +44,7 @@ public class K8sV1StatefulSetStub
|
|||
*/
|
||||
protected K8sV1StatefulSetStub(K8sClient client, String namespace,
|
||||
String name) {
|
||||
super(V1StatefulSet.class, V1StatefulSetList.class, client,
|
||||
new APIResource("apps", List.of("v1"), "v1", "StatefulSet", true,
|
||||
"statefulsets", "statefulset"),
|
||||
super(V1StatefulSet.class, V1StatefulSetList.class, client, CONTEXT,
|
||||
namespace, name);
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue