parent
6491742eb0
commit
ae3941707a
86 changed files with 12225 additions and 514 deletions
10
org.jdrupes.vmoperator.common/.checkstyle
Normal file
10
org.jdrupes.vmoperator.common/.checkstyle
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<fileset-config file-format-version="1.2.0" simple-config="false" sync-formatter="false">
|
||||
<local-check-config name="Project Checks" location="/VM-Operator/checkstyle.xml" type="project" description="">
|
||||
<additional-data name="protect-config-file" value="false"/>
|
||||
</local-check-config>
|
||||
<fileset name="all" enabled="true" check-config-name="Project Checks" local="true">
|
||||
<file-match-pattern match-pattern="." include-pattern="true"/>
|
||||
</fileset>
|
||||
</fileset-config>
|
||||
7
org.jdrupes.vmoperator.common/.eclipse-pmd
Normal file
7
org.jdrupes.vmoperator.common/.eclipse-pmd
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<eclipse-pmd xmlns="http://acanda.ch/eclipse-pmd/0.8" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://acanda.ch/eclipse-pmd/0.8 http://acanda.ch/eclipse-pmd/eclipse-pmd-0.8.xsd">
|
||||
<analysis enabled="true" />
|
||||
<rulesets>
|
||||
<ruleset name="Custom Rules" ref="moodle-tools-console/ruleset.xml" refcontext="workspace" />
|
||||
</rulesets>
|
||||
</eclipse-pmd>
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
arguments=
|
||||
auto.sync=false
|
||||
build.scans.enabled=false
|
||||
connection.gradle.distribution=GRADLE_DISTRIBUTION(WRAPPER)
|
||||
connection.project.dir=..
|
||||
eclipse.preferences.version=1
|
||||
gradle.user.home=
|
||||
java.home=
|
||||
jvm.arguments=
|
||||
offline.mode=false
|
||||
override.workspace.settings=false
|
||||
show.console.view=false
|
||||
show.executions.view=false
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
eclipse.preferences.version=1
|
||||
encoding/<project>=UTF-8
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
eclipse.preferences.version=1
|
||||
line.separator=\n
|
||||
14
org.jdrupes.vmoperator.common/build.gradle
Normal file
14
org.jdrupes.vmoperator.common/build.gradle
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
/*
|
||||
* This file was generated by the Gradle 'init' task.
|
||||
*
|
||||
* This project uses @Incubating APIs which are subject to change.
|
||||
*/
|
||||
|
||||
plugins {
|
||||
id 'org.jdrupes.vmoperator.java-library-conventions'
|
||||
}
|
||||
|
||||
dependencies {
|
||||
api project(':org.jdrupes.vmoperator.util')
|
||||
api 'io.kubernetes:client-java:[18.0.0,19)'
|
||||
}
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
/*
|
||||
* VM-Operator
|
||||
* Copyright (C) 2023 Michael N. Lipp
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.jdrupes.vmoperator.common;
|
||||
|
||||
/**
|
||||
* Some constants.
|
||||
*/
|
||||
public class Constants {
|
||||
|
||||
/** The Constant VM_OP_NAME. */
|
||||
public static final String VM_OP_NAME = "vm-operator";
|
||||
|
||||
/** The Constant VM_OP_GROUP. */
|
||||
public static final String VM_OP_GROUP = "vmoperator.jdrupes.org";
|
||||
|
||||
/** The Constant VM_OP_KIND_VM. */
|
||||
public static final String VM_OP_KIND_VM = "VirtualMachine";
|
||||
}
|
||||
|
|
@ -0,0 +1,116 @@
|
|||
/*
|
||||
* VM-Operator
|
||||
* Copyright (C) 2023 Michael N. Lipp
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.jdrupes.vmoperator.common;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.BigInteger;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* Provides methods for parsing "official" memory sizes..
|
||||
*/
|
||||
@SuppressWarnings("PMD.UseUtilityClass")
|
||||
public class Convertions {
|
||||
|
||||
@SuppressWarnings({ "PMD.UseConcurrentHashMap",
|
||||
"PMD.FieldNamingConventions", "PMD.VariableNamingConventions" })
|
||||
private static final Map<String, BigInteger> unitMap = new HashMap<>();
|
||||
@SuppressWarnings({ "PMD.FieldNamingConventions",
|
||||
"PMD.VariableNamingConventions" })
|
||||
private static final List<Map.Entry<String, BigInteger>> unitMappings;
|
||||
@SuppressWarnings({ "PMD.FieldNamingConventions",
|
||||
"PMD.VariableNamingConventions" })
|
||||
private static final Pattern memorySize
|
||||
= Pattern.compile("^\\s*(\\d+(\\.\\d+)?)\\s*([A-Za-z]*)\\s*");
|
||||
|
||||
static {
|
||||
// SI units and common abbreviations
|
||||
BigInteger factor = BigInteger.ONE;
|
||||
unitMap.put("", factor);
|
||||
BigInteger scale = BigInteger.valueOf(1000);
|
||||
for (var unit : List.of("B", "kB", "MB", "GB", "TB", "PB", "EB")) {
|
||||
unitMap.put(unit, factor);
|
||||
factor = factor.multiply(scale);
|
||||
}
|
||||
// Binary units
|
||||
factor = BigInteger.valueOf(1024);
|
||||
scale = BigInteger.valueOf(1024);
|
||||
for (var unit : List.of("KiB", "MiB", "GiB", "TiB", "PiB", "EiB")) {
|
||||
unitMap.put(unit, factor);
|
||||
factor = factor.multiply(scale);
|
||||
}
|
||||
unitMappings = unitMap.entrySet().stream()
|
||||
.sorted((a, b) -> -1 * a.getValue().compareTo(b.getValue()))
|
||||
.toList();
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a memory size specification.
|
||||
*
|
||||
* @param amount the amount
|
||||
* @return the big integer
|
||||
*/
|
||||
@SuppressWarnings("PMD.DataflowAnomalyAnalysis")
|
||||
public static BigInteger parseMemory(Object amount) {
|
||||
if (amount == null) {
|
||||
return (BigInteger) amount;
|
||||
}
|
||||
if (amount instanceof BigInteger number) {
|
||||
return number;
|
||||
}
|
||||
if (amount instanceof Number number) {
|
||||
return BigInteger.valueOf(number.longValue());
|
||||
}
|
||||
var matcher = memorySize.matcher(amount.toString());
|
||||
if (!matcher.matches()) {
|
||||
throw new NumberFormatException(amount.toString());
|
||||
}
|
||||
var unit = BigInteger.ONE;
|
||||
if (matcher.group(3) != null) {
|
||||
unit = unitMap.get(matcher.group(3));
|
||||
if (unit == null) {
|
||||
throw new NumberFormatException("Illegal unit \""
|
||||
+ matcher.group(3) + "\" in \"" + amount.toString() + "\"");
|
||||
}
|
||||
}
|
||||
var number = matcher.group(1);
|
||||
return new BigDecimal(number).multiply(new BigDecimal(unit))
|
||||
.toBigInteger();
|
||||
}
|
||||
|
||||
/**
|
||||
* Format memory size for humans.
|
||||
*
|
||||
* @param size the size
|
||||
* @return the string
|
||||
*/
|
||||
public static String formatMemory(BigInteger size) {
|
||||
for (var mapping : unitMappings) {
|
||||
if (size.compareTo(mapping.getValue()) >= 0
|
||||
&& size.mod(mapping.getValue()).equals(BigInteger.ZERO)) {
|
||||
return (size.divide(mapping.getValue()).toString()
|
||||
+ " " + mapping.getKey()).trim();
|
||||
}
|
||||
}
|
||||
return size.toString();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,207 @@
|
|||
/*
|
||||
* VM-Operator
|
||||
* Copyright (C) 2023 Michael N. Lipp
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.jdrupes.vmoperator.common;
|
||||
|
||||
import io.kubernetes.client.common.KubernetesListObject;
|
||||
import io.kubernetes.client.common.KubernetesObject;
|
||||
import io.kubernetes.client.custom.V1Patch;
|
||||
import io.kubernetes.client.openapi.ApiClient;
|
||||
import io.kubernetes.client.openapi.ApiException;
|
||||
import io.kubernetes.client.openapi.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.models.V1ObjectMeta;
|
||||
import io.kubernetes.client.openapi.models.V1PersistentVolumeClaim;
|
||||
import io.kubernetes.client.openapi.models.V1PersistentVolumeClaimList;
|
||||
import io.kubernetes.client.openapi.models.V1Pod;
|
||||
import io.kubernetes.client.openapi.models.V1PodList;
|
||||
import io.kubernetes.client.util.generic.GenericKubernetesApi;
|
||||
import io.kubernetes.client.util.generic.dynamic.DynamicKubernetesApi;
|
||||
import io.kubernetes.client.util.generic.options.DeleteOptions;
|
||||
import io.kubernetes.client.util.generic.options.PatchOptions;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* Helpers for K8s API.
|
||||
*/
|
||||
@SuppressWarnings({ "PMD.ShortClassName", "PMD.UseUtilityClass",
|
||||
"PMD.DataflowAnomalyAnalysis" })
|
||||
public class K8s {
|
||||
|
||||
/**
|
||||
* Given a groupVersion, returns only the version.
|
||||
*
|
||||
* @param groupVersion the group version
|
||||
* @return the string
|
||||
*/
|
||||
public static String version(String groupVersion) {
|
||||
return groupVersion.substring(groupVersion.lastIndexOf('/') + 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get PVC API.
|
||||
*
|
||||
* @param client the client
|
||||
* @return the generic kubernetes api
|
||||
*/
|
||||
public static GenericKubernetesApi<V1PersistentVolumeClaim,
|
||||
V1PersistentVolumeClaimList> pvcApi(ApiClient client) {
|
||||
return new GenericKubernetesApi<>(V1PersistentVolumeClaim.class,
|
||||
V1PersistentVolumeClaimList.class, "", "v1",
|
||||
"persistentvolumeclaims", client);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get config map API.
|
||||
*
|
||||
* @param client the client
|
||||
* @return the generic kubernetes api
|
||||
*/
|
||||
public static GenericKubernetesApi<V1ConfigMap,
|
||||
V1ConfigMapList> cmApi(ApiClient client) {
|
||||
return new GenericKubernetesApi<>(V1ConfigMap.class,
|
||||
V1ConfigMapList.class, "", "v1", "configmaps", client);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get pod API.
|
||||
*
|
||||
* @param client the client
|
||||
* @return the generic kubernetes api
|
||||
*/
|
||||
public static GenericKubernetesApi<V1Pod, V1PodList>
|
||||
podApi(ApiClient client) {
|
||||
return new GenericKubernetesApi<>(V1Pod.class, V1PodList.class, "",
|
||||
"v1", "pods", client);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the API for a custom resource.
|
||||
*
|
||||
* @param client the client
|
||||
* @param group the group
|
||||
* @param kind the kind
|
||||
* @param namespace the namespace
|
||||
* @param name the name
|
||||
* @return the dynamic kubernetes api
|
||||
* @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);
|
||||
}
|
||||
}
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an object from its metadata.
|
||||
*
|
||||
* @param <T> the generic type
|
||||
* @param <LT> the generic type
|
||||
* @param api the api
|
||||
* @param meta the meta
|
||||
* @return the object
|
||||
*/
|
||||
public static <T extends KubernetesObject, LT extends KubernetesListObject>
|
||||
Optional<T>
|
||||
get(GenericKubernetesApi<T, LT> api, V1ObjectMeta meta) {
|
||||
var response = api.get(meta.getNamespace(), meta.getName());
|
||||
if (response.isSuccess()) {
|
||||
return Optional.of(response.getObject());
|
||||
}
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete an object.
|
||||
*
|
||||
* @param <T> the generic type
|
||||
* @param <LT> the generic type
|
||||
* @param api the api
|
||||
* @param object the object
|
||||
*/
|
||||
public static <T extends KubernetesObject, LT extends KubernetesListObject>
|
||||
void delete(GenericKubernetesApi<T, LT> api, T object)
|
||||
throws ApiException {
|
||||
api.delete(object.getMetadata().getNamespace(),
|
||||
object.getMetadata().getName()).throwsApiException();
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete an object.
|
||||
*
|
||||
* @param <T> the generic type
|
||||
* @param <LT> the generic type
|
||||
* @param api the api
|
||||
* @param object the object
|
||||
*/
|
||||
public static <T extends KubernetesObject, LT extends KubernetesListObject>
|
||||
void delete(GenericKubernetesApi<T, LT> api, T object,
|
||||
DeleteOptions options) throws ApiException {
|
||||
api.delete(object.getMetadata().getNamespace(),
|
||||
object.getMetadata().getName(), options).throwsApiException();
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply the given patch data.
|
||||
*
|
||||
* @param <T> the generic type
|
||||
* @param <LT> the generic type
|
||||
* @param api the api
|
||||
* @param existing the existing
|
||||
* @param update the update
|
||||
* @throws ApiException the api exception
|
||||
*/
|
||||
public static <T extends KubernetesObject, LT extends KubernetesListObject>
|
||||
T apply(GenericKubernetesApi<T, LT> api, T existing, String update)
|
||||
throws ApiException {
|
||||
PatchOptions opts = new PatchOptions();
|
||||
opts.setForce(true);
|
||||
opts.setFieldManager("kubernetes-java-kubectl-apply");
|
||||
var response = api.patch(existing.getMetadata().getNamespace(),
|
||||
existing.getMetadata().getName(), V1Patch.PATCH_FORMAT_APPLY_YAML,
|
||||
new V1Patch(update), opts).throwsApiException();
|
||||
return response.getObject();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
/*
|
||||
* VM-Operator
|
||||
* Copyright (C) 2023 Michael N. Lipp
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Classes and methods shared among the VM operator modules.
|
||||
*/
|
||||
package org.jdrupes.vmoperator.common;
|
||||
Loading…
Add table
Add a link
Reference in a new issue