Refactor internal Kubernetes API and upgrade to official v19 (#19)
Some checks failed
Java CI with Gradle / build (push) Has been cancelled

This commit is contained in:
Michael N. Lipp 2024-03-14 20:12:37 +01:00 committed by GitHub
parent ee2de96c56
commit a2641da7f5
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
28 changed files with 2343 additions and 395 deletions

View file

@ -0,0 +1,7 @@
add_header=true
eclipse.preferences.version=1
header_text=/*\n * VM-Operator\n * Copyright (C) 2024 Michael N. Lipp\n * \n * This program is free software\: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as\n * published by the Free Software Foundation, either version 3 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <https\://www.gnu.org/licenses/>.\n */
project_specific_settings=true
visibility_package=false
visibility_private=false
visibility_protected=false

View file

@ -10,5 +10,6 @@ plugins {
dependencies {
api project(':org.jdrupes.vmoperator.util')
api 'io.kubernetes:client-java:[18.0.0,19)'
api 'io.kubernetes:client-java:[19.0.0,20.0.0)'
api 'org.yaml:snakeyaml'
}

View file

@ -1,6 +1,6 @@
/*
* VM-Operator
* Copyright (C) 2023 Michael N. Lipp
* Copyright (C) 2023,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
@ -18,29 +18,31 @@
package org.jdrupes.vmoperator.common;
import com.google.gson.JsonObject;
import io.kubernetes.client.Discovery;
import io.kubernetes.client.Discovery.APIResource;
import io.kubernetes.client.common.KubernetesListObject;
import io.kubernetes.client.common.KubernetesObject;
import io.kubernetes.client.common.KubernetesType;
import io.kubernetes.client.custom.V1Patch;
import io.kubernetes.client.openapi.ApiClient;
import io.kubernetes.client.openapi.ApiException;
import io.kubernetes.client.openapi.apis.ApisApi;
import io.kubernetes.client.openapi.apis.CustomObjectsApi;
import io.kubernetes.client.openapi.models.V1APIGroup;
import io.kubernetes.client.openapi.models.V1ConfigMap;
import io.kubernetes.client.openapi.models.V1ConfigMapList;
import io.kubernetes.client.openapi.models.V1GroupVersionForDiscovery;
import io.kubernetes.client.openapi.apis.EventsV1Api;
import io.kubernetes.client.openapi.models.EventsV1Event;
import io.kubernetes.client.openapi.models.V1ObjectMeta;
import io.kubernetes.client.openapi.models.V1ObjectReference;
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.Strings;
import io.kubernetes.client.util.generic.GenericKubernetesApi;
import io.kubernetes.client.util.generic.dynamic.DynamicKubernetesApi;
import io.kubernetes.client.util.generic.dynamic.DynamicKubernetesObject;
import io.kubernetes.client.util.generic.options.DeleteOptions;
import io.kubernetes.client.util.generic.KubernetesApiResponse;
import io.kubernetes.client.util.generic.options.PatchOptions;
import java.io.Reader;
import java.net.HttpURLConnection;
import java.time.OffsetDateTime;
import java.util.Map;
import java.util.Optional;
import org.yaml.snakeyaml.LoaderOptions;
import org.yaml.snakeyaml.Yaml;
import org.yaml.snakeyaml.constructor.SafeConstructor;
/**
* Helpers for K8s API.
@ -50,89 +52,80 @@ import java.util.Optional;
public class K8s {
/**
* Given a groupVersion, returns only the version.
* Returns the result from an API call as {@link Optional} if the
* call was successful. Returns an empty `Optional` if the status
* code is 404 (not found). Else throws an exception.
*
* @param groupVersion the group version
* @return the string
* @param <T> the generic type
* @param response the response
* @return the optional
* @throws ApiException the API exception
*/
public static String version(String groupVersion) {
return groupVersion.substring(groupVersion.lastIndexOf('/') + 1);
public static <T extends KubernetesType> Optional<T>
optional(KubernetesApiResponse<T> response) throws ApiException {
if (response.isSuccess()) {
return Optional.of(response.getObject());
}
if (response.getHttpStatusCode() == HttpURLConnection.HTTP_NOT_FOUND) {
return Optional.empty();
}
response.throwsApiException();
// Never reached
return Optional.empty();
}
/**
* Get PVC API.
* Convert Yaml to Json.
*
* @param client the client
* @return the generic kubernetes api
* @param yaml the yaml
* @return the json element
*/
public static GenericKubernetesApi<V1PersistentVolumeClaim,
V1PersistentVolumeClaimList> pvcApi(ApiClient client) {
return new GenericKubernetesApi<>(V1PersistentVolumeClaim.class,
V1PersistentVolumeClaimList.class, "", "v1",
"persistentvolumeclaims", client);
public static JsonObject yamlToJson(ApiClient client, Reader yaml) {
// Avoid Yaml.load due to
// https://github.com/kubernetes-client/java/issues/2741
@SuppressWarnings("PMD.UseConcurrentHashMap")
Map<String, Object> yamlData
= new Yaml(new SafeConstructor(new LoaderOptions())).load(yaml);
// There's no short-cut from Java (collections) to Gson
var gson = client.getJSON().getGson();
var jsonText = gson.toJson(yamlData);
return gson.fromJson(jsonText, JsonObject.class);
}
/**
* 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 the API for a custom resource.
* Lookup the specified API resource. If the version is `null` or
* empty, the preferred version in the result is the default
* returned from the server.
*
* @param client the client
* @param group the group
* @param version the version
* @param kind the kind
* @param namespace the namespace
* @param name the name
* @return the dynamic kubernetes api
* @return the optional
* @throws ApiException the api exception
*/
@SuppressWarnings("PMD.UseObjectForClearerAPI")
public static Optional<DynamicKubernetesApi> crApi(ApiClient client,
String group, String kind, String namespace, String name)
throws ApiException {
var apis = new ApisApi(client).getAPIVersions();
var crdVersions = apis.getGroups().stream()
.filter(g -> g.getName().equals(group)).findFirst()
.map(V1APIGroup::getVersions).stream().flatMap(l -> l.stream())
.map(V1GroupVersionForDiscovery::getVersion).toList();
var coa = new CustomObjectsApi(client);
for (var crdVersion : crdVersions) {
var crdApiRes = coa.getAPIResources(group, crdVersion)
.getResources().stream().filter(r -> kind.equals(r.getKind()))
.findFirst();
if (crdApiRes.isEmpty()) {
continue;
}
@SuppressWarnings("PMD.AvoidInstantiatingObjectsInLoops")
var crApi = new DynamicKubernetesApi(group,
crdVersion, crdApiRes.get().getName(), client);
var customResource = crApi.get(namespace, name);
if (customResource.isSuccess()) {
return Optional.of(crApi);
}
public static Optional<APIResource> context(ApiClient client,
String group, String version, String kind) throws ApiException {
var apiMatch = new Discovery(client).findAll().stream()
.filter(r -> r.getGroup().equals(group) && r.getKind().equals(kind)
&& (Strings.isNullOrEmpty(version)
|| r.getVersions().contains(version)))
.findFirst();
if (apiMatch.isEmpty()) {
return Optional.empty();
}
return Optional.empty();
var apiRes = apiMatch.get();
if (!Strings.isNullOrEmpty(version)) {
if (!apiRes.getVersions().contains(version)) {
return Optional.empty();
}
apiRes = new APIResource(apiRes.getGroup(), apiRes.getVersions(),
version, apiRes.getKind(), apiRes.getNamespaced(),
apiRes.getResourcePlural(), apiRes.getResourceSingular());
}
return Optional.of(apiRes);
}
/**
@ -144,6 +137,7 @@ public class K8s {
* @param meta the meta
* @return the object
*/
@Deprecated
public static <T extends KubernetesObject, LT extends KubernetesListObject>
Optional<T>
get(GenericKubernetesApi<T, LT> api, V1ObjectMeta meta) {
@ -154,36 +148,6 @@ public class K8s {
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.
*
@ -213,7 +177,7 @@ public class K8s {
* @return the v 1 object reference
*/
public static V1ObjectReference
objectReference(DynamicKubernetesObject object) {
objectReference(KubernetesObject object) {
return new V1ObjectReference().apiVersion(object.getApiVersion())
.kind(object.getKind())
.namespace(object.getMetadata().getNamespace())
@ -221,4 +185,54 @@ public class K8s {
.resourceVersion(object.getMetadata().getResourceVersion())
.uid(object.getMetadata().getUid());
}
/**
* Creates an event related to the object, adding reasonable defaults.
*
* * If `kind` is not set, it is set to "Event".
* * If `metadata.namespace` is not set, it is set
* to the object's namespace.
* * If neither `metadata.name` nor `matadata.generateName` are set,
* set `generateName` to the object's name with a dash appended.
* * If `reportingInstance` is not set, set it to the object's name.
* * If `eventTime` is not set, set it to now.
* * If `type` is not set, set it to "Normal"
* * If `regarding` is not set, set it to the given object.
*
* @param event the event
* @throws ApiException
*/
@SuppressWarnings("PMD.NPathComplexity")
public static void createEvent(ApiClient client,
KubernetesObject object, EventsV1Event event)
throws ApiException {
if (Strings.isNullOrEmpty(event.getKind())) {
event.kind("Event");
}
if (event.getMetadata() == null) {
event.metadata(new V1ObjectMeta());
}
if (Strings.isNullOrEmpty(event.getMetadata().getNamespace())) {
event.getMetadata().namespace(object.getMetadata().getNamespace());
}
if (Strings.isNullOrEmpty(event.getMetadata().getName())
&& Strings.isNullOrEmpty(event.getMetadata().getGenerateName())) {
event.getMetadata()
.generateName(object.getMetadata().getName() + "-");
}
if (Strings.isNullOrEmpty(event.getReportingInstance())) {
event.reportingInstance(object.getMetadata().getName());
}
if (event.getEventTime() == null) {
event.eventTime(OffsetDateTime.now());
}
if (Strings.isNullOrEmpty(event.getType())) {
event.type("Normal");
}
if (event.getRegarding() == null) {
event.regarding(objectReference(object));
}
new EventsV1Api(client).createNamespacedEvent(
object.getMetadata().getNamespace(), event, null, null, null, null);
}
}

View file

@ -0,0 +1,759 @@
/*
* 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.openapi.ApiCallback;
import io.kubernetes.client.openapi.ApiClient;
import io.kubernetes.client.openapi.ApiException;
import io.kubernetes.client.openapi.ApiResponse;
import io.kubernetes.client.openapi.JSON;
import io.kubernetes.client.openapi.Pair;
import io.kubernetes.client.openapi.auth.Authentication;
import io.kubernetes.client.util.ClientBuilder;
import io.kubernetes.client.util.generic.options.PatchOptions;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Type;
import java.text.DateFormat;
import java.time.format.DateTimeFormatter;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import javax.net.ssl.KeyManager;
import okhttp3.Call;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Request.Builder;
import okhttp3.RequestBody;
import okhttp3.Response;
/**
* A client with some additional properties.
*/
@SuppressWarnings({ "PMD.ExcessivePublicCount", "PMD.TooManyMethods",
"PMD.LinguisticNaming", "checkstyle:LineLength" })
public class K8sClient extends ApiClient {
private ApiClient apiClient;
private PatchOptions defaultPatchOptions;
/**
* Instantiates a new client.
*
* @throws IOException Signals that an I/O exception has occurred.
*/
public K8sClient() throws IOException {
defaultPatchOptions = new PatchOptions();
defaultPatchOptions.setFieldManager("kubernetes-java-kubectl-apply");
}
private ApiClient apiClient() {
if (apiClient == null) {
try {
apiClient = ClientBuilder.standard().build();
} catch (IOException e) {
throw new IllegalStateException(e);
}
}
return apiClient;
}
/**
* Gets the default patch options.
*
* @return the defaultPatchOptions
*/
public PatchOptions defaultPatchOptions() {
return defaultPatchOptions;
}
/**
* Changes the default patch options.
*
* @param patchOptions the patch options
* @return the client
*/
public K8sClient with(PatchOptions patchOptions) {
defaultPatchOptions = patchOptions;
return this;
}
/**
* @return
* @see ApiClient#getBasePath()
*/
public String getBasePath() {
return apiClient().getBasePath();
}
/**
* @param basePath
* @return
* @see ApiClient#setBasePath(java.lang.String)
*/
public ApiClient setBasePath(String basePath) {
return apiClient().setBasePath(basePath);
}
/**
* @return
* @see ApiClient#getHttpClient()
*/
public OkHttpClient getHttpClient() {
return apiClient().getHttpClient();
}
/**
* @param newHttpClient
* @return
* @see ApiClient#setHttpClient(okhttp3.OkHttpClient)
*/
public ApiClient setHttpClient(OkHttpClient newHttpClient) {
return apiClient().setHttpClient(newHttpClient);
}
/**
* @return
* @see ApiClient#getJSON()
*/
@SuppressWarnings("abbreviationAsWordInName")
public JSON getJSON() {
return apiClient().getJSON();
}
/**
* @param json
* @return
* @see ApiClient#setJSON(io.kubernetes.client.openapi.JSON)
*/
@SuppressWarnings("abbreviationAsWordInName")
public ApiClient setJSON(JSON json) {
return apiClient().setJSON(json);
}
/**
* @return
* @see ApiClient#isVerifyingSsl()
*/
public boolean isVerifyingSsl() {
return apiClient().isVerifyingSsl();
}
/**
* @param verifyingSsl
* @return
* @see ApiClient#setVerifyingSsl(boolean)
*/
public ApiClient setVerifyingSsl(boolean verifyingSsl) {
return apiClient().setVerifyingSsl(verifyingSsl);
}
/**
* @return
* @see ApiClient#getSslCaCert()
*/
public InputStream getSslCaCert() {
return apiClient().getSslCaCert();
}
/**
* @param sslCaCert
* @return
* @see ApiClient#setSslCaCert(java.io.InputStream)
*/
public ApiClient setSslCaCert(InputStream sslCaCert) {
return apiClient().setSslCaCert(sslCaCert);
}
/**
* @return
* @see ApiClient#getKeyManagers()
*/
public KeyManager[] getKeyManagers() {
return apiClient().getKeyManagers();
}
/**
* @param managers
* @return
* @see ApiClient#setKeyManagers(javax.net.ssl.KeyManager[])
*/
@SuppressWarnings("PMD.UseVarargs")
public ApiClient setKeyManagers(KeyManager[] managers) {
return apiClient().setKeyManagers(managers);
}
/**
* @return
* @see ApiClient#getDateFormat()
*/
public DateFormat getDateFormat() {
return apiClient().getDateFormat();
}
/**
* @param dateFormat
* @return
* @see ApiClient#setDateFormat(java.text.DateFormat)
*/
public ApiClient setDateFormat(DateFormat dateFormat) {
return apiClient().setDateFormat(dateFormat);
}
/**
* @param dateFormat
* @return
* @see ApiClient#setSqlDateFormat(java.text.DateFormat)
*/
public ApiClient setSqlDateFormat(DateFormat dateFormat) {
return apiClient().setSqlDateFormat(dateFormat);
}
/**
* @param dateFormat
* @return
* @see ApiClient#setOffsetDateTimeFormat(java.time.format.DateTimeFormatter)
*/
public ApiClient setOffsetDateTimeFormat(DateTimeFormatter dateFormat) {
return apiClient().setOffsetDateTimeFormat(dateFormat);
}
/**
* @param dateFormat
* @return
* @see ApiClient#setLocalDateFormat(java.time.format.DateTimeFormatter)
*/
public ApiClient setLocalDateFormat(DateTimeFormatter dateFormat) {
return apiClient().setLocalDateFormat(dateFormat);
}
/**
* @param lenientOnJson
* @return
* @see ApiClient#setLenientOnJson(boolean)
*/
public ApiClient setLenientOnJson(boolean lenientOnJson) {
return apiClient().setLenientOnJson(lenientOnJson);
}
/**
* @return
* @see ApiClient#getAuthentications()
*/
public Map<String, Authentication> getAuthentications() {
return apiClient().getAuthentications();
}
/**
* @param authName
* @return
* @see ApiClient#getAuthentication(java.lang.String)
*/
public Authentication getAuthentication(String authName) {
return apiClient().getAuthentication(authName);
}
/**
* @param username
* @see ApiClient#setUsername(java.lang.String)
*/
public void setUsername(String username) {
apiClient().setUsername(username);
}
/**
* @param password
* @see ApiClient#setPassword(java.lang.String)
*/
public void setPassword(String password) {
apiClient().setPassword(password);
}
/**
* @param apiKey
* @see ApiClient#setApiKey(java.lang.String)
*/
public void setApiKey(String apiKey) {
apiClient().setApiKey(apiKey);
}
/**
* @param apiKeyPrefix
* @see ApiClient#setApiKeyPrefix(java.lang.String)
*/
public void setApiKeyPrefix(String apiKeyPrefix) {
apiClient().setApiKeyPrefix(apiKeyPrefix);
}
/**
* @param accessToken
* @see ApiClient#setAccessToken(java.lang.String)
*/
public void setAccessToken(String accessToken) {
apiClient().setAccessToken(accessToken);
}
/**
* @param userAgent
* @return
* @see ApiClient#setUserAgent(java.lang.String)
*/
public ApiClient setUserAgent(String userAgent) {
return apiClient().setUserAgent(userAgent);
}
/**
* @return
* @see java.lang.Object#toString()
*/
public String toString() {
return apiClient().toString();
}
/**
* @param key
* @param value
* @return
* @see ApiClient#addDefaultHeader(java.lang.String, java.lang.String)
*/
public ApiClient addDefaultHeader(String key, String value) {
return apiClient().addDefaultHeader(key, value);
}
/**
* @param key
* @param value
* @return
* @see ApiClient#addDefaultCookie(java.lang.String, java.lang.String)
*/
public ApiClient addDefaultCookie(String key, String value) {
return apiClient().addDefaultCookie(key, value);
}
/**
* @return
* @see ApiClient#isDebugging()
*/
public boolean isDebugging() {
return apiClient().isDebugging();
}
/**
* @param debugging
* @return
* @see ApiClient#setDebugging(boolean)
*/
public ApiClient setDebugging(boolean debugging) {
return apiClient().setDebugging(debugging);
}
/**
* @return
* @see ApiClient#getTempFolderPath()
*/
public String getTempFolderPath() {
return apiClient().getTempFolderPath();
}
/**
* @param tempFolderPath
* @return
* @see ApiClient#setTempFolderPath(java.lang.String)
*/
public ApiClient setTempFolderPath(String tempFolderPath) {
return apiClient().setTempFolderPath(tempFolderPath);
}
/**
* @return
* @see ApiClient#getConnectTimeout()
*/
public int getConnectTimeout() {
return apiClient().getConnectTimeout();
}
/**
* @param connectionTimeout
* @return
* @see ApiClient#setConnectTimeout(int)
*/
public ApiClient setConnectTimeout(int connectionTimeout) {
return apiClient().setConnectTimeout(connectionTimeout);
}
/**
* @return
* @see ApiClient#getReadTimeout()
*/
public int getReadTimeout() {
return apiClient().getReadTimeout();
}
/**
* @param readTimeout
* @return
* @see ApiClient#setReadTimeout(int)
*/
public ApiClient setReadTimeout(int readTimeout) {
return apiClient().setReadTimeout(readTimeout);
}
/**
* @return
* @see ApiClient#getWriteTimeout()
*/
public int getWriteTimeout() {
return apiClient().getWriteTimeout();
}
/**
* @param writeTimeout
* @return
* @see ApiClient#setWriteTimeout(int)
*/
public ApiClient setWriteTimeout(int writeTimeout) {
return apiClient().setWriteTimeout(writeTimeout);
}
/**
* @param param
* @return
* @see ApiClient#parameterToString(java.lang.Object)
*/
public String parameterToString(Object param) {
return apiClient().parameterToString(param);
}
/**
* @param name
* @param value
* @return
* @see ApiClient#parameterToPair(java.lang.String, java.lang.Object)
*/
public List<Pair> parameterToPair(String name, Object value) {
return apiClient().parameterToPair(name, value);
}
/**
* @param collectionFormat
* @param name
* @param value
* @return
* @see ApiClient#parameterToPairs(java.lang.String, java.lang.String, java.util.Collection)
*/
@SuppressWarnings({ "rawtypes", "PMD.AvoidDuplicateLiterals" })
public List<Pair> parameterToPairs(String collectionFormat, String name,
Collection value) {
return apiClient().parameterToPairs(collectionFormat, name, value);
}
/**
* @param collectionFormat
* @param value
* @return
* @see ApiClient#collectionPathParameterToString(java.lang.String, java.util.Collection)
*/
@SuppressWarnings("rawtypes")
public String collectionPathParameterToString(String collectionFormat,
Collection value) {
return apiClient().collectionPathParameterToString(collectionFormat,
value);
}
/**
* @param filename
* @return
* @see ApiClient#sanitizeFilename(java.lang.String)
*/
public String sanitizeFilename(String filename) {
return apiClient().sanitizeFilename(filename);
}
/**
* @param mime
* @return
* @see ApiClient#isJsonMime(java.lang.String)
*/
public boolean isJsonMime(String mime) {
return apiClient().isJsonMime(mime);
}
/**
* @param accepts
* @return
* @see ApiClient#selectHeaderAccept(java.lang.String[])
*/
@SuppressWarnings("PMD.UseVarargs")
public String selectHeaderAccept(String[] accepts) {
return apiClient().selectHeaderAccept(accepts);
}
/**
* @param contentTypes
* @return
* @see ApiClient#selectHeaderContentType(java.lang.String[])
*/
@SuppressWarnings("PMD.UseVarargs")
public String selectHeaderContentType(String[] contentTypes) {
return apiClient().selectHeaderContentType(contentTypes);
}
/**
* @param str
* @return
* @see ApiClient#escapeString(java.lang.String)
*/
public String escapeString(String str) {
return apiClient().escapeString(str);
}
/**
* @param <T>
* @param response
* @param returnType
* @return
* @throws ApiException
* @see ApiClient#deserialize(okhttp3.Response, java.lang.reflect.Type)
*/
public <T> T deserialize(Response response, Type returnType)
throws ApiException {
return apiClient().deserialize(response, returnType);
}
/**
* @param obj
* @param contentType
* @return
* @throws ApiException
* @see ApiClient#serialize(java.lang.Object, java.lang.String)
*/
public RequestBody serialize(Object obj, String contentType)
throws ApiException {
return apiClient().serialize(obj, contentType);
}
/**
* @param response
* @return
* @throws ApiException
* @see ApiClient#downloadFileFromResponse(okhttp3.Response)
*/
public File downloadFileFromResponse(Response response)
throws ApiException {
return apiClient().downloadFileFromResponse(response);
}
/**
* @param response
* @return
* @throws IOException
* @see ApiClient#prepareDownloadFile(okhttp3.Response)
*/
public File prepareDownloadFile(Response response) throws IOException {
return apiClient().prepareDownloadFile(response);
}
/**
* @param <T>
* @param call
* @return
* @throws ApiException
* @see ApiClient#execute(okhttp3.Call)
*/
public <T> ApiResponse<T> execute(Call call) throws ApiException {
return apiClient().execute(call);
}
/**
* @param <T>
* @param call
* @param returnType
* @return
* @throws ApiException
* @see ApiClient#execute(okhttp3.Call, java.lang.reflect.Type)
*/
public <T> ApiResponse<T> execute(Call call, Type returnType)
throws ApiException {
return apiClient().execute(call, returnType);
}
/**
* @param <T>
* @param call
* @param callback
* @see ApiClient#executeAsync(okhttp3.Call, io.kubernetes.client.openapi.ApiCallback)
*/
public <T> void executeAsync(Call call, ApiCallback<T> callback) {
apiClient().executeAsync(call, callback);
}
/**
* @param <T>
* @param call
* @param returnType
* @param callback
* @see ApiClient#executeAsync(okhttp3.Call, java.lang.reflect.Type, io.kubernetes.client.openapi.ApiCallback)
*/
public <T> void executeAsync(Call call, Type returnType,
ApiCallback<T> callback) {
apiClient().executeAsync(call, returnType, callback);
}
/**
* @param <T>
* @param response
* @param returnType
* @return
* @throws ApiException
* @see ApiClient#handleResponse(okhttp3.Response, java.lang.reflect.Type)
*/
public <T> T handleResponse(Response response, Type returnType)
throws ApiException {
return apiClient().handleResponse(response, returnType);
}
/**
* @param path
* @param method
* @param queryParams
* @param collectionQueryParams
* @param body
* @param headerParams
* @param cookieParams
* @param formParams
* @param authNames
* @param callback
* @return
* @throws ApiException
* @see ApiClient#buildCall(java.lang.String, java.lang.String, java.util.List, java.util.List, java.lang.Object, java.util.Map, java.util.Map, java.util.Map, java.lang.String[], io.kubernetes.client.openapi.ApiCallback)
*/
@SuppressWarnings({ "rawtypes", "PMD.ExcessiveParameterList" })
public Call buildCall(String path, String method, List<Pair> queryParams,
List<Pair> collectionQueryParams, Object body,
Map<String, String> headerParams, Map<String, String> cookieParams,
Map<String, Object> formParams, String[] authNames,
ApiCallback callback) throws ApiException {
return apiClient().buildCall(path, method, queryParams,
collectionQueryParams, body, headerParams, cookieParams, formParams,
authNames, callback);
}
/**
* @param path
* @param method
* @param queryParams
* @param collectionQueryParams
* @param body
* @param headerParams
* @param cookieParams
* @param formParams
* @param authNames
* @param callback
* @return
* @throws ApiException
* @see ApiClient#buildRequest(java.lang.String, java.lang.String, java.util.List, java.util.List, java.lang.Object, java.util.Map, java.util.Map, java.util.Map, java.lang.String[], io.kubernetes.client.openapi.ApiCallback)
*/
@SuppressWarnings({ "rawtypes", "PMD.ExcessiveParameterList" })
public Request buildRequest(String path, String method,
List<Pair> queryParams, List<Pair> collectionQueryParams,
Object body, Map<String, String> headerParams,
Map<String, String> cookieParams, Map<String, Object> formParams,
String[] authNames, ApiCallback callback) throws ApiException {
return apiClient().buildRequest(path, method, queryParams,
collectionQueryParams, body, headerParams, cookieParams, formParams,
authNames, callback);
}
/**
* @param path
* @param queryParams
* @param collectionQueryParams
* @return
* @see ApiClient#buildUrl(java.lang.String, java.util.List, java.util.List)
*/
public String buildUrl(String path, List<Pair> queryParams,
List<Pair> collectionQueryParams) {
return apiClient().buildUrl(path, queryParams, collectionQueryParams);
}
/**
* @param headerParams
* @param reqBuilder
* @see ApiClient#processHeaderParams(java.util.Map, okhttp3.Request.Builder)
*/
public void processHeaderParams(Map<String, String> headerParams,
Builder reqBuilder) {
apiClient().processHeaderParams(headerParams, reqBuilder);
}
/**
* @param cookieParams
* @param reqBuilder
* @see ApiClient#processCookieParams(java.util.Map, okhttp3.Request.Builder)
*/
public void processCookieParams(Map<String, String> cookieParams,
Builder reqBuilder) {
apiClient().processCookieParams(cookieParams, reqBuilder);
}
/**
* @param authNames
* @param queryParams
* @param headerParams
* @param cookieParams
* @see ApiClient#updateParamsForAuth(java.lang.String[], java.util.List, java.util.Map, java.util.Map)
*/
public void updateParamsForAuth(String[] authNames, List<Pair> queryParams,
Map<String, String> headerParams,
Map<String, String> cookieParams) {
apiClient().updateParamsForAuth(authNames, queryParams, headerParams,
cookieParams);
}
/**
* @param formParams
* @return
* @see ApiClient#buildRequestBodyFormEncoding(java.util.Map)
*/
public RequestBody
buildRequestBodyFormEncoding(Map<String, Object> formParams) {
return apiClient().buildRequestBodyFormEncoding(formParams);
}
/**
* @param formParams
* @return
* @see ApiClient#buildRequestBodyMultipart(java.util.Map)
*/
public RequestBody
buildRequestBodyMultipart(Map<String, Object> formParams) {
return apiClient().buildRequestBodyMultipart(formParams);
}
/**
* @param file
* @return
* @see ApiClient#guessContentTypeFromFile(java.io.File)
*/
public String guessContentTypeFromFile(File file) {
return apiClient().guessContentTypeFromFile(file);
}
}

View file

@ -0,0 +1,114 @@
/*
* 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 io.kubernetes.client.common.KubernetesObject;
import io.kubernetes.client.openapi.models.V1ObjectMeta;
/**
* Represents a Kubernetes object 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 KubernetesObject}.
*/
@SuppressWarnings("PMD.DataClass")
public class K8sDynamicModel implements KubernetesObject {
private final V1ObjectMeta metadata;
private final JsonObject data;
/**
* Instantiates a new model from the JSON representation.
*
* @param delegate the gson instance to use for extracting structured data
* @param json the JSON
*/
public K8sDynamicModel(Gson delegate, JsonObject json) {
this.data = json;
metadata = delegate.fromJson(data.get("metadata"), V1ObjectMeta.class);
}
@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 V1ObjectMeta getMetadata() {
return metadata;
}
/**
* Gets the metadata. (Abbreviated method name for convenience.)
*
* @return the metadata
*/
public V1ObjectMeta metadata() {
return metadata;
}
/**
* Gets the data.
*
* @return the data
*/
public JsonObject data() {
return data;
}
/**
* Convenience method for getting the status.
*
* @return the JSON object describing the status
*/
public JsonObject status() {
return data.getAsJsonObject("status");
}
@Override
public String toString() {
return data.toString();
}
}

View file

@ -0,0 +1,130 @@
/*
* 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")
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

@ -0,0 +1,163 @@
/*
* 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.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 K8sDynamicModels implements KubernetesListObject {
private final JsonObject data;
private final V1ListMeta metadata;
private final List<K8sDynamicModel> items;
/**
* 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 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()));
}
}
@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,109 @@
/*
* 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 java.io.Reader;
/**
* 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 K8sDynamicStub
extends K8sGenericStub<K8sDynamicModel, K8sDynamicModels> {
/**
* 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,
APIResource context, String namespace, String name) {
super(objectClass, objectListClass, 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 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);
}
/**
* 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 K8sDynamicStub get(K8sClient client,
APIResource context, String namespace, String name)
throws ApiException {
return K8sGenericStub.get(K8sDynamicModel.class, K8sDynamicModels.class,
client, context, namespace, name, K8sDynamicStub::new);
}
/**
* 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 K8sDynamicStub createFromYaml(K8sClient client,
APIResource context, Reader yaml) throws ApiException {
var model = new K8sDynamicModel(client.getJSON().getGson(),
K8s.yamlToJson(client, yaml));
return K8sGenericStub.create(K8sDynamicModel.class,
K8sDynamicModels.class, client, context, model,
K8sDynamicStub::new);
}
}

View file

@ -0,0 +1,418 @@
/*
* 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 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.ListOptions;
import io.kubernetes.client.util.generic.options.PatchOptions;
import java.net.HttpURLConnection;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Optional;
import java.util.function.Function;
/**
* A stub for namespaced custom objects. This stub provides the
* functions common to all Kubernetes objects, but uses variables
* for all types. This class should be used as base class only.
*
* @param <O> the generic type
* @param <L> the generic type
*/
@SuppressWarnings("PMD.DataflowAnomalyAnalysis")
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 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.
*
* @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
*/
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());
}
api = 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 group.
*
* @return the group
*/
public String group() {
return group;
}
/**
* Gets the version.
*
* @return the version
*/
public String version() {
return version;
}
/**
* Gets the kind.
*
* @return the kind
*/
public String kind() {
return kind;
}
/**
* Gets the plural.
*
* @return the plural
*/
public String plural() {
return plural;
}
/**
* Gets the namespace.
*
* @return the namespace
*/
public String namespace() {
return namespace;
}
/**
* Gets the name.
*
* @return the name
*/
public String name() {
return name;
}
/**
* Delete the Kubernetes object.
*
* @throws ApiException the API exception
*/
public void delete() throws ApiException {
var result = api.delete(namespace, name);
if (result.isSuccess()
|| result.getHttpStatusCode() == HttpURLConnection.HTTP_NOT_FOUND) {
return;
}
result.throwsApiException();
}
/**
* Retrieves and returns the current state of the object.
*
* @return the object's state
* @throws ApiException the api exception
*/
public Optional<O> model() throws ApiException {
return K8s.optional(api.get(namespace, name));
}
/**
* Updates the object's status.
*
* @param object the current state of the object (passed to `status`)
* @param status function that returns the new status
* @return the updated model or empty if not successful
* @throws ApiException the api exception
*/
public Optional<O> updateStatus(O object,
Function<O, Object> status) throws ApiException {
return K8s.optional(api.updateStatus(object, status));
}
/**
* Updates the status.
*
* @param status the status
* @return the kubernetes api response
* the updated model or empty if not successful
* @throws ApiException the api exception
*/
public Optional<O> updateStatus(Function<O, Object> status)
throws ApiException {
return updateStatus(
api.get(namespace, name).throwsApiException().getObject(), status);
}
/**
* Patch the object.
*
* @param patchType the patch type
* @param patch the patch
* @param options the options
* @return the kubernetes api response
* @throws ApiException the api exception
*/
public Optional<O> patch(String patchType, V1Patch patch,
PatchOptions options) throws ApiException {
return K8s
.optional(api.patch(namespace, name, patchType, patch, options));
}
/**
* Patch the object using default options.
*
* @param patchType the patch type
* @param patch the patch
* @return the kubernetes api response
* @throws ApiException the api exception
*/
public Optional<O>
patch(String patchType, V1Patch patch) throws ApiException {
PatchOptions opts = new PatchOptions();
return patch(patchType, patch, opts);
}
/**
* A supplier for generic stubs.
*
* @param <O> the object type
* @param <L> the object list type
* @param <R> the result type
*/
public interface GenericSupplier<O extends KubernetesObject,
L extends KubernetesListObject, R extends K8sGenericStub<O, L>> {
/**
* 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);
}
/**
* 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;
}
}

View file

@ -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.V1ConfigMap;
import io.kubernetes.client.openapi.models.V1ConfigMapList;
import java.util.List;
/**
* A stub for config maps (v1).
*/
@SuppressWarnings("PMD.DataflowAnomalyAnalysis")
public class K8sV1ConfigMapStub
extends K8sGenericStub<V1ConfigMap, V1ConfigMapList> {
/**
* Instantiates a new stub.
*
* @param client the client
* @param namespace the namespace
* @param name the name
*/
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);
}
/**
* 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 K8sV1ConfigMapStub get(K8sClient client, String namespace,
String name) {
return new K8sV1ConfigMapStub(client, namespace, name);
}
}

View file

@ -0,0 +1,77 @@
/*
* 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.custom.V1Patch;
import io.kubernetes.client.openapi.ApiException;
import io.kubernetes.client.openapi.models.V1Deployment;
import io.kubernetes.client.openapi.models.V1DeploymentList;
import java.util.List;
import java.util.Optional;
/**
* A stub for pods (v1).
*/
@SuppressWarnings("PMD.DataflowAnomalyAnalysis")
public class K8sV1DeploymentStub
extends K8sGenericStub<V1Deployment, V1DeploymentList> {
/**
* Instantiates a new stub.
*
* @param client the client
* @param namespace the namespace
* @param name the name
*/
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);
}
/**
* Scales the deployment.
*
* @param replicas the replicas
* @return the new model or empty if not successful
* @throws ApiException the API exception
*/
public Optional<V1Deployment> scale(int replicas) throws ApiException {
return patch(V1Patch.PATCH_FORMAT_JSON_PATCH,
new V1Patch("[{\"op\": \"replace\", \"path\": \"/spec/replicas"
+ "\", \"value\": " + replicas + "}]"),
client.defaultPatchOptions());
}
}

View file

@ -0,0 +1,78 @@
/*
* 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.ApiException;
import io.kubernetes.client.openapi.models.V1Pod;
import io.kubernetes.client.openapi.models.V1PodList;
import io.kubernetes.client.util.generic.options.ListOptions;
import java.util.Collection;
import java.util.List;
/**
* A stub for pods (v1).
*/
@SuppressWarnings("PMD.DataflowAnomalyAnalysis")
public class K8sV1PodStub extends K8sGenericStub<V1Pod, V1PodList> {
public static final APIResource CONTEXT
= new APIResource("", List.of("v1"), "v1", "Pod", true, "pods", "pod");
/**
* Instantiates a new stub.
*
* @param client the client
* @param namespace the namespace
* @param name the name
*/
protected K8sV1PodStub(K8sClient client, String namespace, String name) {
super(V1Pod.class, V1PodList.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 kpod stub
*/
public static K8sV1PodStub get(K8sClient client, String namespace,
String name) {
return new K8sV1PodStub(client, namespace, name);
}
/**
* 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<K8sV1PodStub> list(K8sClient client,
String namespace, ListOptions options) throws ApiException {
return K8sGenericStub.list(V1Pod.class, V1PodList.class, client,
CONTEXT, namespace, options, K8sV1PodStub::new);
}
}

View file

@ -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.V1StatefulSet;
import io.kubernetes.client.openapi.models.V1StatefulSetList;
import java.util.List;
/**
* A stub for stateful sets (v1).
*/
@SuppressWarnings("PMD.DataflowAnomalyAnalysis")
public class K8sV1StatefulSetStub
extends K8sGenericStub<V1StatefulSet, V1StatefulSetList> {
/**
* Instantiates a new stub.
*
* @param client the client
* @param namespace the namespace
* @param name the name
*/
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"),
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 stateful set stub
*/
public static K8sV1StatefulSetStub get(K8sClient client, String namespace,
String name) {
return new K8sV1StatefulSetStub(client, namespace, name);
}
}