Compare commits

...
Sign in to create a new pull request.

25 commits

Author SHA1 Message Date
c6c6358426 Fix warnings.
Some checks failed
ci/woodpecker/push/build Pipeline was successful
CodeQL / Analyze (push) Has been cancelled
Java CI with Gradle / build (push) Has been cancelled
Deploy Jekyll site to Pages / build (push) Has been cancelled
Deploy Jekyll site to Pages / deploy (push) Has been cancelled
2025-08-11 20:32:08 +02:00
470c266157 Build with woodpecker (#1)
Some checks are pending
CodeQL / Analyze (push) Waiting to run
Java CI with Gradle / build (push) Waiting to run
Deploy Jekyll site to Pages / build (push) Waiting to run
Deploy Jekyll site to Pages / deploy (push) Blocked by required conditions
ci/woodpecker/push/build Pipeline was successful
Reviewed-on: #1
Co-authored-by: Michael N. Lipp <mnl@mnl.de>
Co-committed-by: Michael N. Lipp <mnl@mnl.de>
2025-08-11 12:50:28 +02:00
7b8df80828 Use display manager for login.
Some checks are pending
CodeQL / Analyze (push) Waiting to run
Java CI with Gradle / build (push) Waiting to run
Deploy Jekyll site to Pages / build (push) Waiting to run
Deploy Jekyll site to Pages / deploy (push) Blocked by required conditions
2025-08-01 17:41:03 +02:00
fccf2a6b65 Fix branch evaluation.
Some checks failed
Java CI with Gradle / build (push) Has been cancelled
2025-07-26 22:43:45 +02:00
00e9affee4 Fix evaluation of template source. 2025-07-26 15:18:22 +02:00
fa84110e1d Handle configuration value properly. 2025-07-14 17:34:09 +02:00
76b579c404 Add key, allowing vue to optimize.
Some checks failed
Java CI with Gradle / build (push) Has been cancelled
2025-05-04 11:36:55 +02:00
a5433c869b Upgrade webconsole base library. 2025-05-03 22:29:42 +02:00
10f3028f06 Increase concurrency and avoid race condition. 2025-04-30 16:27:15 +02:00
b7fad4614d Improve debug messages.
Some checks failed
Java CI with Gradle / build (push) Has been cancelled
2025-04-29 14:02:12 +02:00
7d298ce24b Clarify intend. 2025-04-14 21:39:35 +02:00
6ef4c2aaa2 Fix return value. 2025-04-14 12:08:48 +02:00
5bcf0ba051 Add umami to javadoc. 2025-04-13 17:01:38 +02:00
d67f374de7 Try umami. 2025-04-13 16:48:42 +02:00
2b3420c801 Update component picture.
Some checks failed
Java CI with Gradle / build (push) Has been cancelled
2025-03-31 12:21:47 +02:00
bd54d293eb Update picture. 2025-03-30 21:58:22 +02:00
cb2ae7c33e Update. 2025-03-30 21:32:55 +02:00
85a9b41046 Update picture. 2025-03-30 21:12:18 +02:00
fb976802cf Minor edit. 2025-03-30 13:05:33 +02:00
af112bb66b Editorial changes. 2025-03-30 12:37:49 +02:00
592b30f6c5 Update state diagram. 2025-03-30 12:17:14 +02:00
c716e32534 Make tests work again. 2025-03-30 11:42:03 +02:00
c79d678a2a More consistent logging. 2025-03-29 18:38:09 +01:00
f30ea79abb Minor editorial changes. 2025-03-29 17:41:01 +01:00
d7d5c860a2 Merge branch 'wip/optimize' 2025-03-29 15:10:32 +01:00
101 changed files with 446 additions and 431 deletions

View file

@ -1,78 +0,0 @@
stages:
- build
- test
- publish
- deploy
.any-job:
rules:
- if: $CI_SERVER_HOST == "gitlab.mnl.de"
.gradle-job:
extends: .any-job
image: registry.mnl.de/org/jgrapes/jdk21-builder:v2
cache:
- key: dependencies-${CI_COMMIT_BRANCH}
policy: pull-push
paths:
- .gradle
- node_modules
- key: "$CI_COMMIT_SHA"
policy: pull-push
paths:
- build
- "*/build"
before_script:
- echo -n $CI_REGISTRY_PASSWORD | podman login -u "$CI_REGISTRY_USER" --password-stdin $CI_REGISTRY
- git switch $(git branch -r --sort="authordate" --contains $CI_COMMIT_SHA | head -1 | sed -e 's#[^/]*/##')
- git pull
- git reset --hard $CI_COMMIT_SHA
build-jars:
stage: build
extends: .gradle-job
script:
- ./gradlew -Pdocker.registry=$CI_REGISTRY_IMAGE build apidocs
publish-images:
stage: publish
extends: .gradle-job
dependencies:
- build-jars
script:
- ./gradlew -Pdocker.registry=$CI_REGISTRY_IMAGE publishImage
.pages-job:
extends: .any-job
image: ${CI_DEPENDENCY_PROXY_GROUP_IMAGE_PREFIX}/ruby:3.2
variables:
JEKYLL_ENV: production
LC_ALL: C.UTF-8
before_script:
- git fetch origin gh-pages
- git checkout gh-pages
- gem install bundler
- bundle install
test-pages:
stage: test
extends: .pages-job
rules:
- if: $CI_COMMIT_BRANCH == "gh-pages"
script:
- bundle exec jekyll build -d test
artifacts:
paths:
- test
#publish-pages:
# stage: publish
# extends: .pages-job
# rules:
# - if: $CI_COMMIT_BRANCH == "gh-pages"
# script:
# - bundle exec jekyll build -d public
# artifacts:
# paths:
# - public
# environment: production

38
.woodpecker/build.yaml Normal file
View file

@ -0,0 +1,38 @@
when:
- event: push
evaluate: 'CI_SYSTEM_HOST == "woodpecker.mnl.de"'
clone:
- name: git
image: woodpeckerci/plugin-git
settings:
partial: false
tags: true
depth: 0
steps:
- name: prepare
image: alpine
commands:
# Because we run the next step as user 1000 to make podman work:
- mkdir /woodpecker/workflow
- chown 1000:1000 /woodpecker/workflow
- chown -R 1000:1000 $CI_WORKSPACE
- name: build-jars
image: registry.mnl.de/mnl/jdk21-builder:v4
environment:
HOME: /woodpecker/workflow
REGISTRY: registry.mnl.de
REGISTRY_USER: mnl
REGISTRY_TOKEN:
from_secret: REGISTRY_TOKEN
commands:
- echo $REGISTRY_TOKEN | podman login -u $REGISTRY_USER --password-stdin $REGISTRY
- ./gradlew -Pdocker.registry=$REGISTRY/$REGISTRY_USER build apidocs publishImage
backend_options:
kubernetes:
securityContext:
privileged: true
runAsUser: 1000
runAsGroup: 1000

View file

@ -3,9 +3,9 @@
The CRD must be deployed independently. Apart from that, the The CRD must be deployed independently. Apart from that, the
`kustomize.yaml` `kustomize.yaml`
* creates a small cdrom image repository and * creates a small cdrom image repository and
* deploys the operator in namespace `vmop-dev` with a replica of 0. * deploys the operator in namespace `vmop-dev` with a replica of 0.
This allows you to run the manager in your IDE. This allows you to run the manager in your IDE.

View file

@ -0,0 +1,3 @@
#!/bin/sh
sed -i '/AutomaticLogin/d' /etc/gdm/custom.conf

View file

@ -72,8 +72,8 @@ doLogin() {
return return
fi fi
# Check if this user is already logged in on tty1 # Check if this user is already logged in on tty2
curUser=$(loginctl -j | jq -r '.[] | select(.tty=="tty1") | .user') curUser=$(loginctl -j | jq -r '.[] | select(.tty=="tty2") | .user')
if [ "$curUser" = "$user" ]; then if [ "$curUser" = "$user" ]; then
echo >&${con} "201 User already logged in" echo >&${con} "201 User already logged in"
return return
@ -96,17 +96,14 @@ doLogin() {
return return
fi fi
fi fi
# Start the desktop for the user # Configure user as auto login user
systemd-run 2>$temperr \ sed -i '/AutomaticLogin/d' /etc/gdm/custom.conf
--unit vmop-user-desktop --uid=$uid --gid=$uid \ sed -i '/\[daemon\]/a AutomaticLoginEnable=true\nAutomaticLogin='$user \
--working-directory="/home/$user" -p TTYPath=/dev/tty1 \ /etc/gdm/custom.conf
-p PAMName=login -p StandardInput=tty -p StandardOutput=journal \
-p Conflicts="gdm.service getty@tty1.service" \ # Activate user
-E XDG_RUNTIME_DIR="/run/user/$uid" \ systemctl restart gdm
-E XDG_CURRENT_DESKTOP=GNOME \
-p ExecStartPre="/usr/bin/chvt 1" \
dbus-run-session -- gnome-shell --display-server --wayland
if [ $? -eq 0 ]; then if [ $? -eq 0 ]; then
echo >&${con} "201 User logged in successfully" echo >&${con} "201 User logged in successfully"
else else
@ -117,14 +114,8 @@ doLogin() {
# Attempt to log out a user currently using tty1. This is an intermediate # Attempt to log out a user currently using tty1. This is an intermediate
# operation that can be invoked from other operations # operation that can be invoked from other operations
attemptLogout() { attemptLogout() {
systemctl status vmop-user-desktop > /dev/null 2>&1 sed -i '/AutomaticLogin/d' /etc/gdm/custom.conf
if [ $? = 0 ]; then systemctl stop gdm
systemctl stop vmop-user-desktop
fi
loginctl -j | jq -r '.[] | select(.tty=="tty1") | .session' \
| while read sid; do
loginctl kill-session $sid
done
echo >&${con} "102 Desktop stopped" echo >&${con} "102 Desktop stopped"
} }
@ -133,15 +124,7 @@ attemptLogout() {
# Also try to restart gdm, if it is not running. # Also try to restart gdm, if it is not running.
doLogout() { doLogout() {
attemptLogout attemptLogout
systemctl status gdm >/dev/null 2>&1 systemctl restart gdm
if [ $? != 0 ]; then
systemctl restart gdm 2>$temperr
if [ $? -eq 0 ]; then
echo >&${con} "102 gdm restarted"
else
echo >&${con} "102 Restarting gdm failed: $(tr '\n' ' ' <${temperr})"
fi
fi
echo >&${con} "202 User logged out" echo >&${con} "202 User logged out"
} }
@ -153,7 +136,7 @@ while read line <&${con}; do
done done
onExit() { onExit() {
attemptLogout doLogout
if [ -n "$temperr" ]; then if [ -n "$temperr" ]; then
rm -f $temperr rm -f $temperr
fi fi

View file

@ -32,4 +32,5 @@
<noscript><p><img referrerpolicy="no-referrer-when-downgrade" <noscript><p><img referrerpolicy="no-referrer-when-downgrade"
src="//piwik.mnl.de/matomo.php?idsite=17&amp;rec=1&amp;action_name=VM-Operator" style="border:0;" alt="" /></p></noscript> src="//piwik.mnl.de/matomo.php?idsite=17&amp;rec=1&amp;action_name=VM-Operator" style="border:0;" alt="" /></p></noscript>
<!-- End Matomo Code --> <!-- End Matomo Code -->
<script defer src="https://gotit.mnl.de/script.js" data-website-id="14b277ad-d330-4a54-82f1-a77d111240ac"></script>
</div> </div>

View file

@ -32,13 +32,11 @@ import java.util.regex.Pattern;
public class Convertions { public class Convertions {
@SuppressWarnings({ "PMD.UseConcurrentHashMap", @SuppressWarnings({ "PMD.UseConcurrentHashMap",
"PMD.FieldNamingConventions", "PMD.VariableNamingConventions" }) "PMD.FieldNamingConventions" })
private static final Map<String, BigInteger> unitMap = new HashMap<>(); private static final Map<String, BigInteger> unitMap = new HashMap<>();
@SuppressWarnings({ "PMD.FieldNamingConventions", @SuppressWarnings({ "PMD.FieldNamingConventions" })
"PMD.VariableNamingConventions" })
private static final List<Map.Entry<String, BigInteger>> unitMappings; private static final List<Map.Entry<String, BigInteger>> unitMappings;
@SuppressWarnings({ "PMD.FieldNamingConventions", @SuppressWarnings({ "PMD.FieldNamingConventions" })
"PMD.VariableNamingConventions" })
private static final Pattern memorySize private static final Pattern memorySize
= Pattern.compile("^\\s*(\\d+(\\.\\d+)?)\\s*([A-Za-z]*)\\s*"); = Pattern.compile("^\\s*(\\d+(\\.\\d+)?)\\s*([A-Za-z]*)\\s*");
@ -69,7 +67,6 @@ public class Convertions {
* @param amount the amount * @param amount the amount
* @return the big integer * @return the big integer
*/ */
@SuppressWarnings("PMD.DataflowAnomalyAnalysis")
public static BigInteger parseMemory(Object amount) { public static BigInteger parseMemory(Object amount) {
if (amount == null) { if (amount == null) {
return (BigInteger) amount; return (BigInteger) amount;

View file

@ -47,8 +47,7 @@ import org.yaml.snakeyaml.constructor.SafeConstructor;
/** /**
* Helpers for K8s API. * Helpers for K8s API.
*/ */
@SuppressWarnings({ "PMD.ShortClassName", "PMD.UseUtilityClass", @SuppressWarnings({ "PMD.ShortClassName", "PMD.UseUtilityClass" })
"PMD.DataflowAnomalyAnalysis" })
public class K8s { public class K8s {
/** /**
@ -113,7 +112,6 @@ public class K8s {
public static JsonObject yamlToJson(ApiClient client, Reader yaml) { public static JsonObject yamlToJson(ApiClient client, Reader yaml) {
// Avoid Yaml.load due to // Avoid Yaml.load due to
// https://github.com/kubernetes-client/java/issues/2741 // https://github.com/kubernetes-client/java/issues/2741
@SuppressWarnings("PMD.UseConcurrentHashMap")
Map<String, Object> yamlData Map<String, Object> yamlData
= new Yaml(new SafeConstructor(new LoaderOptions())).load(yaml); = new Yaml(new SafeConstructor(new LoaderOptions())).load(yaml);

View file

@ -48,8 +48,7 @@ import okhttp3.Response;
* A client with some additional properties. * A client with some additional properties.
*/ */
@SuppressWarnings({ "PMD.ExcessivePublicCount", "PMD.TooManyMethods", @SuppressWarnings({ "PMD.ExcessivePublicCount", "PMD.TooManyMethods",
"PMD.LinguisticNaming", "checkstyle:LineLength", "checkstyle:LineLength", "PMD.CouplingBetweenObjects", "PMD.GodClass" })
"PMD.CouplingBetweenObjects", "PMD.GodClass" })
public class K8sClient extends ApiClient { public class K8sClient extends ApiClient {
private ApiClient apiClient; private ApiClient apiClient;
@ -231,7 +230,6 @@ public class K8sClient extends ApiClient {
* @return the api client * @return the api client
* @see ApiClient#setKeyManagers(javax.net.ssl.KeyManager[]) * @see ApiClient#setKeyManagers(javax.net.ssl.KeyManager[])
*/ */
@SuppressWarnings("PMD.UseVarargs")
@Override @Override
public ApiClient setKeyManagers(KeyManager[] managers) { public ApiClient setKeyManagers(KeyManager[] managers) {
return apiClient().setKeyManagers(managers); return apiClient().setKeyManagers(managers);
@ -638,7 +636,6 @@ public class K8sClient extends ApiClient {
* @return the string * @return the string
* @see ApiClient#selectHeaderAccept(java.lang.String[]) * @see ApiClient#selectHeaderAccept(java.lang.String[])
*/ */
@SuppressWarnings("PMD.UseVarargs")
@Override @Override
public String selectHeaderAccept(String[] accepts) { public String selectHeaderAccept(String[] accepts) {
return apiClient().selectHeaderAccept(accepts); return apiClient().selectHeaderAccept(accepts);
@ -651,7 +648,6 @@ public class K8sClient extends ApiClient {
* @return the string * @return the string
* @see ApiClient#selectHeaderContentType(java.lang.String[]) * @see ApiClient#selectHeaderContentType(java.lang.String[])
*/ */
@SuppressWarnings("PMD.UseVarargs")
@Override @Override
public String selectHeaderContentType(String[] contentTypes) { public String selectHeaderContentType(String[] contentTypes) {
return apiClient().selectHeaderContentType(contentTypes); return apiClient().selectHeaderContentType(contentTypes);
@ -818,7 +814,7 @@ public class K8sClient extends ApiClient {
* @throws ApiException the api exception * @throws ApiException the api exception
* @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) * @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" }) @SuppressWarnings({ "rawtypes" })
@Override @Override
public Call buildCall(String path, String method, List<Pair> queryParams, public Call buildCall(String path, String method, List<Pair> queryParams,
List<Pair> collectionQueryParams, Object body, List<Pair> collectionQueryParams, Object body,
@ -847,7 +843,7 @@ public class K8sClient extends ApiClient {
* @throws ApiException the api exception * @throws ApiException the api exception
* @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) * @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" }) @SuppressWarnings({ "rawtypes" })
@Override @Override
public Request buildRequest(String path, String method, public Request buildRequest(String path, String method,
List<Pair> queryParams, List<Pair> collectionQueryParams, List<Pair> queryParams, List<Pair> collectionQueryParams,

View file

@ -45,8 +45,7 @@ import java.util.function.Function;
* @param <O> the generic type * @param <O> the generic type
* @param <L> the generic type * @param <L> the generic type
*/ */
@SuppressWarnings({ "PMD.DataflowAnomalyAnalysis", @SuppressWarnings({ "PMD.CouplingBetweenObjects" })
"PMD.CouplingBetweenObjects" })
public class K8sClusterGenericStub<O extends KubernetesObject, public class K8sClusterGenericStub<O extends KubernetesObject,
L extends KubernetesListObject> { L extends KubernetesListObject> {
protected final K8sClient client; protected final K8sClient client;
@ -240,6 +239,7 @@ public class K8sClusterGenericStub<O extends KubernetesObject,
* @param <L> the object list type * @param <L> the object list type
* @param <R> the result type * @param <R> the result type
*/ */
@FunctionalInterface
public interface GenericSupplier<O extends KubernetesObject, public interface GenericSupplier<O extends KubernetesObject,
L extends KubernetesListObject, L extends KubernetesListObject,
R extends K8sClusterGenericStub<O, L>> { R extends K8sClusterGenericStub<O, L>> {
@ -254,7 +254,6 @@ public class K8sClusterGenericStub<O extends KubernetesObject,
* @param name the name * @param name the name
* @return the result * @return the result
*/ */
@SuppressWarnings("PMD.UseObjectForClearerAPI")
R get(Class<O> objectClass, Class<L> objectListClass, K8sClient client, R get(Class<O> objectClass, Class<L> objectListClass, K8sClient client,
APIResource context, String name); APIResource context, String name);
} }
@ -283,7 +282,6 @@ public class K8sClusterGenericStub<O extends KubernetesObject,
* @return the stub if the object exists * @return the stub if the object exists
* @throws ApiException the api exception * @throws ApiException the api exception
*/ */
@SuppressWarnings({ "PMD.AvoidBranchingStatementAsLastInLoop" })
public static <O extends KubernetesObject, L extends KubernetesListObject, public static <O extends KubernetesObject, L extends KubernetesListObject,
R extends K8sClusterGenericStub<O, L>> R extends K8sClusterGenericStub<O, L>>
R get(Class<O> objectClass, Class<L> objectListClass, R get(Class<O> objectClass, Class<L> objectListClass,
@ -314,8 +312,6 @@ public class K8sClusterGenericStub<O extends KubernetesObject,
* @return the stub if the object exists * @return the stub if the object exists
* @throws ApiException the api exception * @throws ApiException the api exception
*/ */
@SuppressWarnings({ "PMD.AvoidBranchingStatementAsLastInLoop",
"PMD.UseObjectForClearerAPI" })
public static <O extends KubernetesObject, L extends KubernetesListObject, public static <O extends KubernetesObject, L extends KubernetesListObject,
R extends K8sClusterGenericStub<O, L>> R extends K8sClusterGenericStub<O, L>>
R get(Class<O> objectClass, Class<L> objectListClass, R get(Class<O> objectClass, Class<L> objectListClass,
@ -340,8 +336,6 @@ public class K8sClusterGenericStub<O extends KubernetesObject,
* @return the stub if the object exists * @return the stub if the object exists
* @throws ApiException the api exception * @throws ApiException the api exception
*/ */
@SuppressWarnings({ "PMD.AvoidBranchingStatementAsLastInLoop",
"PMD.AvoidInstantiatingObjectsInLoops", "PMD.UseObjectForClearerAPI" })
public static <O extends KubernetesObject, L extends KubernetesListObject, public static <O extends KubernetesObject, L extends KubernetesListObject,
R extends K8sClusterGenericStub<O, L>> R extends K8sClusterGenericStub<O, L>>
R create(Class<O> objectClass, Class<L> objectListClass, R create(Class<O> objectClass, Class<L> objectListClass,

View file

@ -29,7 +29,6 @@ import io.kubernetes.client.openapi.models.V1ObjectMeta;
* notably the metadata, is made available through the methods * notably the metadata, is made available through the methods
* defined by {@link KubernetesObject}. * defined by {@link KubernetesObject}.
*/ */
@SuppressWarnings("PMD.DataClass")
public class K8sDynamicModel implements KubernetesObject { public class K8sDynamicModel implements KubernetesObject {
private final V1ObjectMeta metadata; private final V1ObjectMeta metadata;

View file

@ -62,7 +62,7 @@ public class K8sDynamicModelsBase<T extends K8sDynamicModel>
} catch (InstantiationException | IllegalAccessException } catch (InstantiationException | IllegalAccessException
| IllegalArgumentException | InvocationTargetException | IllegalArgumentException | InvocationTargetException
| NoSuchMethodException | SecurityException exc) { | NoSuchMethodException | SecurityException exc) {
throw new IllegalArgumentException(exc); // NOPMD throw new IllegalArgumentException(exc);
} }
} }
} }

View file

@ -31,7 +31,6 @@ import java.util.Collection;
* state and can therefore be used for any kind of object, especially * state and can therefore be used for any kind of object, especially
* custom objects. * custom objects.
*/ */
@SuppressWarnings("PMD.DataflowAnomalyAnalysis")
public class K8sDynamicStub public class K8sDynamicStub
extends K8sDynamicStubBase<K8sDynamicModel, K8sDynamicModels> { extends K8sDynamicStubBase<K8sDynamicModel, K8sDynamicModels> {
@ -64,8 +63,6 @@ public class K8sDynamicStub
* @return the stub if the object exists * @return the stub if the object exists
* @throws ApiException the api exception * @throws ApiException the api exception
*/ */
@SuppressWarnings({ "PMD.AvoidBranchingStatementAsLastInLoop",
"PMD.AvoidInstantiatingObjectsInLoops", "PMD.UseObjectForClearerAPI" })
public static K8sDynamicStub get(K8sClient client, public static K8sDynamicStub get(K8sClient client,
GroupVersionKind gvk, String namespace, String name) GroupVersionKind gvk, String namespace, String name)
throws ApiException { throws ApiException {
@ -83,8 +80,6 @@ public class K8sDynamicStub
* @return the stub if the object exists * @return the stub if the object exists
* @throws ApiException the api exception * @throws ApiException the api exception
*/ */
@SuppressWarnings({ "PMD.AvoidBranchingStatementAsLastInLoop",
"PMD.AvoidInstantiatingObjectsInLoops", "PMD.UseObjectForClearerAPI" })
public static K8sDynamicStub get(K8sClient client, public static K8sDynamicStub get(K8sClient client,
APIResource context, String namespace, String name) { APIResource context, String namespace, String name) {
return new K8sDynamicStub(client, context, namespace, name); return new K8sDynamicStub(client, context, namespace, name);

View file

@ -26,7 +26,6 @@ import io.kubernetes.client.Discovery.APIResource;
* state and can therefore be used for any kind of object, especially * state and can therefore be used for any kind of object, especially
* custom objects. * custom objects.
*/ */
@SuppressWarnings("PMD.DataflowAnomalyAnalysis")
public abstract class K8sDynamicStubBase<O extends K8sDynamicModel, public abstract class K8sDynamicStubBase<O extends K8sDynamicModel,
L extends K8sDynamicModelsBase<O>> extends K8sGenericStub<O, L> { L extends K8sDynamicModelsBase<O>> extends K8sGenericStub<O, L> {
@ -40,7 +39,6 @@ public abstract class K8sDynamicStubBase<O extends K8sDynamicModel,
* @param namespace the namespace * @param namespace the namespace
* @param name the name * @param name the name
*/ */
@SuppressWarnings("PMD.ConstructorCallsOverridableMethod")
public K8sDynamicStubBase(Class<O> objectClass, public K8sDynamicStubBase(Class<O> objectClass,
Class<L> objectListClass, DynamicTypeAdapterFactory<O, L> taf, Class<L> objectListClass, DynamicTypeAdapterFactory<O, L> taf,
K8sClient client, APIResource context, String namespace, K8sClient client, APIResource context, String namespace,

View file

@ -48,7 +48,7 @@ import java.util.function.Function;
* @param <O> the generic type * @param <O> the generic type
* @param <L> the generic type * @param <L> the generic type
*/ */
@SuppressWarnings({ "PMD.DataflowAnomalyAnalysis", "PMD.TooManyMethods" }) @SuppressWarnings({ "PMD.TooManyMethods" })
public class K8sGenericStub<O extends KubernetesObject, public class K8sGenericStub<O extends KubernetesObject,
L extends KubernetesListObject> { L extends KubernetesListObject> {
protected final K8sClient client; protected final K8sClient client;
@ -200,7 +200,6 @@ public class K8sGenericStub<O extends KubernetesObject,
* @return the updated model or empty if the object was not found * @return the updated model or empty if the object was not found
* @throws ApiException the api exception * @throws ApiException the api exception
*/ */
@SuppressWarnings("PMD.AssignmentInOperand")
public Optional<O> updateStatus(O object, Function<O, Object> updater) public Optional<O> updateStatus(O object, Function<O, Object> updater)
throws ApiException { throws ApiException {
return K8s.optional(api.updateStatus(object, updater)); return K8s.optional(api.updateStatus(object, updater));
@ -218,7 +217,7 @@ public class K8sGenericStub<O extends KubernetesObject,
* @return the updated model or empty if the object was not found * @return the updated model or empty if the object was not found
* @throws ApiException the api exception * @throws ApiException the api exception
*/ */
@SuppressWarnings({ "PMD.AssignmentInOperand", "PMD.UnusedAssignment" }) @SuppressWarnings({ "PMD.AssignmentInOperand" })
public Optional<O> updateStatus(Function<O, Object> updater, O current, public Optional<O> updateStatus(Function<O, Object> updater, O current,
int retries) throws ApiException { int retries) throws ApiException {
while (true) { while (true) {
@ -248,7 +247,6 @@ public class K8sGenericStub<O extends KubernetesObject,
* @return the updated model or empty if the object was not found * @return the updated model or empty if the object was not found
* @throws ApiException the api exception * @throws ApiException the api exception
*/ */
@SuppressWarnings({ "PMD.AssignmentInOperand", "PMD.UnusedAssignment" })
public Optional<O> updateStatus(Function<O, Object> updater, int retries) public Optional<O> updateStatus(Function<O, Object> updater, int retries)
throws ApiException { throws ApiException {
return updateStatus(updater, null, retries); return updateStatus(updater, null, retries);
@ -359,6 +357,7 @@ public class K8sGenericStub<O extends KubernetesObject,
* @param <L> the object list type * @param <L> the object list type
* @param <R> the result type * @param <R> the result type
*/ */
@FunctionalInterface
public interface GenericSupplier<O extends KubernetesObject, public interface GenericSupplier<O extends KubernetesObject,
L extends KubernetesListObject, R extends K8sGenericStub<O, L>> { L extends KubernetesListObject, R extends K8sGenericStub<O, L>> {
@ -370,7 +369,6 @@ public class K8sGenericStub<O extends KubernetesObject,
* @param name the name * @param name the name
* @return the result * @return the result
*/ */
@SuppressWarnings("PMD.UseObjectForClearerAPI")
R get(K8sClient client, String namespace, String name); R get(K8sClient client, String namespace, String name);
} }
@ -396,8 +394,6 @@ public class K8sGenericStub<O extends KubernetesObject,
* @return the stub if the object exists * @return the stub if the object exists
* @throws ApiException the api exception * @throws ApiException the api exception
*/ */
@SuppressWarnings({ "PMD.AvoidBranchingStatementAsLastInLoop",
"PMD.AvoidInstantiatingObjectsInLoops", "PMD.UseObjectForClearerAPI" })
public static <O extends KubernetesObject, L extends KubernetesListObject, public static <O extends KubernetesObject, L extends KubernetesListObject,
R extends K8sGenericStub<O, L>> R extends K8sGenericStub<O, L>>
R create(Class<O> objectClass, Class<L> objectListClass, R create(Class<O> objectClass, Class<L> objectListClass,

View file

@ -50,7 +50,6 @@ public class K8sObserver<O extends KubernetesObject,
ADDED, MODIFIED, DELETED ADDED, MODIFIED, DELETED
} }
@SuppressWarnings("PMD.FieldNamingConventions")
protected final Logger logger = Logger.getLogger(getClass().getName()); protected final Logger logger = Logger.getLogger(getClass().getName());
protected final K8sClient client; protected final K8sClient client;
@ -73,8 +72,7 @@ public class K8sObserver<O extends KubernetesObject,
* @param namespace the namespace * @param namespace the namespace
* @param options the options * @param options the options
*/ */
@SuppressWarnings({ "PMD.AvoidBranchingStatementAsLastInLoop", @SuppressWarnings({ "PMD.AvoidCatchingThrowable",
"PMD.UseObjectForClearerAPI", "PMD.AvoidCatchingThrowable",
"PMD.CognitiveComplexity", "PMD.AvoidCatchingGenericException" }) "PMD.CognitiveComplexity", "PMD.AvoidCatchingGenericException" })
public K8sObserver(Class<O> objectClass, Class<L> objectListClass, public K8sObserver(Class<O> objectClass, Class<L> objectListClass,
K8sClient client, APIResource context, String namespace, K8sClient client, APIResource context, String namespace,
@ -100,7 +98,6 @@ public class K8sObserver<O extends KubernetesObject,
while (!Thread.currentThread().isInterrupted()) { while (!Thread.currentThread().isInterrupted()) {
Instant startedAt = Instant.now(); Instant startedAt = Instant.now();
try { try {
@SuppressWarnings("PMD.AvoidInstantiatingObjectsInLoops")
var changed var changed
= api.watch(namespace, options).iterator(); = api.watch(namespace, options).iterator();
while (changed.hasNext()) { while (changed.hasNext()) {
@ -233,7 +230,6 @@ public class K8sObserver<O extends KubernetesObject,
} }
@Override @Override
@SuppressWarnings("PMD.UseLocaleWithCaseConversions")
public String toString() { public String toString() {
return "Observer for " + K8s.toString(context) + " " + namespace; return "Observer for " + K8s.toString(context) + " " + namespace;
} }

View file

@ -26,7 +26,6 @@ import java.util.List;
/** /**
* A stub for config maps (v1). * A stub for config maps (v1).
*/ */
@SuppressWarnings("PMD.DataflowAnomalyAnalysis")
public class K8sV1ConfigMapStub public class K8sV1ConfigMapStub
extends K8sGenericStub<V1ConfigMap, V1ConfigMapList> { extends K8sGenericStub<V1ConfigMap, V1ConfigMapList> {

View file

@ -29,7 +29,6 @@ import java.util.Optional;
/** /**
* A stub for pods (v1). * A stub for pods (v1).
*/ */
@SuppressWarnings("PMD.DataflowAnomalyAnalysis")
public class K8sV1DeploymentStub public class K8sV1DeploymentStub
extends K8sGenericStub<V1Deployment, V1DeploymentList> { extends K8sGenericStub<V1Deployment, V1DeploymentList> {

View file

@ -29,7 +29,6 @@ import java.util.List;
/** /**
* A stub for nodes (v1). * A stub for nodes (v1).
*/ */
@SuppressWarnings("PMD.DataflowAnomalyAnalysis")
public class K8sV1NodeStub extends K8sClusterGenericStub<V1Node, V1NodeList> { public class K8sV1NodeStub extends K8sClusterGenericStub<V1Node, V1NodeList> {
public static final APIResource CONTEXT = new APIResource("", List.of("v1"), public static final APIResource CONTEXT = new APIResource("", List.of("v1"),
@ -74,8 +73,7 @@ public class K8sV1NodeStub extends K8sClusterGenericStub<V1Node, V1NodeList> {
/** /**
* Provide {@link GenericSupplier}. * Provide {@link GenericSupplier}.
*/ */
@SuppressWarnings({ "PMD.UnusedFormalParameter", @SuppressWarnings({ "PMD.UnusedFormalParameter" })
"PMD.UnusedPrivateMethod" })
private static K8sV1NodeStub getGeneric(Class<V1Node> objectClass, private static K8sV1NodeStub getGeneric(Class<V1Node> objectClass,
Class<V1NodeList> objectListClass, K8sClient client, Class<V1NodeList> objectListClass, K8sClient client,
APIResource context, String name) { APIResource context, String name) {

View file

@ -29,7 +29,6 @@ import java.util.List;
/** /**
* A stub for pods (v1). * A stub for pods (v1).
*/ */
@SuppressWarnings("PMD.DataflowAnomalyAnalysis")
public class K8sV1PodStub extends K8sGenericStub<V1Pod, V1PodList> { public class K8sV1PodStub extends K8sGenericStub<V1Pod, V1PodList> {
/** The pods' context. */ /** The pods' context. */

View file

@ -29,7 +29,6 @@ import java.util.List;
/** /**
* A stub for pods (v1). * A stub for pods (v1).
*/ */
@SuppressWarnings("PMD.DataflowAnomalyAnalysis")
public class K8sV1PvcStub extends public class K8sV1PvcStub extends
K8sGenericStub<V1PersistentVolumeClaim, V1PersistentVolumeClaimList> { K8sGenericStub<V1PersistentVolumeClaim, V1PersistentVolumeClaimList> {

View file

@ -29,7 +29,6 @@ import java.util.List;
/** /**
* A stub for secrets (v1). * A stub for secrets (v1).
*/ */
@SuppressWarnings("PMD.DataflowAnomalyAnalysis")
public class K8sV1SecretStub extends K8sGenericStub<V1Secret, V1SecretList> { public class K8sV1SecretStub extends K8sGenericStub<V1Secret, V1SecretList> {
public static final APIResource CONTEXT = new APIResource("", List.of("v1"), public static final APIResource CONTEXT = new APIResource("", List.of("v1"),

View file

@ -29,7 +29,6 @@ import java.util.List;
/** /**
* A stub for secrets (v1). * A stub for secrets (v1).
*/ */
@SuppressWarnings("PMD.DataflowAnomalyAnalysis")
public class K8sV1ServiceStub extends K8sGenericStub<V1Service, V1ServiceList> { public class K8sV1ServiceStub extends K8sGenericStub<V1Service, V1ServiceList> {
public static final APIResource CONTEXT = new APIResource("", List.of("v1"), public static final APIResource CONTEXT = new APIResource("", List.of("v1"),

View file

@ -26,7 +26,6 @@ import java.util.List;
/** /**
* A stub for stateful sets (v1). * A stub for stateful sets (v1).
*/ */
@SuppressWarnings("PMD.DataflowAnomalyAnalysis")
public class K8sV1StatefulSetStub public class K8sV1StatefulSetStub
extends K8sGenericStub<V1StatefulSet, V1StatefulSetList> { extends K8sGenericStub<V1StatefulSet, V1StatefulSetList> {

View file

@ -46,11 +46,10 @@ import org.jdrupes.vmoperator.util.DataPath;
/** /**
* Represents a VM definition. * Represents a VM definition.
*/ */
@SuppressWarnings({ "PMD.DataClass", "PMD.TooManyMethods", @SuppressWarnings({ "PMD.DataClass", "PMD.TooManyMethods" })
"PMD.CouplingBetweenObjects" })
public class VmDefinition extends K8sDynamicModel { public class VmDefinition extends K8sDynamicModel {
@SuppressWarnings({ "PMD.FieldNamingConventions", "unused" }) @SuppressWarnings({ "unused" })
private static final Logger logger private static final Logger logger
= Logger.getLogger(VmDefinition.class.getName()); = Logger.getLogger(VmDefinition.class.getName());
@SuppressWarnings("PMD.FieldNamingConventions") @SuppressWarnings("PMD.FieldNamingConventions")

View file

@ -31,7 +31,6 @@ import java.util.Collection;
* state and can therefore be used for any kind of object, especially * state and can therefore be used for any kind of object, especially
* custom objects. * custom objects.
*/ */
@SuppressWarnings("PMD.DataflowAnomalyAnalysis")
public class VmDefinitionStub public class VmDefinitionStub
extends K8sDynamicStubBase<VmDefinition, VmDefinitions> { extends K8sDynamicStubBase<VmDefinition, VmDefinitions> {
@ -64,8 +63,6 @@ public class VmDefinitionStub
* @return the stub if the object exists * @return the stub if the object exists
* @throws ApiException the api exception * @throws ApiException the api exception
*/ */
@SuppressWarnings({ "PMD.AvoidBranchingStatementAsLastInLoop",
"PMD.AvoidInstantiatingObjectsInLoops", "PMD.UseObjectForClearerAPI" })
public static VmDefinitionStub get(K8sClient client, public static VmDefinitionStub get(K8sClient client,
GroupVersionKind gvk, String namespace, String name) GroupVersionKind gvk, String namespace, String name)
throws ApiException { throws ApiException {
@ -83,8 +80,6 @@ public class VmDefinitionStub
* @return the stub if the object exists * @return the stub if the object exists
* @throws ApiException the api exception * @throws ApiException the api exception
*/ */
@SuppressWarnings({ "PMD.AvoidBranchingStatementAsLastInLoop",
"PMD.AvoidInstantiatingObjectsInLoops", "PMD.UseObjectForClearerAPI" })
public static VmDefinitionStub get(K8sClient client, public static VmDefinitionStub get(K8sClient client,
APIResource context, String namespace, String name) { APIResource context, String namespace, String name) {
return new VmDefinitionStub(client, context, namespace, name); return new VmDefinitionStub(client, context, namespace, name);

View file

@ -34,7 +34,6 @@ import java.util.logging.Logger;
*/ */
public class VmExtraData { public class VmExtraData {
@SuppressWarnings("PMD.FieldNamingConventions")
private static final Logger logger private static final Logger logger
= Logger.getLogger(VmExtraData.class.getName()); = Logger.getLogger(VmExtraData.class.getName());
@ -118,14 +117,14 @@ public class VmExtraData {
if (addr.isEmpty()) { if (addr.isEmpty()) {
logger logger
.severe(() -> "Failed to find display IP for " + vmDef.name()); .severe(() -> "Failed to find display IP for " + vmDef.name());
return null; return Optional.empty();
} }
var port = vmDef.<Number> fromVm("display", "spice", "port") var port = vmDef.<Number> fromVm("display", "spice", "port")
.map(Number::longValue); .map(Number::longValue);
if (port.isEmpty()) { if (port.isEmpty()) {
logger logger
.severe(() -> "No port defined for display of " + vmDef.name()); .severe(() -> "No port defined for display of " + vmDef.name());
return null; return Optional.empty();
} }
StringBuffer data = new StringBuffer(100) StringBuffer data = new StringBuffer(100)
.append("[virt-viewer]\ntype=spice\nhost=") .append("[virt-viewer]\ntype=spice\nhost=")

View file

@ -35,7 +35,6 @@ import org.jdrupes.vmoperator.util.DataPath;
/** /**
* Represents a VM pool. * Represents a VM pool.
*/ */
@SuppressWarnings({ "PMD.DataClass" })
public class VmPool { public class VmPool {
private final String name; private final String name;

View file

@ -24,7 +24,6 @@ import org.jgrapes.core.Event;
/** /**
* Assign a VM from a pool to a user. * Assign a VM from a pool to a user.
*/ */
@SuppressWarnings("PMD.DataClass")
public class AssignVm extends Event<VmData> { public class AssignVm extends Event<VmData> {
private final String fromPool; private final String fromPool;

View file

@ -43,7 +43,6 @@ public interface ChannelDictionary<K, C extends Channel, A> {
* @param channel the channel * @param channel the channel
* @param associated the associated * @param associated the associated
*/ */
@SuppressWarnings("PMD.ShortClassName")
public record Value<C extends Channel, A>(C channel, A associated) { public record Value<C extends Channel, A>(C channel, A associated) {
} }

View file

@ -149,8 +149,6 @@ public class ChannelManager<K, C extends Channel, A>
* @param supplier the supplier * @param supplier the supplier
* @return the channel * @return the channel
*/ */
@SuppressWarnings({ "PMD.AssignmentInOperand",
"PMD.DataflowAnomalyAnalysis" })
public C computeIfAbsent(K key, Function<K, C> supplier) { public C computeIfAbsent(K key, Function<K, C> supplier) {
return entries.computeIfAbsent(key, return entries.computeIfAbsent(key,
k -> new Value<>(supplier.apply(k), null)).channel(); k -> new Value<>(supplier.apply(k), null)).channel();

View file

@ -24,7 +24,6 @@ import org.jgrapes.core.Event;
/** /**
* Gets the current display secret and optionally updates it. * Gets the current display secret and optionally updates it.
*/ */
@SuppressWarnings("PMD.DataClass")
public class GetDisplaySecret extends Event<String> { public class GetDisplaySecret extends Event<String> {
private final VmDefinition vmDef; private final VmDefinition vmDef;

View file

@ -27,7 +27,6 @@ import org.jgrapes.core.Event;
/** /**
* Gets the known pools' definitions. * Gets the known pools' definitions.
*/ */
@SuppressWarnings("PMD.DataClass")
public class GetPools extends Event<List<VmPool>> { public class GetPools extends Event<List<VmPool>> {
private String name; private String name;

View file

@ -27,7 +27,6 @@ import org.jgrapes.core.Event;
/** /**
* Gets the known VMs' definitions and channels. * Gets the known VMs' definitions and channels.
*/ */
@SuppressWarnings("PMD.DataClass")
public class GetVms extends Event<List<GetVms.VmData>> { public class GetVms extends Event<List<GetVms.VmData>> {
private String name; private String name;

View file

@ -24,7 +24,6 @@ import org.jgrapes.core.Event;
/** /**
* Modifies a VM. * Modifies a VM.
*/ */
@SuppressWarnings("PMD.DataClass")
public class ModifyVm extends Event<Void> { public class ModifyVm extends Event<Void> {
private final String name; private final String name;

View file

@ -23,7 +23,6 @@ import org.jgrapes.core.Event;
/** /**
* Triggers a reset of the VM. * Triggers a reset of the VM.
*/ */
@SuppressWarnings("PMD.DataClass")
public class ResetVm extends Event<String> { public class ResetVm extends Event<String> {
private final String vmName; private final String vmName;

View file

@ -24,7 +24,6 @@ import org.jgrapes.core.Event;
/** /**
* Note the assignment to a user in the VM status. * Note the assignment to a user in the VM status.
*/ */
@SuppressWarnings("PMD.DataClass")
public class UpdateAssignment extends Event<Boolean> { public class UpdateAssignment extends Event<Boolean> {
private final VmPool fromPool; private final VmPool fromPool;

View file

@ -28,7 +28,6 @@ import org.jgrapes.core.Subchannel.DefaultSubchannel;
/** /**
* A subchannel used to send the events related to a specific VM. * A subchannel used to send the events related to a specific VM.
*/ */
@SuppressWarnings("PMD.DataClass")
public class VmChannel extends DefaultSubchannel { public class VmChannel extends DefaultSubchannel {
private final EventPipeline pipeline; private final EventPipeline pipeline;
@ -56,7 +55,6 @@ public class VmChannel extends DefaultSubchannel {
* @param definition the definition * @param definition the definition
* @return the watch channel * @return the watch channel
*/ */
@SuppressWarnings("PMD.LinguisticNaming")
public VmChannel setVmDefinition(VmDefinition definition) { public VmChannel setVmDefinition(VmDefinition definition) {
this.definition = definition; this.definition = definition;
return this; return this;
@ -87,7 +85,6 @@ public class VmChannel extends DefaultSubchannel {
* @param generation the generation to set * @param generation the generation to set
* @return true if value has changed * @return true if value has changed
*/ */
@SuppressWarnings("PMD.LinguisticNaming")
public boolean setGeneration(long generation) { public boolean setGeneration(long generation) {
if (this.generation == generation) { if (this.generation == generation) {
return false; return false;

View file

@ -26,7 +26,6 @@ import org.jgrapes.core.Event;
/** /**
* Indicates a change in a pool configuration. * Indicates a change in a pool configuration.
*/ */
@SuppressWarnings("PMD.DataClass")
public class VmPoolChanged extends Event<Void> { public class VmPoolChanged extends Event<Void> {
private final VmPool vmPool; private final VmPool vmPool;

View file

@ -17,7 +17,7 @@ dependencies {
implementation 'org.jgrapes:org.jgrapes.io:[2.12.1,3)' implementation 'org.jgrapes:org.jgrapes.io:[2.12.1,3)'
implementation 'org.jgrapes:org.jgrapes.http:[3.5.0,4)' implementation 'org.jgrapes:org.jgrapes.http:[3.5.0,4)'
implementation 'org.jgrapes:org.jgrapes.webconsole.base:[2.2.0,3)' implementation 'org.jgrapes:org.jgrapes.webconsole.base:[2.3.0,3)'
implementation 'org.jgrapes:org.jgrapes.webconsole.vuejs:[1.8.0,2)' implementation 'org.jgrapes:org.jgrapes.webconsole.vuejs:[1.8.0,2)'
implementation 'org.jgrapes:org.jgrapes.webconsole.rbac:[1.4.0,2)' implementation 'org.jgrapes:org.jgrapes.webconsole.rbac:[1.4.0,2)'
implementation 'org.jgrapes:org.jgrapes.webconlet.oidclogin:[1.7.0,2)' implementation 'org.jgrapes:org.jgrapes.webconlet.oidclogin:[1.7.0,2)'

View file

@ -37,7 +37,7 @@ data:
# The template to use. Resolved relative to /usr/share/vmrunner/templates. # The template to use. Resolved relative to /usr/share/vmrunner/templates.
# template: "Standard-VM-latest.ftl.yaml" # template: "Standard-VM-latest.ftl.yaml"
<#if spec.runnerTemplate?? && spec.runnerTemplate.source?? > <#if spec.runnerTemplate?? && spec.runnerTemplate.source?? >
template: ${ cm.spec().runnerTemplate.source } template: ${ spec.runnerTemplate.source }
</#if> </#if>
# The template is copied to the data diretory when the VM starts for # The template is copied to the data diretory when the VM starts for

View file

@ -51,7 +51,6 @@ import org.jgrapes.util.events.ConfigurationUpdate;
* @param <O> the object type for the context * @param <O> the object type for the context
* @param <L> the object list type for the context * @param <L> the object list type for the context
*/ */
@SuppressWarnings({ "PMD.DataflowAnomalyAnalysis" })
public abstract class AbstractMonitor<O extends KubernetesObject, public abstract class AbstractMonitor<O extends KubernetesObject,
L extends KubernetesListObject, C extends Channel> extends Component { L extends KubernetesListObject, C extends Channel> extends Component {
@ -181,7 +180,6 @@ public abstract class AbstractMonitor<O extends KubernetesObject,
* @param event the event * @param event the event
*/ */
@Handler(priority = 10) @Handler(priority = 10)
@SuppressWarnings("PMD.AvoidInstantiatingObjectsInLoops")
public void onStart(Start event) { public void onStart(Start event) {
try { try {
// Get namespace // Get namespace

View file

@ -56,7 +56,6 @@ import org.yaml.snakeyaml.constructor.SafeConstructor;
/** /**
* Delegee for reconciling the config map * Delegee for reconciling the config map
*/ */
@SuppressWarnings("PMD.DataflowAnomalyAnalysis")
/* default */ class ConfigMapReconciler { /* default */ class ConfigMapReconciler {
protected final Logger logger = Logger.getLogger(getClass().getName()); protected final Logger logger = Logger.getLogger(getClass().getName());
@ -81,7 +80,6 @@ import org.yaml.snakeyaml.constructor.SafeConstructor;
* @throws TemplateException the template exception * @throws TemplateException the template exception
* @throws ApiException the API exception * @throws ApiException the API exception
*/ */
@SuppressWarnings("PMD.AvoidDuplicateLiterals")
public void reconcile(Map<String, Object> model, VmChannel channel, public void reconcile(Map<String, Object> model, VmChannel channel,
boolean modelChanged) boolean modelChanged)
throws IOException, TemplateException, ApiException { throws IOException, TemplateException, ApiException {
@ -98,6 +96,8 @@ import org.yaml.snakeyaml.constructor.SafeConstructor;
channel.setAssociated(PrevData.class, prevData); channel.setAssociated(PrevData.class, prevData);
// Combine template and data and parse result // Combine template and data and parse result
logger.fine(() -> "Create/update configmap "
+ DataPath.<String> get(model, "cr", "name").orElse("unknown"));
model.put("adjustCloudInitMeta", adjustCloudInitMetaModel); model.put("adjustCloudInitMeta", adjustCloudInitMetaModel);
prevData.added.put("adjustCloudInitMeta", adjustCloudInitMetaModel); prevData.added.put("adjustCloudInitMeta", adjustCloudInitMetaModel);
var fmTemplate = fmConfig.getTemplate("runnerConfig.ftl.yaml"); var fmTemplate = fmConfig.getTemplate("runnerConfig.ftl.yaml");
@ -187,7 +187,6 @@ import org.yaml.snakeyaml.constructor.SafeConstructor;
private final TemplateMethodModelEx adjustCloudInitMetaModel private final TemplateMethodModelEx adjustCloudInitMetaModel
= new TemplateMethodModelEx() { = new TemplateMethodModelEx() {
@Override @Override
@SuppressWarnings("PMD.PreserveStackTrace")
public Object exec(@SuppressWarnings("rawtypes") List arguments) public Object exec(@SuppressWarnings("rawtypes") List arguments)
throws TemplateModelException { throws TemplateModelException {
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")

View file

@ -21,7 +21,6 @@ package org.jdrupes.vmoperator.manager;
/** /**
* Some constants. * Some constants.
*/ */
@SuppressWarnings("PMD.DataClass")
public class Constants extends org.jdrupes.vmoperator.common.Constants { public class Constants extends org.jdrupes.vmoperator.common.Constants {
/** The Constant STATE_RUNNING. */ /** The Constant STATE_RUNNING. */

View file

@ -50,6 +50,7 @@ import org.jdrupes.vmoperator.manager.events.VmPoolChanged;
import org.jdrupes.vmoperator.manager.events.VmResourceChanged; import org.jdrupes.vmoperator.manager.events.VmResourceChanged;
import org.jgrapes.core.Channel; import org.jgrapes.core.Channel;
import org.jgrapes.core.Component; import org.jgrapes.core.Component;
import org.jgrapes.core.EventPipeline;
import org.jgrapes.core.annotation.Handler; import org.jgrapes.core.annotation.Handler;
import org.jgrapes.core.events.HandlingError; import org.jgrapes.core.events.HandlingError;
import org.jgrapes.core.events.Start; import org.jgrapes.core.events.Start;
@ -94,7 +95,7 @@ import org.jgrapes.util.events.ConfigurationUpdate;
public class Controller extends Component { public class Controller extends Component {
private String namespace; private String namespace;
private final ChannelManager<String, VmChannel, ?> chanMgr; private final ChannelManager<String, VmChannel, EventPipeline> chanMgr;
/** /**
* Creates a new instance. * Creates a new instance.
@ -182,7 +183,7 @@ public class Controller extends Component {
fire(new Exit(2)); fire(new Exit(2));
return; return;
} }
logger.fine(() -> "Controlling namespace \"" + namespace + "\"."); logger.config(() -> "Controlling namespace \"" + namespace + "\".");
} }
/** /**

View file

@ -42,7 +42,6 @@ import org.jgrapes.core.Channel;
* of the pod running the VM in response to force an update of the files * of the pod running the VM in response to force an update of the files
* in the pod that reflect the information from the secret. * in the pod that reflect the information from the secret.
*/ */
@SuppressWarnings({ "PMD.DataflowAnomalyAnalysis", "PMD.TooManyStaticImports" })
public class DisplaySecretMonitor public class DisplaySecretMonitor
extends AbstractMonitor<V1Secret, V1SecretList, VmChannel> { extends AbstractMonitor<V1Secret, V1SecretList, VmChannel> {

View file

@ -66,7 +66,6 @@ import org.jose4j.base64url.Base64;
* * `passwordValidity`: the validity of the random password in seconds. * * `passwordValidity`: the validity of the random password in seconds.
* Used to calculate the password expiry time in the generated secret. * Used to calculate the password expiry time in the generated secret.
*/ */
@SuppressWarnings({ "PMD.DataflowAnomalyAnalysis", "PMD.TooManyStaticImports" })
public class DisplaySecretReconciler extends Component { public class DisplaySecretReconciler extends Component {
protected final Logger logger = Logger.getLogger(getClass().getName()); protected final Logger logger = Logger.getLogger(getClass().getName());
@ -104,12 +103,15 @@ public class DisplaySecretReconciler extends Component {
return oldConfig; return oldConfig;
}).ifPresent(c -> { }).ifPresent(c -> {
try { try {
if (c.containsKey("passwordValidity")) { Optional.ofNullable(c.get("passwordValidity"))
passwordValidity = Integer .map(p -> p instanceof Integer ? (Integer) p
.parseInt((String) c.get("passwordValidity")); : Integer.valueOf((String) p))
} .ifPresent(p -> {
} catch (ClassCastException e) { passwordValidity = p;
logger.config("Malformed configuration: " + e.getMessage()); });
} catch (NumberFormatException e) {
logger.warning(
() -> "Malformed configuration: " + e.getMessage());
} }
}); });
} }
@ -155,9 +157,11 @@ public class DisplaySecretReconciler extends Component {
} }
// Create secret // Create secret
var secretName = vmDef.name() + "-" + DisplaySecret.NAME;
logger.fine(() -> "Create/update secret " + secretName);
var secret = new V1Secret(); var secret = new V1Secret();
secret.setMetadata(new V1ObjectMeta().namespace(vmDef.namespace()) secret.setMetadata(new V1ObjectMeta().namespace(vmDef.namespace())
.name(vmDef.name() + "-" + DisplaySecret.NAME) .name(secretName)
.putLabelsItem("app.kubernetes.io/name", APP_NAME) .putLabelsItem("app.kubernetes.io/name", APP_NAME)
.putLabelsItem("app.kubernetes.io/component", DisplaySecret.NAME) .putLabelsItem("app.kubernetes.io/component", DisplaySecret.NAME)
.putLabelsItem("app.kubernetes.io/instance", vmDef.name())); .putLabelsItem("app.kubernetes.io/instance", vmDef.name()));
@ -188,7 +192,6 @@ public class DisplaySecretReconciler extends Component {
* @throws ApiException the api exception * @throws ApiException the api exception
*/ */
@Handler @Handler
@SuppressWarnings("PMD.StringInstantiation")
public void onGetDisplaySecret(GetDisplaySecret event, VmChannel channel) public void onGetDisplaySecret(GetDisplaySecret event, VmChannel channel)
throws ApiException { throws ApiException {
// Get VM definition and check if running // Get VM definition and check if running
@ -317,7 +320,6 @@ public class DisplaySecretReconciler extends Component {
/** /**
* The Class PendingGet. * The Class PendingGet.
*/ */
@SuppressWarnings("PMD.DataClass")
private static class PendingRequest { private static class PendingRequest {
public final GetDisplaySecret event; public final GetDisplaySecret event;
public final long expectedSerial; public final long expectedSerial;

View file

@ -36,6 +36,7 @@ import java.util.logging.Logger;
import org.jdrupes.vmoperator.common.K8sV1ServiceStub; import org.jdrupes.vmoperator.common.K8sV1ServiceStub;
import org.jdrupes.vmoperator.common.VmDefinition; import org.jdrupes.vmoperator.common.VmDefinition;
import org.jdrupes.vmoperator.manager.events.VmChannel; import org.jdrupes.vmoperator.manager.events.VmChannel;
import org.jdrupes.vmoperator.util.DataPath;
import org.jdrupes.vmoperator.util.GsonPtr; import org.jdrupes.vmoperator.util.GsonPtr;
import org.yaml.snakeyaml.LoaderOptions; import org.yaml.snakeyaml.LoaderOptions;
import org.yaml.snakeyaml.Yaml; import org.yaml.snakeyaml.Yaml;
@ -44,7 +45,6 @@ import org.yaml.snakeyaml.constructor.SafeConstructor;
/** /**
* Delegee for reconciling the service * Delegee for reconciling the service
*/ */
@SuppressWarnings("PMD.DataflowAnomalyAnalysis")
/* default */ class LoadBalancerReconciler { /* default */ class LoadBalancerReconciler {
private static final String LOAD_BALANCER_SERVICE = "loadBalancerService"; private static final String LOAD_BALANCER_SERVICE = "loadBalancerService";
@ -85,7 +85,7 @@ import org.yaml.snakeyaml.constructor.SafeConstructor;
} }
// Check if to be generated // Check if to be generated
@SuppressWarnings({ "PMD.AvoidDuplicateLiterals", "unchecked" }) @SuppressWarnings({ "unchecked" })
var lbsDef = Optional.of(model) var lbsDef = Optional.of(model)
.map(m -> (Map<String, Object>) m.get("reconciler")) .map(m -> (Map<String, Object>) m.get("reconciler"))
.map(c -> c.get(LOAD_BALANCER_SERVICE)).orElse(Boolean.FALSE); .map(c -> c.get(LOAD_BALANCER_SERVICE)).orElse(Boolean.FALSE);
@ -107,6 +107,8 @@ import org.yaml.snakeyaml.constructor.SafeConstructor;
} }
// Combine template and data and parse result // Combine template and data and parse result
logger.fine(() -> "Create/update load balancer service for "
+ DataPath.<String> get(model, "cr", "name").orElse("unknown"));
var fmTemplate = fmConfig.getTemplate("runnerLoadBalancer.ftl.yaml"); var fmTemplate = fmConfig.getTemplate("runnerLoadBalancer.ftl.yaml");
StringWriter out = new StringWriter(); StringWriter out = new StringWriter();
fmTemplate.process(model, out); fmTemplate.process(model, out);

View file

@ -81,7 +81,7 @@ import org.jgrapes.webconsole.vuejs.VueJsConsoleWeblet;
/** /**
* The application class. * The application class.
*/ */
@SuppressWarnings({ "PMD.DataflowAnomalyAnalysis", "PMD.ExcessiveImports" }) @SuppressWarnings({ "PMD.ExcessiveImports" })
public class Manager extends Component { public class Manager extends Component {
private static String version; private static String version;
@ -97,8 +97,8 @@ public class Manager extends Component {
* @throws IOException Signals that an I/O exception has occurred. * @throws IOException Signals that an I/O exception has occurred.
* @throws URISyntaxException * @throws URISyntaxException
*/ */
@SuppressWarnings({ "PMD.TooFewBranchesForASwitchStatement", @SuppressWarnings({ "PMD.NcssCount",
"PMD.NcssCount", "PMD.ConstructorCallsOverridableMethod" }) "PMD.ConstructorCallsOverridableMethod" })
public Manager(CommandLine cmdLine) throws IOException, URISyntaxException { public Manager(CommandLine cmdLine) throws IOException, URISyntaxException {
super(new NamedChannel("manager")); super(new NamedChannel("manager"));
// Prepare component tree // Prepare component tree
@ -217,7 +217,6 @@ public class Manager extends Component {
* @param event the event * @param event the event
*/ */
@Handler @Handler
@SuppressWarnings("PMD.DataflowAnomalyAnalysis")
public void onConfigurationUpdate(ConfigurationUpdate event) { public void onConfigurationUpdate(ConfigurationUpdate event) {
event.structured(componentPath()).ifPresent(c -> { event.structured(componentPath()).ifPresent(c -> {
if (c.containsKey("clusterName")) { if (c.containsKey("clusterName")) {
@ -264,7 +263,7 @@ public class Manager extends Component {
*/ */
@Handler(priority = -1000) @Handler(priority = -1000)
public void onStop(Stop event) { public void onStop(Stop event) {
logger.fine(() -> "Application stopped."); logger.info(() -> "Application stopped.");
} }
static { static {
@ -291,7 +290,6 @@ public class Manager extends Component {
* @param args the arguments * @param args the arguments
* @throws Exception the exception * @throws Exception the exception
*/ */
@SuppressWarnings("PMD.SignatureDeclareThrowsException")
public static void main(String[] args) { public static void main(String[] args) {
try { try {
// Instance logger is not available yet. // Instance logger is not available yet.

View file

@ -43,7 +43,6 @@ import org.yaml.snakeyaml.constructor.SafeConstructor;
/** /**
* Delegee for reconciling the pod. * Delegee for reconciling the pod.
*/ */
@SuppressWarnings("PMD.DataflowAnomalyAnalysis")
/* default */ class PodReconciler { /* default */ class PodReconciler {
protected final Logger logger = Logger.getLogger(getClass().getName()); protected final Logger logger = Logger.getLogger(getClass().getName());

View file

@ -53,7 +53,6 @@ import org.jgrapes.core.events.Attached;
* {@link VmPoolChanged} events fired on a special pipeline to * {@link VmPoolChanged} events fired on a special pipeline to
* avoid concurrent change informations. * avoid concurrent change informations.
*/ */
@SuppressWarnings({ "PMD.DataflowAnomalyAnalysis", "PMD.ExcessiveImports" })
public class PoolMonitor extends public class PoolMonitor extends
AbstractMonitor<K8sDynamicModel, K8sDynamicModels, Channel> { AbstractMonitor<K8sDynamicModel, K8sDynamicModels, Channel> {

View file

@ -49,7 +49,6 @@ import org.yaml.snakeyaml.constructor.SafeConstructor;
/** /**
* Delegee for reconciling the stateful set (effectively the pod). * Delegee for reconciling the stateful set (effectively the pod).
*/ */
@SuppressWarnings("PMD.DataflowAnomalyAnalysis")
/* default */ class PvcReconciler { /* default */ class PvcReconciler {
protected final Logger logger = Logger.getLogger(getClass().getName()); protected final Logger logger = Logger.getLogger(getClass().getName());
@ -75,7 +74,7 @@ import org.yaml.snakeyaml.constructor.SafeConstructor;
* @throws TemplateException the template exception * @throws TemplateException the template exception
* @throws ApiException the api exception * @throws ApiException the api exception
*/ */
@SuppressWarnings({ "PMD.AvoidDuplicateLiterals", "unchecked" }) @SuppressWarnings({ "unchecked" })
public void reconcile(VmDefinition vmDef, Map<String, Object> model, public void reconcile(VmDefinition vmDef, Map<String, Object> model,
VmChannel channel, boolean specChanged) VmChannel channel, boolean specChanged)
throws IOException, TemplateException, ApiException { throws IOException, TemplateException, ApiException {
@ -139,6 +138,7 @@ import org.yaml.snakeyaml.constructor.SafeConstructor;
// Generate PVC // Generate PVC
var runnerDataPvcName = vmDef.name() + "-runner-data"; var runnerDataPvcName = vmDef.name() + "-runner-data";
logger.fine(() -> "Create/update pvc " + runnerDataPvcName);
model.put("runnerDataPvcName", runnerDataPvcName); model.put("runnerDataPvcName", runnerDataPvcName);
if (!specChanged) { if (!specChanged) {
// Augmenting the model is all we have to do // Augmenting the model is all we have to do
@ -180,6 +180,7 @@ import org.yaml.snakeyaml.constructor.SafeConstructor;
} }
// Generate PVC // Generate PVC
logger.fine(() -> "Create/update pvc " + pvcName);
model.put("disk", diskDef); model.put("disk", diskDef);
var fmTemplate = fmConfig.getTemplate("runnerDiskPvc.ftl.yaml"); var fmTemplate = fmConfig.getTemplate("runnerDiskPvc.ftl.yaml");
StringWriter out = new StringWriter(); StringWriter out = new StringWriter();

View file

@ -137,15 +137,13 @@ import org.jgrapes.util.events.ConfigurationUpdate;
* *
* @see org.jdrupes.vmoperator.manager.DisplaySecretReconciler * @see org.jdrupes.vmoperator.manager.DisplaySecretReconciler
*/ */
@SuppressWarnings({ "PMD.DataflowAnomalyAnalysis", @SuppressWarnings({ "PMD.AvoidDuplicateLiterals" })
"PMD.AvoidDuplicateLiterals" })
public class Reconciler extends Component { public class Reconciler extends Component {
/** The Constant mapper. */ /** The Constant mapper. */
@SuppressWarnings("PMD.FieldNamingConventions") @SuppressWarnings("PMD.FieldNamingConventions")
protected static final ObjectMapper mapper = new ObjectMapper(); protected static final ObjectMapper mapper = new ObjectMapper();
@SuppressWarnings("PMD.SingularField")
private final Configuration fmConfig; private final Configuration fmConfig;
private final ConfigMapReconciler cmReconciler; private final ConfigMapReconciler cmReconciler;
private final DisplaySecretReconciler dsReconciler; private final DisplaySecretReconciler dsReconciler;
@ -203,7 +201,6 @@ public class Reconciler extends Component {
* @throws IOException Signals that an I/O exception has occurred. * @throws IOException Signals that an I/O exception has occurred.
*/ */
@Handler @Handler
@SuppressWarnings("PMD.ConfusingTernary")
public void onVmResourceChanged(VmResourceChanged event, VmChannel channel) public void onVmResourceChanged(VmResourceChanged event, VmChannel channel)
throws ApiException, TemplateException, IOException { throws ApiException, TemplateException, IOException {
// Ownership relationships takes care of deletions // Ownership relationships takes care of deletions
@ -338,7 +335,6 @@ public class Reconciler extends Component {
private final TemplateMethodModelEx formatMemoryModel private final TemplateMethodModelEx formatMemoryModel
= new TemplateMethodModelEx() { = new TemplateMethodModelEx() {
@Override @Override
@SuppressWarnings("PMD.PreserveStackTrace")
public Object exec(@SuppressWarnings("rawtypes") List arguments) public Object exec(@SuppressWarnings("rawtypes") List arguments)
throws TemplateModelException { throws TemplateModelException {
var arg = arguments.get(0); var arg = arguments.get(0);
@ -368,8 +364,7 @@ public class Reconciler extends Component {
private final TemplateMethodModelEx imgageLocationModel private final TemplateMethodModelEx imgageLocationModel
= new TemplateMethodModelEx() { = new TemplateMethodModelEx() {
@Override @Override
@SuppressWarnings({ "PMD.PreserveStackTrace", @SuppressWarnings({ "PMD.AvoidLiteralsInIfCondition" })
"PMD.AvoidLiteralsInIfCondition" })
public Object exec(@SuppressWarnings("rawtypes") List arguments) public Object exec(@SuppressWarnings("rawtypes") List arguments)
throws TemplateModelException { throws TemplateModelException {
var image = ((SimpleScalar) arguments.get(0)).getAsString(); var image = ((SimpleScalar) arguments.get(0)).getAsString();
@ -394,7 +389,6 @@ public class Reconciler extends Component {
private final TemplateMethodModelEx toJsonModel private final TemplateMethodModelEx toJsonModel
= new TemplateMethodModelEx() { = new TemplateMethodModelEx() {
@Override @Override
@SuppressWarnings("PMD.PreserveStackTrace")
public Object exec(@SuppressWarnings("rawtypes") List arguments) public Object exec(@SuppressWarnings("rawtypes") List arguments)
throws TemplateModelException { throws TemplateModelException {
try { try {

View file

@ -22,7 +22,6 @@ import com.google.gson.JsonObject;
import io.kubernetes.client.apimachinery.GroupVersionKind; import io.kubernetes.client.apimachinery.GroupVersionKind;
import io.kubernetes.client.custom.V1Patch; import io.kubernetes.client.custom.V1Patch;
import io.kubernetes.client.openapi.ApiException; import io.kubernetes.client.openapi.ApiException;
import io.kubernetes.client.openapi.models.V1ObjectMeta;
import io.kubernetes.client.util.Watch; import io.kubernetes.client.util.Watch;
import io.kubernetes.client.util.generic.options.ListOptions; import io.kubernetes.client.util.generic.options.ListOptions;
import java.io.IOException; import java.io.IOException;
@ -32,7 +31,6 @@ import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.Optional; import java.util.Optional;
import java.util.Set; import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import org.jdrupes.vmoperator.common.Constants.Crd; import org.jdrupes.vmoperator.common.Constants.Crd;
import org.jdrupes.vmoperator.common.Constants.Status; import org.jdrupes.vmoperator.common.Constants.Status;
@ -57,16 +55,27 @@ import org.jdrupes.vmoperator.manager.events.VmResourceChanged;
import org.jdrupes.vmoperator.util.GsonPtr; import org.jdrupes.vmoperator.util.GsonPtr;
import org.jgrapes.core.Channel; import org.jgrapes.core.Channel;
import org.jgrapes.core.Event; import org.jgrapes.core.Event;
import org.jgrapes.core.EventPipeline;
import org.jgrapes.core.annotation.Handler; import org.jgrapes.core.annotation.Handler;
/** /**
* Watches for changes of VM definitions. * Watches for changes of VM definitions. When a VM definition (CR)
* becomes known, is is registered with a {@link ChannelManager} and thus
* gets an associated {@link VmChannel} and an associated
* {@link EventPipeline}.
*
* The {@link EventPipeline} is used for submitting an action that processes
* the change data from kubernetes, eventually transforming it to a
* {@link VmResourceChanged} event that is handled by another
* {@link EventPipeline} associated with the {@link VmChannel}. This
* event pipeline should be used for all events related to changes of
* a particular VM.
*/ */
@SuppressWarnings({ "PMD.DataflowAnomalyAnalysis", "PMD.ExcessiveImports" })
public class VmMonitor extends public class VmMonitor extends
AbstractMonitor<VmDefinition, VmDefinitions, VmChannel> { AbstractMonitor<VmDefinition, VmDefinitions, VmChannel> {
private final ChannelManager<String, VmChannel, ?> channelManager; private final ChannelManager<String, VmChannel,
EventPipeline> channelManager;
/** /**
* Instantiates a new VM definition watcher. * Instantiates a new VM definition watcher.
@ -75,7 +84,7 @@ public class VmMonitor extends
* @param channelManager the channel manager * @param channelManager the channel manager
*/ */
public VmMonitor(Channel componentChannel, public VmMonitor(Channel componentChannel,
ChannelManager<String, VmChannel, ?> channelManager) { ChannelManager<String, VmChannel, EventPipeline> channelManager) {
super(componentChannel, VmDefinition.class, super(componentChannel, VmDefinition.class,
VmDefinitions.class); VmDefinitions.class);
this.channelManager = channelManager; this.channelManager = channelManager;
@ -97,7 +106,6 @@ public class VmMonitor extends
purge(); purge();
} }
@SuppressWarnings("PMD.CognitiveComplexity")
private void purge() throws ApiException { private void purge() throws ApiException {
// Get existing CRs (VMs) // Get existing CRs (VMs)
var known = K8sDynamicStub.list(client(), context(), namespace()) var known = K8sDynamicStub.list(client(), context(), namespace())
@ -122,14 +130,18 @@ public class VmMonitor extends
@Override @Override
protected void handleChange(K8sClient client, protected void handleChange(K8sClient client,
Watch.Response<VmDefinition> response) { Watch.Response<VmDefinition> response) {
V1ObjectMeta metadata = response.object.getMetadata(); var name = response.object.getMetadata().getName();
AtomicBoolean toBeAdded = new AtomicBoolean(false);
VmChannel channel = channelManager.channel(metadata.getName())
.orElseGet(() -> {
toBeAdded.set(true);
return channelManager.createChannel(metadata.getName());
});
// Process the response data on a VM specific pipeline to
// increase concurrency when e.g. starting many VMs.
var preparing = channelManager.associated(name)
.orElseGet(() -> newEventPipeline());
preparing.submit("VmChange[" + name + "]",
() -> processChange(client, response, preparing));
}
private void processChange(K8sClient client,
Watch.Response<VmDefinition> response, EventPipeline preparing) {
// Get full definition and associate with channel as backup // Get full definition and associate with channel as backup
var vmDef = response.object; var vmDef = response.object;
if (vmDef.data() == null) { if (vmDef.data() == null) {
@ -137,6 +149,9 @@ public class VmMonitor extends
// https://github.com/kubernetes-client/java/issues/3215 // https://github.com/kubernetes-client/java/issues/3215
vmDef = getModel(client, vmDef); vmDef = getModel(client, vmDef);
} }
var name = response.object.getMetadata().getName();
var channel = channelManager.channel(name)
.orElseGet(() -> channelManager.createChannel(name));
if (vmDef.data() != null) { if (vmDef.data() != null) {
// New data, augment and save // New data, augment and save
addExtraData(vmDef, channel.vmDefinition()); addExtraData(vmDef, channel.vmDefinition());
@ -150,9 +165,7 @@ public class VmMonitor extends
+ response.object.getMetadata()); + response.object.getMetadata());
return; return;
} }
if (toBeAdded.get()) { channelManager.put(name, channel, preparing);
channelManager.put(vmDef.name(), channel);
}
// Create and fire changed event. Remove channel from channel // Create and fire changed event. Remove channel from channel
// manager on completion. // manager on completion.
@ -177,7 +190,6 @@ public class VmMonitor extends
} }
} }
@SuppressWarnings("PMD.AvoidDuplicateLiterals")
private void addExtraData(VmDefinition vmDef, VmDefinition prevState) { private void addExtraData(VmDefinition vmDef, VmDefinition prevState) {
var extra = new VmExtraData(vmDef); var extra = new VmExtraData(vmDef);
var prevExtra = Optional.ofNullable(prevState).map(VmDefinition::extra); var prevExtra = Optional.ofNullable(prevState).map(VmDefinition::extra);
@ -199,9 +211,16 @@ public class VmMonitor extends
@Handler @Handler
public void onPodChanged(PodChanged event, VmChannel channel) { public void onPodChanged(PodChanged event, VmChannel channel) {
var vmDef = channel.vmDefinition(); var vmDef = channel.vmDefinition();
updateNodeInfo(event, vmDef);
channel // Make sure that this is properly sync'd with VM CR changes.
.fire(new VmResourceChanged(ResponseType.MODIFIED, vmDef, false, true)); channelManager.associated(vmDef.name())
.orElseGet(() -> activeEventPipeline())
.submit("NodeInfo[" + vmDef.name() + "]",
() -> {
updateNodeInfo(event, vmDef);
channel.fire(new VmResourceChanged(ResponseType.MODIFIED,
vmDef, false, true));
});
} }
private void updateNodeInfo(PodChanged event, VmDefinition vmDef) { private void updateNodeInfo(PodChanged event, VmDefinition vmDef) {
@ -219,7 +238,6 @@ public class VmMonitor extends
.ofNullable(pod.getSpec().getNodeName()).orElse(""); .ofNullable(pod.getSpec().getNodeName()).orElse("");
logger.finer(() -> "Adding node name " + nodeName logger.finer(() -> "Adding node name " + nodeName
+ " to VM info for " + vmDef.name()); + " to VM info for " + vmDef.name());
@SuppressWarnings("PMD.AvoidInstantiatingObjectsInLoops")
var addrs = new ArrayList<String>(); var addrs = new ArrayList<String>();
Optional.ofNullable(pod.getStatus().getPodIPs()) Optional.ofNullable(pod.getStatus().getPodIPs())
.orElse(Collections.emptyList()).stream() .orElse(Collections.emptyList()).stream()

View file

@ -1,6 +1,6 @@
/* /*
* VM-Operator * VM-Operator
* Copyright (C) 2023 Michael N. Lipp * Copyright (C) 2023,2025 Michael N. Lipp
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as * it under the terms of the GNU Affero General Public License as
@ -83,8 +83,18 @@
* [YamlConfigurationStore] *-right[hidden]- [Controller] * [YamlConfigurationStore] *-right[hidden]- [Controller]
* *
* [Manager] *-- [Controller] * [Manager] *-- [Controller]
* [Controller] *-- [VmWatcher] * Component VmMonitor as VmMonitor <<internal>>
* [Controller] *-- [Reconciler] * [Controller] *-- [VmMonitor]
* [VmMonitor] -right[hidden]- [PoolMonitor]
* Component PoolMonitor as PoolMonitor <<internal>>
* [Controller] *-- [PoolMonitor]
* Component PodMonitor as PodMonitor <<internal>>
* [Controller] *-- [PodMonitor]
* [PodMonitor] -up[hidden]- VmMonitor
* Component DisplaySecretMonitor as DisplaySecretMonitor <<internal>>
* [Controller] *-- [DisplaySecretMonitor]
* [DisplaySecretMonitor] -up[hidden]- VmMonitor
* [Controller] *-left- [Reconciler]
* [Controller] -right[hidden]- [GuiHttpServer] * [Controller] -right[hidden]- [GuiHttpServer]
* *
* [Manager] *-down- [GuiSocketServer:8080] * [Manager] *-down- [GuiSocketServer:8080]

View file

@ -1,8 +1,8 @@
apiVersion: "vmoperator.jdrupes.org/v1" apiVersion: "vmoperator.jdrupes.org/v1"
kind: VirtualMachine kind: VirtualMachine
metadata: metadata:
namespace: vmop-dev namespace: vmop-test
name: unittest-vm name: test-vm
spec: spec:
image: image:
repository: docker-registry.lan.mnl.de repository: docker-registry.lan.mnl.de

View file

@ -0,0 +1,111 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- ../../deploy
namespace: vmop-test
patches:
- patch: |-
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
name: vmop-image-repository
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 10Gi
storageClassName: local-path
- patch: |-
kind: ConfigMap
apiVersion: v1
metadata:
name: vm-operator
data:
# Keep in sync with config.yaml
config.yaml: |
"/Manager":
# clusterName: "test"
"/Controller":
"/Reconciler":
runnerData:
storageClassName: null
loadBalancerService:
labels:
label1: label1
label2: toBeReplaced
annotations:
metallb.universe.tf/loadBalancerIPs: 192.168.168.1
metallb.universe.tf/ip-allocated-from-pool: single-common
metallb.universe.tf/allow-shared-ip: single-common
"/GuiSocketServer":
port: 8888
"/GuiHttpServer":
# This configures the GUI
"/ConsoleWeblet":
"/WebConsole":
"/LoginConlet":
users:
- name: admin
fullName: Administrator
password: "$2b$05$NiBd74ZGdplLC63ePZf1f.UtjMKkbQ23cQoO2OKOFalDBHWAOy21."
- name: test1
fullName: Test Account
password: "$2b$05$hZaI/jToXf/d3BctZdT38Or7H7h6Pn2W3WiB49p5AyhDHFkkYCvo2"
- name: test2
fullName: Test Account
password: "$2b$05$hZaI/jToXf/d3BctZdT38Or7H7h6Pn2W3WiB49p5AyhDHFkkYCvo2"
- name: test3
fullName: Test Account
password: "$2b$05$hZaI/jToXf/d3BctZdT38Or7H7h6Pn2W3WiB49p5AyhDHFkkYCvo2"
"/RoleConfigurator":
rolesByUser:
# User admin has role admin
admin:
- admin
test1:
- user
test2:
- user
test3:
- user
# All users have role other
"*":
- other
replace: false
"/RoleConletFilter":
conletTypesByRole:
# Admins can use all conlets
admin:
- "*"
user:
- org.jdrupes.vmoperator.vmviewer.VmViewer
# Others cannot use any conlet (except login conlet to log out)
other:
- org.jgrapes.webconlet.locallogin.LoginConlet
"/ComponentCollector":
"/VmAccess":
displayResource:
preferredIpVersion: ipv4
syncPreviewsFor:
- role: user
- target:
group: apps
version: v1
kind: Deployment
name: vm-operator
patch: |-
- op: replace
path: /spec/template/spec/containers/0/image
value: docker-registry.lan.mnl.de/vmoperator/org.jdrupes.vmoperator.manager:test
- op: replace
path: /spec/template/spec/containers/0/imagePullPolicy
value: Always
- op: replace
path: /spec/replicas
value: 0

View file

@ -41,7 +41,7 @@ class BasicTests {
private static APIResource vmsContext; private static APIResource vmsContext;
private static K8sV1DeploymentStub mgrDeployment; private static K8sV1DeploymentStub mgrDeployment;
private static K8sDynamicStub vmStub; private static K8sDynamicStub vmStub;
private static final String VM_NAME = "unittest-vm"; private static final String VM_NAME = "test-vm";
private static final Object EXISTS = new Object(); private static final Object EXISTS = new Object();
@BeforeAll @BeforeAll
@ -54,7 +54,7 @@ class BasicTests {
// Update manager pod by scaling deployment // Update manager pod by scaling deployment
mgrDeployment mgrDeployment
= K8sV1DeploymentStub.get(client, "vmop-dev", "vm-operator"); = K8sV1DeploymentStub.get(client, "vmop-test", "vm-operator");
mgrDeployment.scale(0); mgrDeployment.scale(0);
mgrDeployment.scale(1); mgrDeployment.scale(1);
waitForManager(); waitForManager();
@ -65,13 +65,13 @@ class BasicTests {
vmsContext = apiRes.get(); vmsContext = apiRes.get();
// Cleanup existing VM // Cleanup existing VM
K8sDynamicStub.get(client, vmsContext, "vmop-dev", VM_NAME) K8sDynamicStub.get(client, vmsContext, "vmop-test", VM_NAME)
.delete(); .delete();
ListOptions listOpts = new ListOptions(); ListOptions listOpts = new ListOptions();
listOpts.setLabelSelector("app.kubernetes.io/name=" + APP_NAME + "," listOpts.setLabelSelector("app.kubernetes.io/name=" + APP_NAME + ","
+ "app.kubernetes.io/instance=" + VM_NAME + "," + "app.kubernetes.io/instance=" + VM_NAME + ","
+ "app.kubernetes.io/component=" + DisplaySecret.NAME); + "app.kubernetes.io/component=" + DisplaySecret.NAME);
var secrets = K8sV1SecretStub.list(client, "vmop-dev", listOpts); var secrets = K8sV1SecretStub.list(client, "vmop-test", listOpts);
for (var secret : secrets) { for (var secret : secrets) {
secret.delete(); secret.delete();
} }
@ -103,7 +103,7 @@ class BasicTests {
"app.kubernetes.io/managed-by=" + VM_OP_NAME + "," "app.kubernetes.io/managed-by=" + VM_OP_NAME + ","
+ "app.kubernetes.io/name=" + APP_NAME + "," + "app.kubernetes.io/name=" + APP_NAME + ","
+ "app.kubernetes.io/instance=" + VM_NAME); + "app.kubernetes.io/instance=" + VM_NAME);
var knownPvcs = K8sV1PvcStub.list(client, "vmop-dev", listOpts); var knownPvcs = K8sV1PvcStub.list(client, "vmop-test", listOpts);
for (var pvc : knownPvcs) { for (var pvc : knownPvcs) {
pvc.delete(); pvc.delete();
} }
@ -112,7 +112,7 @@ class BasicTests {
@AfterAll @AfterAll
static void tearDownAfterClass() throws Exception { static void tearDownAfterClass() throws Exception {
// Cleanup // Cleanup
K8sDynamicStub.get(client, vmsContext, "vmop-dev", VM_NAME) K8sDynamicStub.get(client, vmsContext, "vmop-test", VM_NAME)
.delete(); .delete();
deletePvcs(); deletePvcs();
@ -124,7 +124,7 @@ class BasicTests {
void testConfigMap() void testConfigMap()
throws IOException, InterruptedException, ApiException { throws IOException, InterruptedException, ApiException {
K8sV1ConfigMapStub stub K8sV1ConfigMapStub stub
= K8sV1ConfigMapStub.get(client, "vmop-dev", VM_NAME); = K8sV1ConfigMapStub.get(client, "vmop-test", VM_NAME);
for (int i = 0; i < 10; i++) { for (int i = 0; i < 10; i++) {
if (stub.model().isPresent()) { if (stub.model().isPresent()) {
break; break;
@ -134,7 +134,7 @@ class BasicTests {
// Check config map // Check config map
var config = stub.model().get(); var config = stub.model().get();
Map<List<? extends Object>, Object> toCheck = Map.of( Map<List<? extends Object>, Object> toCheck = Map.of(
List.of("namespace"), "vmop-dev", List.of("namespace"), "vmop-test",
List.of("name"), VM_NAME, List.of("name"), VM_NAME,
List.of("labels", "app.kubernetes.io/name"), Constants.APP_NAME, List.of("labels", "app.kubernetes.io/name"), Constants.APP_NAME,
List.of("labels", "app.kubernetes.io/instance"), VM_NAME, List.of("labels", "app.kubernetes.io/instance"), VM_NAME,
@ -191,7 +191,7 @@ class BasicTests {
+ "app.kubernetes.io/component=" + DisplaySecret.NAME); + "app.kubernetes.io/component=" + DisplaySecret.NAME);
Collection<K8sV1SecretStub> secrets = null; Collection<K8sV1SecretStub> secrets = null;
for (int i = 0; i < 10; i++) { for (int i = 0; i < 10; i++) {
secrets = K8sV1SecretStub.list(client, "vmop-dev", listOpts); secrets = K8sV1SecretStub.list(client, "vmop-test", listOpts);
if (secrets.size() > 0) { if (secrets.size() > 0) {
break; break;
} }
@ -207,7 +207,7 @@ class BasicTests {
@Test @Test
void testRunnerPvc() throws ApiException, InterruptedException { void testRunnerPvc() throws ApiException, InterruptedException {
var stub var stub
= K8sV1PvcStub.get(client, "vmop-dev", VM_NAME + "-runner-data"); = K8sV1PvcStub.get(client, "vmop-test", VM_NAME + "-runner-data");
for (int i = 0; i < 10; i++) { for (int i = 0; i < 10; i++) {
if (stub.model().isPresent()) { if (stub.model().isPresent()) {
break; break;
@ -227,7 +227,7 @@ class BasicTests {
@Test @Test
void testSystemDiskPvc() throws ApiException, InterruptedException { void testSystemDiskPvc() throws ApiException, InterruptedException {
var stub var stub
= K8sV1PvcStub.get(client, "vmop-dev", VM_NAME + "-system-disk"); = K8sV1PvcStub.get(client, "vmop-test", VM_NAME + "-system-disk");
for (int i = 0; i < 10; i++) { for (int i = 0; i < 10; i++) {
if (stub.model().isPresent()) { if (stub.model().isPresent()) {
break; break;
@ -248,7 +248,7 @@ class BasicTests {
@Test @Test
void testDisk1Pvc() throws ApiException, InterruptedException { void testDisk1Pvc() throws ApiException, InterruptedException {
var stub var stub
= K8sV1PvcStub.get(client, "vmop-dev", VM_NAME + "-disk-1"); = K8sV1PvcStub.get(client, "vmop-test", VM_NAME + "-disk-1");
for (int i = 0; i < 10; i++) { for (int i = 0; i < 10; i++) {
if (stub.model().isPresent()) { if (stub.model().isPresent()) {
break; break;
@ -274,7 +274,7 @@ class BasicTests {
new V1Patch("[{\"op\": \"replace\", \"path\": \"/spec/vm/state" new V1Patch("[{\"op\": \"replace\", \"path\": \"/spec/vm/state"
+ "\", \"value\": \"Running\"}]"), + "\", \"value\": \"Running\"}]"),
client.defaultPatchOptions()).isPresent()); client.defaultPatchOptions()).isPresent());
var stub = K8sV1PodStub.get(client, "vmop-dev", VM_NAME); var stub = K8sV1PodStub.get(client, "vmop-test", VM_NAME);
for (int i = 0; i < 20; i++) { for (int i = 0; i < 20; i++) {
if (stub.model().isPresent()) { if (stub.model().isPresent()) {
break; break;
@ -303,7 +303,7 @@ class BasicTests {
@Test @Test
public void testLoadBalancer() throws ApiException, InterruptedException { public void testLoadBalancer() throws ApiException, InterruptedException {
var stub = K8sV1ServiceStub.get(client, "vmop-dev", VM_NAME); var stub = K8sV1ServiceStub.get(client, "vmop-test", VM_NAME);
for (int i = 0; i < 10; i++) { for (int i = 0; i < 10; i++) {
if (stub.model().isPresent()) { if (stub.model().isPresent()) {
break; break;

View file

@ -36,7 +36,6 @@ import org.jgrapes.core.annotation.Handler;
/** /**
* The Class CdMediaController. * The Class CdMediaController.
*/ */
@SuppressWarnings("PMD.DataflowAnomalyAnalysis")
public class CdMediaController extends Component { public class CdMediaController extends Component {
/** /**
@ -55,7 +54,6 @@ public class CdMediaController extends Component {
* *
* @param componentChannel the component channel * @param componentChannel the component channel
*/ */
@SuppressWarnings("PMD.AssignmentToNonFinalStatic")
public CdMediaController(Channel componentChannel) { public CdMediaController(Channel componentChannel) {
super(componentChannel); super(componentChannel);
} }
@ -66,8 +64,7 @@ public class CdMediaController extends Component {
* @param event the event * @param event the event
*/ */
@Handler @Handler
@SuppressWarnings({ "PMD.AvoidLiteralsInIfCondition", @SuppressWarnings({ "PMD.AvoidInstantiatingObjectsInLoops" })
"PMD.AvoidInstantiatingObjectsInLoops" })
public void onConfigureQemu(ConfigureQemu event) { public void onConfigureQemu(ConfigureQemu event) {
if (event.runState() == RunState.TERMINATING) { if (event.runState() == RunState.TERMINATING) {
return; return;

View file

@ -39,11 +39,9 @@ import org.jdrupes.vmoperator.util.FsdUtils;
/** /**
* The configuration information from the configuration file. * The configuration information from the configuration file.
*/ */
@SuppressWarnings({ "PMD.ExcessivePublicCount", "PMD.TooManyFields" })
public class Configuration implements Dto { public class Configuration implements Dto {
private static final String CI_INSTANCE_ID = "instance-id"; private static final String CI_INSTANCE_ID = "instance-id";
@SuppressWarnings("PMD.FieldNamingConventions")
protected final Logger logger = Logger.getLogger(getClass().getName()); protected final Logger logger = Logger.getLogger(getClass().getName());
/** Configuration timestamp. */ /** Configuration timestamp. */
@ -95,15 +93,12 @@ public class Configuration implements Dto {
public static class CloudInit implements Dto { public static class CloudInit implements Dto {
/** The meta data. */ /** The meta data. */
@SuppressWarnings("PMD.UseConcurrentHashMap")
public Map<String, Object> metaData; public Map<String, Object> metaData;
/** The user data. */ /** The user data. */
@SuppressWarnings("PMD.UseConcurrentHashMap")
public Map<String, Object> userData; public Map<String, Object> userData;
/** The network config. */ /** The network config. */
@SuppressWarnings("PMD.UseConcurrentHashMap")
public Map<String, Object> networkConfig; public Map<String, Object> networkConfig;
} }
@ -299,7 +294,6 @@ public class Configuration implements Dto {
return true; return true;
} }
@SuppressWarnings("PMD.AvoidLiteralsInIfCondition")
private void checkDrives() { private void checkDrives() {
for (Drive drive : vm.drives) { for (Drive drive : vm.drives) {
if (drive.file != null || drive.device != null if (drive.file != null || drive.device != null
@ -319,7 +313,6 @@ public class Configuration implements Dto {
} }
} }
@SuppressWarnings("PMD.AvoidDeeplyNestedIfStmts")
private boolean checkRuntimeDir() { private boolean checkRuntimeDir() {
// Runtime directory (sockets etc.) // Runtime directory (sockets etc.)
if (runtimeDir == null) { if (runtimeDir == null) {
@ -355,7 +348,6 @@ public class Configuration implements Dto {
return true; return true;
} }
@SuppressWarnings("PMD.AvoidDeeplyNestedIfStmts")
private boolean checkDataDir() { private boolean checkDataDir() {
// Data directory // Data directory
if (dataDir == null) { if (dataDir == null) {

View file

@ -41,7 +41,6 @@ import org.jgrapes.core.events.Start;
* A (sub)component that updates the console status in the CR status. * A (sub)component that updates the console status in the CR status.
* Created as child of {@link StatusUpdater}. * Created as child of {@link StatusUpdater}.
*/ */
@SuppressWarnings("PMD.DataflowAnomalyAnalysis")
public class ConsoleTracker extends VmDefUpdater { public class ConsoleTracker extends VmDefUpdater {
private VmDefinitionStub vmStub; private VmDefinitionStub vmStub;
@ -53,7 +52,6 @@ public class ConsoleTracker extends VmDefUpdater {
* *
* @param componentChannel the component channel * @param componentChannel the component channel
*/ */
@SuppressWarnings("PMD.ConstructorCallsOverridableMethod")
public ConsoleTracker(Channel componentChannel) { public ConsoleTracker(Channel componentChannel) {
super(componentChannel); super(componentChannel);
apiClient = (K8sClient) io.kubernetes.client.openapi.Configuration apiClient = (K8sClient) io.kubernetes.client.openapi.Configuration
@ -91,8 +89,7 @@ public class ConsoleTracker extends VmDefUpdater {
* @throws ApiException the api exception * @throws ApiException the api exception
*/ */
@Handler @Handler
@SuppressWarnings({ "PMD.AvoidLiteralsInIfCondition", @SuppressWarnings({ "PMD.AvoidLiteralsInIfCondition" })
"PMD.AvoidDuplicateLiterals" })
public void onSpiceInitialized(SpiceInitializedEvent event) public void onSpiceInitialized(SpiceInitializedEvent event)
throws ApiException { throws ApiException {
if (vmStub == null) { if (vmStub == null) {
@ -127,7 +124,6 @@ public class ConsoleTracker extends VmDefUpdater {
* @throws ApiException the api exception * @throws ApiException the api exception
*/ */
@Handler @Handler
@SuppressWarnings("PMD.AvoidDuplicateLiterals")
public void onSpiceDisconnected(SpiceDisconnectedEvent event) public void onSpiceDisconnected(SpiceDisconnectedEvent event)
throws ApiException { throws ApiException {
if (vmStub == null) { if (vmStub == null) {

View file

@ -21,7 +21,6 @@ package org.jdrupes.vmoperator.runner.qemu;
/** /**
* Some constants. * Some constants.
*/ */
@SuppressWarnings("PMD.DataClass")
public class Constants extends org.jdrupes.vmoperator.common.Constants { public class Constants extends org.jdrupes.vmoperator.common.Constants {
/** /**

View file

@ -41,7 +41,6 @@ import org.jgrapes.core.annotation.Handler;
/** /**
* The Class CpuController. * The Class CpuController.
*/ */
@SuppressWarnings("PMD.DataflowAnomalyAnalysis")
public class CpuController extends Component { public class CpuController extends Component {
private Integer currentCpus; private Integer currentCpus;

View file

@ -43,7 +43,6 @@ import org.jgrapes.util.events.WatchFile;
/** /**
* The Class DisplayController. * The Class DisplayController.
*/ */
@SuppressWarnings("PMD.DataflowAnomalyAnalysis")
public class DisplayController extends Component { public class DisplayController extends Component {
private String currentPassword; private String currentPassword;
@ -59,8 +58,7 @@ public class DisplayController extends Component {
* @param componentChannel the component channel * @param componentChannel the component channel
* @param configDir * @param configDir
*/ */
@SuppressWarnings({ "PMD.AssignmentToNonFinalStatic", @SuppressWarnings({ "PMD.ConstructorCallsOverridableMethod" })
"PMD.ConstructorCallsOverridableMethod" })
public DisplayController(Channel componentChannel, Path configDir) { public DisplayController(Channel componentChannel, Path configDir) {
super(componentChannel); super(componentChannel);
this.configDir = configDir; this.configDir = configDir;
@ -114,7 +112,6 @@ public class DisplayController extends Component {
* @param event the event * @param event the event
*/ */
@Handler @Handler
@SuppressWarnings("PMD.EmptyCatchBlock")
public void onFileChanged(FileChanged event) { public void onFileChanged(FileChanged event) {
if (event.path().equals(configDir.resolve(DisplaySecret.PASSWORD))) { if (event.path().equals(configDir.resolve(DisplaySecret.PASSWORD))) {
logger.fine(() -> "Display password updated"); logger.fine(() -> "Display password updated");

View file

@ -69,14 +69,14 @@ public class GuestAgentClient extends AgentConnector {
*/ */
@Override @Override
protected void agentConnected() { protected void agentConnected() {
logger.fine(() -> "guest agent connected"); logger.fine(() -> "Guest agent connected");
connected = true; connected = true;
rep().fire(new GuestAgentCommand(new QmpGuestGetOsinfo())); rep().fire(new GuestAgentCommand(new QmpGuestGetOsinfo()));
} }
@Override @Override
protected void agentDisconnected() { protected void agentDisconnected() {
logger.fine(() -> "guest agent disconnected"); logger.fine(() -> "Guest agent disconnected");
connected = false; connected = false;
} }
@ -88,15 +88,16 @@ public class GuestAgentClient extends AgentConnector {
*/ */
@Override @Override
protected void processInput(String line) throws IOException { protected void processInput(String line) throws IOException {
logger.fine(() -> "guest agent(in): " + line); logger.finer(() -> "guest agent(in): " + line);
try { try {
var response = mapper.readValue(line, ObjectNode.class); var response = mapper.readValue(line, ObjectNode.class);
if (response.has("return") || response.has("error")) { if (response.has("return") || response.has("error")) {
QmpCommand executed = executing.poll(); QmpCommand executed = executing.poll();
logger.fine(() -> String.format("(Previous \"guest agent(in)\"" logger.finer(() -> String.format("(Previous \"guest agent(in)\""
+ " is result from executing %s)", executed)); + " is result from executing %s)", executed));
if (executed instanceof QmpGuestGetOsinfo) { if (executed instanceof QmpGuestGetOsinfo) {
var osInfo = new OsinfoEvent(response.get("return")); var osInfo = new OsinfoEvent(response.get("return"));
logger.fine(() -> "Guest agent triggers: " + osInfo);
rep().fire(osInfo); rep().fire(osInfo);
} }
} }
@ -120,10 +121,11 @@ public class GuestAgentClient extends AgentConnector {
return; return;
} }
var command = event.command(); var command = event.command();
logger.fine(() -> "guest agent(out): " + command.toString()); logger.fine(() -> "Guest handles: " + event);
String asText; String asText;
try { try {
asText = command.asText(); asText = command.asText();
logger.finer(() -> "guest agent(out): " + asText);
} catch (JsonProcessingException e) { } catch (JsonProcessingException e) {
logger.log(Level.SEVERE, e, logger.log(Level.SEVERE, e,
() -> "Cannot serialize Json: " + e.getMessage()); () -> "Cannot serialize Json: " + e.getMessage());
@ -163,8 +165,8 @@ public class GuestAgentClient extends AgentConnector {
} }
event.suspendHandling(); event.suspendHandling();
suspendedStop = event; suspendedStop = event;
logger.fine(() -> "Sending powerdown command, waiting for" logger.fine(() -> "Attempting shutdown through guest agent,"
+ " termination until " + waitUntil); + " waiting for termination until " + waitUntil);
powerdownTimer = Components.schedule(t -> { powerdownTimer = Components.schedule(t -> {
logger.fine(() -> "Powerdown timeout reached."); logger.fine(() -> "Powerdown timeout reached.");
synchronized (this) { synchronized (this) {

View file

@ -54,7 +54,6 @@ import org.jgrapes.util.events.ConfigurationUpdate;
* If the log level for this class is set to fine, the messages * If the log level for this class is set to fine, the messages
* exchanged on the monitor socket are logged. * exchanged on the monitor socket are logged.
*/ */
@SuppressWarnings("PMD.DataflowAnomalyAnalysis")
public class QemuMonitor extends QemuConnector { public class QemuMonitor extends QemuConnector {
private int powerdownTimeout; private int powerdownTimeout;
@ -72,8 +71,6 @@ public class QemuMonitor extends QemuConnector {
* @param configDir the config dir * @param configDir the config dir
* @throws IOException Signals that an I/O exception has occurred. * @throws IOException Signals that an I/O exception has occurred.
*/ */
@SuppressWarnings({ "PMD.AssignmentToNonFinalStatic",
"PMD.ConstructorCallsOverridableMethod" })
public QemuMonitor(Channel componentChannel, Path configDir) public QemuMonitor(Channel componentChannel, Path configDir)
throws IOException { throws IOException {
super(componentChannel); super(componentChannel);
@ -108,24 +105,30 @@ public class QemuMonitor extends QemuConnector {
@Override @Override
protected void processInput(String line) protected void processInput(String line)
throws IOException { throws IOException {
logger.fine(() -> "monitor(in): " + line); logger.finer(() -> "monitor(in): " + line);
try { try {
var response = mapper.readValue(line, ObjectNode.class); var response = mapper.readValue(line, ObjectNode.class);
if (response.has("QMP")) { if (response.has("QMP")) {
monitorReady = true; monitorReady = true;
logger.fine(() -> "QMP connection ready");
rep().fire(new MonitorReady()); rep().fire(new MonitorReady());
return; return;
} }
if (response.has("return") || response.has("error")) { if (response.has("return") || response.has("error")) {
QmpCommand executed = executing.poll(); QmpCommand executed = executing.poll();
logger.fine( logger.finer(
() -> String.format("(Previous \"monitor(in)\" is result " () -> String.format("(Previous \"monitor(in)\" is result "
+ "from executing %s)", executed)); + "from executing %s)", executed));
rep().fire(MonitorResult.from(executed, response)); var monRes = MonitorResult.from(executed, response);
logger.fine(() -> "QMP triggers: " + monRes);
rep().fire(monRes);
return; return;
} }
if (response.has("event")) { if (response.has("event")) {
MonitorEvent.from(response).ifPresent(rep()::fire); MonitorEvent.from(response).ifPresent(me -> {
logger.fine(() -> "QMP triggers: " + me);
rep().fire(me);
});
} }
} catch (JsonProcessingException e) { } catch (JsonProcessingException e) {
throw new IOException(e); throw new IOException(e);
@ -141,7 +144,7 @@ public class QemuMonitor extends QemuConnector {
public void onClosed(Closed<?> event, SocketIOChannel channel) { public void onClosed(Closed<?> event, SocketIOChannel channel) {
channel.associated(this, getClass()).ifPresent(qm -> { channel.associated(this, getClass()).ifPresent(qm -> {
super.onClosed(event, channel); super.onClosed(event, channel);
logger.finer(() -> "QMP socket closed."); logger.fine(() -> "QMP connection closed.");
monitorReady = false; monitorReady = false;
}); });
} }
@ -158,7 +161,7 @@ public class QemuMonitor extends QemuConnector {
public void onMonitorCommand(MonitorCommand event) throws IOException { public void onMonitorCommand(MonitorCommand event) throws IOException {
// Check prerequisites // Check prerequisites
if (!monitorReady && !(event.command() instanceof QmpCapabilities)) { if (!monitorReady && !(event.command() instanceof QmpCapabilities)) {
logger.severe(() -> "Premature monitor command (not ready): " logger.severe(() -> "Premature QMP command (not ready): "
+ event.command()); + event.command());
rep().fire(new Stop()); rep().fire(new Stop());
return; return;
@ -166,10 +169,11 @@ public class QemuMonitor extends QemuConnector {
// Send the command // Send the command
var command = event.command(); var command = event.command();
logger.fine(() -> "monitor(out): " + command.toString()); logger.fine(() -> "QMP handles: " + event.toString());
String asText; String asText;
try { try {
asText = command.asText(); asText = command.asText();
logger.finer(() -> "monitor(out): " + asText);
} catch (JsonProcessingException e) { } catch (JsonProcessingException e) {
logger.log(Level.SEVERE, e, logger.log(Level.SEVERE, e,
() -> "Cannot serialize Json: " + e.getMessage()); () -> "Cannot serialize Json: " + e.getMessage());
@ -192,8 +196,8 @@ public class QemuMonitor extends QemuConnector {
@SuppressWarnings("PMD.AvoidSynchronizedStatement") @SuppressWarnings("PMD.AvoidSynchronizedStatement")
public void onStop(Stop event) { public void onStop(Stop event) {
if (!monitorReady) { if (!monitorReady) {
logger.fine(() -> "No QMP connection," logger.fine(() -> "Not sending QMP powerdown command"
+ " cannot send powerdown command"); + " because QMP connection is closed");
return; return;
} }

View file

@ -39,7 +39,6 @@ public class RamController extends Component {
* *
* @param componentChannel the component channel * @param componentChannel the component channel
*/ */
@SuppressWarnings("PMD.AssignmentToNonFinalStatic")
public RamController(Channel componentChannel) { public RamController(Channel componentChannel) {
super(componentChannel); super(componentChannel);
} }

View file

@ -157,6 +157,15 @@ import org.jgrapes.util.events.WatchFile;
* *
* success --> Running * success --> Running
* *
* state Running {
* state Booting
* state Booted
*
* [*] -right-> Booting
* Booting -down-> Booting: VserportChanged[guest agent connected]/fire GetOsinfo
* Booting --> Booted: Osinfo
* }
*
* state Terminating { * state Terminating {
* state terminate <<entryPoint>> * state terminate <<entryPoint>>
* state qemuRunning <<choice>> * state qemuRunning <<choice>>
@ -192,8 +201,7 @@ import org.jgrapes.util.events.WatchFile;
* *
*/ */
@SuppressWarnings({ "PMD.ExcessiveImports", "PMD.AvoidPrintStackTrace", @SuppressWarnings({ "PMD.ExcessiveImports", "PMD.AvoidPrintStackTrace",
"PMD.DataflowAnomalyAnalysis", "PMD.TooManyMethods", "PMD.TooManyMethods", "PMD.CouplingBetweenObjects" })
"PMD.CouplingBetweenObjects", "PMD.TooManyFields" })
public class Runner extends Component { public class Runner extends Component {
private static final String TEMPLATE_DIR private static final String TEMPLATE_DIR
@ -209,7 +217,6 @@ public class Runner extends Component {
.builder().disable(YAMLGenerator.Feature.WRITE_DOC_START_MARKER) .builder().disable(YAMLGenerator.Feature.WRITE_DOC_START_MARKER)
.build()); .build());
private final JsonNode defaults; private final JsonNode defaults;
@SuppressWarnings("PMD.UseConcurrentHashMap")
private final File configFile; private final File configFile;
private final Path configDir; private final Path configDir;
private Configuration initialConfig; private Configuration initialConfig;
@ -241,8 +248,7 @@ public class Runner extends Component {
* @param cmdLine the cmd line * @param cmdLine the cmd line
* @throws IOException Signals that an I/O exception has occurred. * @throws IOException Signals that an I/O exception has occurred.
*/ */
@SuppressWarnings({ "PMD.SystemPrintln", @SuppressWarnings({ "PMD.ConstructorCallsOverridableMethod" })
"PMD.ConstructorCallsOverridableMethod" })
public Runner(CommandLine cmdLine) throws IOException { public Runner(CommandLine cmdLine) throws IOException {
yamlMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, yamlMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES,
false); false);
@ -378,8 +384,6 @@ public class Runner extends Component {
} }
} }
@SuppressWarnings({ "PMD.CognitiveComplexity",
"PMD.DataflowAnomalyAnalysis" })
private void setFirmwarePaths(Configuration config) throws IOException { private void setFirmwarePaths(Configuration config) throws IOException {
JsonNode firmware = defaults.path("firmware").path(config.vm.firmware); JsonNode firmware = defaults.path("firmware").path(config.vm.firmware);
// Get file for firmware ROM // Get file for firmware ROM
@ -611,8 +615,6 @@ public class Runner extends Component {
* @throws InterruptedException the interrupted exception * @throws InterruptedException the interrupted exception
*/ */
@Handler @Handler
@SuppressWarnings({ "PMD.SwitchStmtsShouldHaveDefault",
"PMD.TooFewBranchesForASwitchStatement" })
public void onProcessStarted(ProcessStarted event, ProcessChannel channel) public void onProcessStarted(ProcessStarted event, ProcessChannel channel)
throws InterruptedException { throws InterruptedException {
event.startEvent().associated(CommandDefinition.class) event.startEvent().associated(CommandDefinition.class)
@ -769,7 +771,6 @@ public class Runner extends Component {
"The VM has been shut down")); "The VM has been shut down"));
} }
@SuppressWarnings("PMD.ConfusingArgumentToVarargsMethod")
private void shutdown() { private void shutdown() {
if (!Set.of(RunState.TERMINATING, RunState.STOPPED).contains(state)) { if (!Set.of(RunState.TERMINATING, RunState.STOPPED).contains(state)) {
fire(new Stop()); fire(new Stop());

View file

@ -66,8 +66,7 @@ import org.jgrapes.core.events.Start;
/** /**
* Updates the CR status. * Updates the CR status.
*/ */
@SuppressWarnings({ "PMD.DataflowAnomalyAnalysis", @SuppressWarnings({ "PMD.CouplingBetweenObjects" })
"PMD.CouplingBetweenObjects" })
public class StatusUpdater extends VmDefUpdater { public class StatusUpdater extends VmDefUpdater {
@SuppressWarnings("PMD.FieldNamingConventions") @SuppressWarnings("PMD.FieldNamingConventions")
@ -90,7 +89,6 @@ public class StatusUpdater extends VmDefUpdater {
* *
* @param componentChannel the component channel * @param componentChannel the component channel
*/ */
@SuppressWarnings("PMD.ConstructorCallsOverridableMethod")
public StatusUpdater(Channel componentChannel) { public StatusUpdater(Channel componentChannel) {
super(componentChannel); super(componentChannel);
attach(new ConsoleTracker(componentChannel)); attach(new ConsoleTracker(componentChannel));
@ -153,7 +151,6 @@ public class StatusUpdater extends VmDefUpdater {
* @throws ApiException * @throws ApiException
*/ */
@Handler @Handler
@SuppressWarnings("PMD.AvoidDuplicateLiterals")
public void onConfigureQemu(ConfigureQemu event) public void onConfigureQemu(ConfigureQemu event)
throws ApiException { throws ApiException {
guestShutdownStops = event.configuration().guestShutdownStops; guestShutdownStops = event.configuration().guestShutdownStops;
@ -187,8 +184,7 @@ public class StatusUpdater extends VmDefUpdater {
* @throws ApiException * @throws ApiException
*/ */
@Handler @Handler
@SuppressWarnings({ "PMD.AvoidLiteralsInIfCondition", @SuppressWarnings({ "PMD.AssignmentInOperand" })
"PMD.AssignmentInOperand", "PMD.AvoidDuplicateLiterals" })
public void onRunnerStateChanged(RunnerStateChange event) public void onRunnerStateChanged(RunnerStateChange event)
throws ApiException { throws ApiException {
VmDefinition vmDef; VmDefinition vmDef;
@ -425,7 +421,6 @@ public class StatusUpdater extends VmDefUpdater {
* @throws ApiException * @throws ApiException
*/ */
@Handler @Handler
@SuppressWarnings("PMD.AssignmentInOperand")
public void onVmopAgentLoggedIn(VmopAgentLoggedIn event) public void onVmopAgentLoggedIn(VmopAgentLoggedIn event)
throws ApiException { throws ApiException {
vmStub.updateStatus(from -> { vmStub.updateStatus(from -> {
@ -442,7 +437,6 @@ public class StatusUpdater extends VmDefUpdater {
* @throws ApiException * @throws ApiException
*/ */
@Handler @Handler
@SuppressWarnings("PMD.AssignmentInOperand")
public void onVmopAgentLoggedOut(VmopAgentLoggedOut event) public void onVmopAgentLoggedOut(VmopAgentLoggedOut event)
throws ApiException { throws ApiException {
vmStub.updateStatus(from -> { vmStub.updateStatus(from -> {

View file

@ -43,7 +43,6 @@ import org.jgrapes.util.events.InitialConfiguration;
/** /**
* Updates the CR status. * Updates the CR status.
*/ */
@SuppressWarnings("PMD.DataflowAnomalyAnalysis")
public class VmDefUpdater extends Component { public class VmDefUpdater extends Component {
protected String namespace; protected String namespace;

View file

@ -59,10 +59,14 @@ public class VmopAgentClient extends AgentConnector {
*/ */
@Handler @Handler
public void onVmopAgentLogIn(VmopAgentLogIn event) throws IOException { public void onVmopAgentLogIn(VmopAgentLogIn event) throws IOException {
logger.fine(() -> "vmop agent(out): login " + event.user());
if (writer().isPresent()) { if (writer().isPresent()) {
logger.fine(() -> "Vmop agent handles:" + event);
executing.add(event); executing.add(event);
logger.finer(() -> "vmop agent(out): login " + event.user());
sendCommand("login " + event.user()); sendCommand("login " + event.user());
} else {
logger
.warning(() -> "No vmop agent connection for sending " + event);
} }
} }
@ -74,34 +78,38 @@ public class VmopAgentClient extends AgentConnector {
*/ */
@Handler @Handler
public void onVmopAgentLogout(VmopAgentLogOut event) throws IOException { public void onVmopAgentLogout(VmopAgentLogOut event) throws IOException {
logger.fine(() -> "vmop agent(out): logout");
if (writer().isPresent()) { if (writer().isPresent()) {
logger.fine(() -> "Vmop agent handles:" + event);
executing.add(event); executing.add(event);
logger.finer(() -> "vmop agent(out): logout");
sendCommand("logout"); sendCommand("logout");
} }
} }
@Override @Override
@SuppressWarnings({ "PMD.UnnecessaryReturn", @SuppressWarnings({ "PMD.AvoidLiteralsInIfCondition" })
"PMD.AvoidLiteralsInIfCondition" })
protected void processInput(String line) throws IOException { protected void processInput(String line) throws IOException {
logger.fine(() -> "vmop agent(in): " + line); logger.finer(() -> "vmop agent(in): " + line);
// Check validity // Check validity
if (line.isEmpty() || !Character.isDigit(line.charAt(0))) { if (line.isEmpty() || !Character.isDigit(line.charAt(0))) {
logger.warning(() -> "Illegal response: " + line); logger.warning(() -> "Illegal vmop agent response: " + line);
return; return;
} }
// Check positive responses // Check positive responses
if (line.startsWith("220 ")) { if (line.startsWith("220 ")) {
rep().fire(new VmopAgentConnected()); var evt = new VmopAgentConnected();
logger.fine(() -> "Vmop agent triggers " + evt);
rep().fire(evt);
return; return;
} }
if (line.startsWith("201 ")) { if (line.startsWith("201 ")) {
Event<?> cmd = executing.pop(); Event<?> cmd = executing.pop();
if (cmd instanceof VmopAgentLogIn login) { if (cmd instanceof VmopAgentLogIn login) {
rep().fire(new VmopAgentLoggedIn(login)); var evt = new VmopAgentLoggedIn(login);
logger.fine(() -> "Vmop agent triggers " + evt);
rep().fire(evt);
} else { } else {
logger.severe(() -> "Response " + line logger.severe(() -> "Response " + line
+ " does not match executing command " + cmd); + " does not match executing command " + cmd);
@ -111,7 +119,9 @@ public class VmopAgentClient extends AgentConnector {
if (line.startsWith("202 ")) { if (line.startsWith("202 ")) {
Event<?> cmd = executing.pop(); Event<?> cmd = executing.pop();
if (cmd instanceof VmopAgentLogOut logout) { if (cmd instanceof VmopAgentLogOut logout) {
rep().fire(new VmopAgentLoggedOut(logout)); var evt = new VmopAgentLoggedOut(logout);
logger.fine(() -> "Vmop agent triggers " + evt);
rep().fire(evt);
} else { } else {
logger.severe(() -> "Response " + line logger.severe(() -> "Response " + line
+ "does not match executing command " + cmd); + "does not match executing command " + cmd);
@ -125,7 +135,7 @@ public class VmopAgentClient extends AgentConnector {
} }
// Error // Error
logger.warning(() -> "Error response: " + line); logger.warning(() -> "Error response from vmop agent: " + line);
executing.pop(); executing.pop();
} }

View file

@ -25,8 +25,7 @@ import com.fasterxml.jackson.databind.JsonNode;
*/ */
public class QmpCapabilities extends QmpCommand { public class QmpCapabilities extends QmpCommand {
@SuppressWarnings({ "PMD.FieldNamingConventions", @SuppressWarnings({ "PMD.FieldNamingConventions" })
"PMD.VariableNamingConventions" })
private static final JsonNode jsonTemplate private static final JsonNode jsonTemplate
= parseJson("{ \"execute\": \"qmp_capabilities\" }"); = parseJson("{ \"execute\": \"qmp_capabilities\" }");

View file

@ -27,8 +27,7 @@ import com.fasterxml.jackson.databind.node.ObjectNode;
*/ */
public class QmpChangeMedium extends QmpCommand { public class QmpChangeMedium extends QmpCommand {
@SuppressWarnings({ "PMD.FieldNamingConventions", @SuppressWarnings({ "PMD.FieldNamingConventions" })
"PMD.VariableNamingConventions" })
private static final JsonNode jsonTemplate private static final JsonNode jsonTemplate
= parseJson("{ \"execute\": \"blockdev-change-medium\",\"arguments\": {" = parseJson("{ \"execute\": \"blockdev-change-medium\",\"arguments\": {"
+ "\"id\": \"\",\"filename\": \"\",\"format\": \"raw\"," + "\"id\": \"\",\"filename\": \"\",\"format\": \"raw\","

View file

@ -30,8 +30,7 @@ import java.util.logging.Logger;
*/ */
public abstract class QmpCommand { public abstract class QmpCommand {
@SuppressWarnings({ "PMD.FieldNamingConventions", @SuppressWarnings({ "PMD.FieldNamingConventions" })
"PMD.VariableNamingConventions" })
protected static final ObjectMapper mapper = new ObjectMapper(); protected static final ObjectMapper mapper = new ObjectMapper();
/** /**

View file

@ -25,8 +25,7 @@ import com.fasterxml.jackson.databind.JsonNode;
*/ */
public class QmpCont extends QmpCommand { public class QmpCont extends QmpCommand {
@SuppressWarnings({ "PMD.FieldNamingConventions", @SuppressWarnings({ "PMD.FieldNamingConventions" })
"PMD.VariableNamingConventions" })
private static final JsonNode jsonTemplate private static final JsonNode jsonTemplate
= parseJson("{ \"execute\": \"cont\" }"); = parseJson("{ \"execute\": \"cont\" }");

View file

@ -27,8 +27,7 @@ import com.fasterxml.jackson.databind.node.ObjectNode;
*/ */
public class QmpDelCpu extends QmpCommand { public class QmpDelCpu extends QmpCommand {
@SuppressWarnings({ "PMD.FieldNamingConventions", @SuppressWarnings({ "PMD.FieldNamingConventions" })
"PMD.VariableNamingConventions" })
private static final JsonNode jsonTemplate private static final JsonNode jsonTemplate
= parseJson("{ \"execute\": \"device_del\", " = parseJson("{ \"execute\": \"device_del\", "
+ "\"arguments\": " + "{ \"id\": 0 } }"); + "\"arguments\": " + "{ \"id\": 0 } }");

View file

@ -27,8 +27,7 @@ import com.fasterxml.jackson.databind.node.ObjectNode;
*/ */
public class QmpOpenTray extends QmpCommand { public class QmpOpenTray extends QmpCommand {
@SuppressWarnings({ "PMD.FieldNamingConventions", @SuppressWarnings({ "PMD.FieldNamingConventions" })
"PMD.VariableNamingConventions" })
private static final JsonNode jsonTemplate private static final JsonNode jsonTemplate
= parseJson("{ \"execute\": \"blockdev-open-tray\",\"arguments\": {" = parseJson("{ \"execute\": \"blockdev-open-tray\",\"arguments\": {"
+ "\"id\": \"\" } }"); + "\"id\": \"\" } }");

View file

@ -25,8 +25,7 @@ import com.fasterxml.jackson.databind.JsonNode;
*/ */
public class QmpPowerdown extends QmpCommand { public class QmpPowerdown extends QmpCommand {
@SuppressWarnings({ "PMD.FieldNamingConventions", @SuppressWarnings({ "PMD.FieldNamingConventions" })
"PMD.VariableNamingConventions" })
private static final JsonNode jsonTemplate private static final JsonNode jsonTemplate
= parseJson("{ \"execute\": \"system_powerdown\" }"); = parseJson("{ \"execute\": \"system_powerdown\" }");

View file

@ -25,8 +25,7 @@ import com.fasterxml.jackson.databind.JsonNode;
*/ */
public class QmpQueryHotpluggableCpus extends QmpCommand { public class QmpQueryHotpluggableCpus extends QmpCommand {
@SuppressWarnings({ "PMD.FieldNamingConventions", @SuppressWarnings({ "PMD.FieldNamingConventions" })
"PMD.VariableNamingConventions" })
private static final JsonNode jsonTemplate = parseJson( private static final JsonNode jsonTemplate = parseJson(
"{\"execute\":\"query-hotpluggable-cpus\",\"arguments\":{}}"); "{\"execute\":\"query-hotpluggable-cpus\",\"arguments\":{}}");

View file

@ -27,8 +27,7 @@ import com.fasterxml.jackson.databind.node.ObjectNode;
*/ */
public class QmpRemoveMedium extends QmpCommand { public class QmpRemoveMedium extends QmpCommand {
@SuppressWarnings({ "PMD.FieldNamingConventions", @SuppressWarnings({ "PMD.FieldNamingConventions" })
"PMD.VariableNamingConventions" })
private static final JsonNode jsonTemplate private static final JsonNode jsonTemplate
= parseJson("{ \"execute\": \"blockdev-remove-medium\",\"arguments\": {" = parseJson("{ \"execute\": \"blockdev-remove-medium\",\"arguments\": {"
+ "\"id\": \"\" } }"); + "\"id\": \"\" } }");

View file

@ -25,8 +25,7 @@ import com.fasterxml.jackson.databind.JsonNode;
*/ */
public class QmpReset extends QmpCommand { public class QmpReset extends QmpCommand {
@SuppressWarnings({ "PMD.FieldNamingConventions", @SuppressWarnings({ "PMD.FieldNamingConventions" })
"PMD.VariableNamingConventions" })
private static final JsonNode jsonTemplate private static final JsonNode jsonTemplate
= parseJson("{ \"execute\": \"system_reset\" }"); = parseJson("{ \"execute\": \"system_reset\" }");

View file

@ -28,8 +28,7 @@ import java.math.BigInteger;
*/ */
public class QmpSetBalloon extends QmpCommand { public class QmpSetBalloon extends QmpCommand {
@SuppressWarnings({ "PMD.FieldNamingConventions", @SuppressWarnings({ "PMD.FieldNamingConventions" })
"PMD.VariableNamingConventions" })
private static final JsonNode jsonTemplate private static final JsonNode jsonTemplate
= parseJson("{ \"execute\": \"balloon\", " = parseJson("{ \"execute\": \"balloon\", "
+ "\"arguments\": " + "{ \"value\": 0 } }"); + "\"arguments\": " + "{ \"value\": 0 } }");

View file

@ -20,6 +20,8 @@ package org.jdrupes.vmoperator.runner.qemu.events;
import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.JsonNode;
import java.util.Optional; import java.util.Optional;
import org.jgrapes.core.Channel;
import org.jgrapes.core.Components;
import org.jgrapes.core.Event; import org.jgrapes.core.Event;
/** /**
@ -47,7 +49,6 @@ public class MonitorEvent extends Event<Void> {
* @param response the response * @param response the response
* @return the optional * @return the optional
*/ */
@SuppressWarnings("PMD.TooFewBranchesForASwitchStatement")
public static Optional<MonitorEvent> from(JsonNode response) { public static Optional<MonitorEvent> from(JsonNode response) {
try { try {
var kind = Kind.valueOf(response.get("event").asText()); var kind = Kind.valueOf(response.get("event").asText());
@ -112,4 +113,20 @@ public class MonitorEvent extends Event<Void> {
public JsonNode data() { public JsonNode data() {
return data; return data;
} }
/*
* (non-Javadoc)
*
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
builder.append(Components.objectName(this)).append(" [").append(data);
if (channels() != null) {
builder.append(", channels=").append(Channel.toString(channels()));
}
builder.append(']');
return builder.toString();
}
} }

View file

@ -19,6 +19,8 @@
package org.jdrupes.vmoperator.runner.qemu.events; package org.jdrupes.vmoperator.runner.qemu.events;
import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.JsonNode;
import org.jgrapes.core.Channel;
import org.jgrapes.core.Components;
import org.jgrapes.core.Event; import org.jgrapes.core.Event;
/** /**
@ -40,4 +42,21 @@ public class OsinfoEvent extends Event<Void> {
public JsonNode osinfo() { public JsonNode osinfo() {
return osinfo; return osinfo;
} }
/*
* (non-Javadoc)
*
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
builder.append(Components.objectName(this)).append(" [")
.append(osinfo);
if (channels() != null) {
builder.append(", channels=").append(Channel.toString(channels()));
}
builder.append(']');
return builder.toString();
}
} }

View file

@ -26,7 +26,6 @@ import org.jgrapes.core.Event;
/** /**
* The Class RunnerStateChange. * The Class RunnerStateChange.
*/ */
@SuppressWarnings("PMD.DataClass")
public class RunnerStateChange extends Event<Void> { public class RunnerStateChange extends Event<Void> {
/** /**

View file

@ -32,7 +32,6 @@ import java.util.logging.Logger;
*/ */
public final class DataPath { public final class DataPath {
@SuppressWarnings("PMD.FieldNamingConventions")
private static final Logger logger private static final Logger logger
= Logger.getLogger(DataPath.class.getName()); = Logger.getLogger(DataPath.class.getName());
@ -56,7 +55,6 @@ public final class DataPath {
* @param selectors the selectors * @param selectors the selectors
* @return the result * @return the result
*/ */
@SuppressWarnings("PMD.UseLocaleWithCaseConversions")
public static <T> Optional<T> get(Object from, Object... selectors) { public static <T> Optional<T> get(Object from, Object... selectors) {
Object cur = from; Object cur = from;
for (var selector : selectors) { for (var selector : selectors) {
@ -132,7 +130,6 @@ public final class DataPath {
@SuppressWarnings({ "PMD.CognitiveComplexity", "unchecked" }) @SuppressWarnings({ "PMD.CognitiveComplexity", "unchecked" })
public static <T> T deepCopy(T object) { public static <T> T deepCopy(T object) {
if (object instanceof Map map) { if (object instanceof Map map) {
@SuppressWarnings("PMD.UseConcurrentHashMap")
Map<Object, Object> copy; Map<Object, Object> copy;
try { try {
copy = (Map<Object, Object>) object.getClass().getConstructor() copy = (Map<Object, Object>) object.getClass().getConstructor()

View file

@ -32,8 +32,7 @@ import java.util.function.Supplier;
/** /**
* Utility class for pointing to elements on a Gson (Json) tree. * Utility class for pointing to elements on a Gson (Json) tree.
*/ */
@SuppressWarnings({ "PMD.DataflowAnomalyAnalysis", @SuppressWarnings({ "PMD.ClassWithOnlyPrivateConstructorsShouldBeFinal" })
"PMD.ClassWithOnlyPrivateConstructorsShouldBeFinal", "PMD.GodClass" })
public class GsonPtr { public class GsonPtr {
private final JsonElement position; private final JsonElement position;
@ -102,7 +101,7 @@ public class GsonPtr {
* @param selectors the selectors * @param selectors the selectors
* @return the Gson pointer * @return the Gson pointer
*/ */
@SuppressWarnings({ "PMD.ShortMethodName", "PMD.PreserveStackTrace" }) @SuppressWarnings({ "PMD.PreserveStackTrace" })
public Optional<GsonPtr> get(Object... selectors) { public Optional<GsonPtr> get(Object... selectors) {
JsonElement element = position; JsonElement element = position;
for (Object sel : selectors) { for (Object sel : selectors) {
@ -146,7 +145,6 @@ public class GsonPtr {
* @param cls the cls * @param cls the cls
* @return the result * @return the result
*/ */
@SuppressWarnings({ "PMD.AvoidBranchingStatementAsLastInLoop" })
public <T extends JsonElement> T getAs(Class<T> cls) { public <T extends JsonElement> T getAs(Class<T> cls) {
if (cls.isAssignableFrom(position.getClass())) { if (cls.isAssignableFrom(position.getClass())) {
return cls.cast(position); return cls.cast(position);

View file

@ -111,9 +111,8 @@ import org.jgrapes.webconsole.base.freemarker.FreeMarkerConlet;
* users and roles. * users and roles.
* *
*/ */
@SuppressWarnings({ "PMD.DataflowAnomalyAnalysis", "PMD.ExcessiveImports", @SuppressWarnings({ "PMD.ExcessiveImports", "PMD.CouplingBetweenObjects",
"PMD.CouplingBetweenObjects", "PMD.GodClass", "PMD.TooManyMethods", "PMD.GodClass", "PMD.TooManyMethods", "PMD.CyclomaticComplexity" })
"PMD.CyclomaticComplexity" })
public class VmAccess extends FreeMarkerConlet<VmAccess.ResourceModel> { public class VmAccess extends FreeMarkerConlet<VmAccess.ResourceModel> {
private static final String VM_NAME_PROPERTY = "vmName"; private static final String VM_NAME_PROPERTY = "vmName";
@ -167,7 +166,7 @@ public class VmAccess extends FreeMarkerConlet<VmAccess.ResourceModel> {
* *
* @param event the event * @param event the event
*/ */
@SuppressWarnings({ "unchecked", "PMD.AvoidDuplicateLiterals" }) @SuppressWarnings({ "unchecked" })
@Handler @Handler
public void onConfigurationUpdate(ConfigurationUpdate event) { public void onConfigurationUpdate(ConfigurationUpdate event) {
event.structured(componentPath()) event.structured(componentPath())
@ -267,7 +266,7 @@ public class VmAccess extends FreeMarkerConlet<VmAccess.ResourceModel> {
public void onConsoleConfigured(ConsoleConfigured event, public void onConsoleConfigured(ConsoleConfigured event,
ConsoleConnection connection) throws InterruptedException, ConsoleConnection connection) throws InterruptedException,
IOException { IOException {
@SuppressWarnings({ "unchecked", "PMD.PrematureDeclaration" }) @SuppressWarnings({ "unchecked" })
final var rendered final var rendered
= (Set<ResourceModel>) connection.session().get(RENDERED); = (Set<ResourceModel>) connection.session().get(RENDERED);
connection.session().remove(RENDERED); connection.session().remove(RENDERED);
@ -277,8 +276,7 @@ public class VmAccess extends FreeMarkerConlet<VmAccess.ResourceModel> {
addMissingConlets(event, connection, rendered); addMissingConlets(event, connection, rendered);
} }
@SuppressWarnings({ "PMD.AvoidInstantiatingObjectsInLoops", @SuppressWarnings({ "PMD.AvoidInstantiatingObjectsInLoops" })
"PMD.AvoidDuplicateLiterals" })
private void addMissingConlets(ConsoleConfigured event, private void addMissingConlets(ConsoleConfigured event,
ConsoleConnection connection, final Set<ResourceModel> rendered) ConsoleConnection connection, final Set<ResourceModel> rendered)
throws InterruptedException { throws InterruptedException {
@ -406,7 +404,6 @@ public class VmAccess extends FreeMarkerConlet<VmAccess.ResourceModel> {
} }
@Override @Override
@SuppressWarnings({ "PMD.AvoidInstantiatingObjectsInLoops" })
protected Set<RenderMode> doRenderConlet(RenderConletRequestBase<?> event, protected Set<RenderMode> doRenderConlet(RenderConletRequestBase<?> event,
ConsoleConnection channel, String conletId, ResourceModel model) ConsoleConnection channel, String conletId, ResourceModel model)
throws Exception { throws Exception {
@ -655,9 +652,8 @@ public class VmAccess extends FreeMarkerConlet<VmAccess.ResourceModel> {
* @throws InterruptedException * @throws InterruptedException
*/ */
@Handler(namedChannels = "manager") @Handler(namedChannels = "manager")
@SuppressWarnings({ "PMD.ConfusingTernary", "PMD.CognitiveComplexity", @SuppressWarnings({ "PMD.CognitiveComplexity",
"PMD.AvoidInstantiatingObjectsInLoops", "PMD.AvoidDuplicateLiterals", "PMD.AvoidInstantiatingObjectsInLoops" })
"PMD.ConfusingArgumentToVarargsMethod" })
public void onVmResourceChanged(VmResourceChanged event, VmChannel channel) public void onVmResourceChanged(VmResourceChanged event, VmChannel channel)
throws IOException, InterruptedException { throws IOException, InterruptedException {
var vmDef = event.vmDefinition(); var vmDef = event.vmDefinition();
@ -853,8 +849,7 @@ public class VmAccess extends FreeMarkerConlet<VmAccess.ResourceModel> {
model.getConletId(), "openConsole", cf))); model.getConletId(), "openConsole", cf)));
} }
@SuppressWarnings({ "PMD.AvoidLiteralsInIfCondition", @SuppressWarnings({ "PMD.UseLocaleWithCaseConversions" })
"PMD.UseLocaleWithCaseConversions" })
private void selectResource(NotifyConletModel event, private void selectResource(NotifyConletModel event,
ConsoleConnection channel, ResourceModel model) ConsoleConnection channel, ResourceModel model)
throws JsonProcessingException, InterruptedException { throws JsonProcessingException, InterruptedException {
@ -881,7 +876,6 @@ public class VmAccess extends FreeMarkerConlet<VmAccess.ResourceModel> {
/** /**
* The Class AccessModel. * The Class AccessModel.
*/ */
@SuppressWarnings("PMD.DataClass")
public static class ResourceModel extends ConletBaseModel { public static class ResourceModel extends ConletBaseModel {
/** /**

View file

@ -30,7 +30,7 @@
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
<template v-for="(entry, rowIndex) in filteredData"> <template v-for="(entry, rowIndex) in filteredData" :key="entry.name">
<tr :class="[(rowIndex % 2) ? 'odd' : 'even']" <tr :class="[(rowIndex % 2) ? 'odd' : 'even']"
:aria-expanded="(entry.name in detailsByName) ? 'true' : 'false'"> :aria-expanded="(entry.name in detailsByName) ? 'true' : 'false'">
<td v-for="key in controller.keys" <td v-for="key in controller.keys"

View file

@ -28,7 +28,6 @@ import java.util.List;
/** /**
* The Class TimeSeries. * The Class TimeSeries.
*/ */
@SuppressWarnings("PMD.DataflowAnomalyAnalysis")
public class TimeSeries { public class TimeSeries {
@SuppressWarnings("PMD.LooseCoupling") @SuppressWarnings("PMD.LooseCoupling")

View file

@ -75,8 +75,7 @@ import org.jgrapes.webconsole.base.freemarker.FreeMarkerConlet;
/** /**
* The Class {@link VmMgmt}. * The Class {@link VmMgmt}.
*/ */
@SuppressWarnings({ "PMD.DataflowAnomalyAnalysis", "PMD.CouplingBetweenObjects", @SuppressWarnings({ "PMD.CouplingBetweenObjects", "PMD.ExcessiveImports" })
"PMD.ExcessiveImports" })
public class VmMgmt extends FreeMarkerConlet<VmMgmt.VmsModel> { public class VmMgmt extends FreeMarkerConlet<VmMgmt.VmsModel> {
private Class<?> preferredIpVersion = Inet4Address.class; private Class<?> preferredIpVersion = Inet4Address.class;
@ -112,7 +111,7 @@ public class VmMgmt extends FreeMarkerConlet<VmMgmt.VmsModel> {
* *
* @param event the event * @param event the event
*/ */
@SuppressWarnings({ "unchecked", "PMD.AvoidDuplicateLiterals" }) @SuppressWarnings({ "unchecked" })
@Handler @Handler
public void onConfigurationUpdate(ConfigurationUpdate event) { public void onConfigurationUpdate(ConfigurationUpdate event) {
event.structured("/Manager/GuiHttpServer" event.structured("/Manager/GuiHttpServer"
@ -177,7 +176,6 @@ public class VmMgmt extends FreeMarkerConlet<VmMgmt.VmsModel> {
} }
@Override @Override
@SuppressWarnings("PMD.AvoidInstantiatingObjectsInLoops")
protected Set<RenderMode> doRenderConlet(RenderConletRequestBase<?> event, protected Set<RenderMode> doRenderConlet(RenderConletRequestBase<?> event,
ConsoleConnection channel, String conletId, VmsModel conletState) ConsoleConnection channel, String conletId, VmsModel conletState)
throws Exception { throws Exception {
@ -230,7 +228,6 @@ public class VmMgmt extends FreeMarkerConlet<VmMgmt.VmsModel> {
simplifiedVmDefinition(vmDef, user, roles))); simplifiedVmDefinition(vmDef, user, roles)));
} }
@SuppressWarnings("PMD.AvoidDuplicateLiterals")
private Map<String, Object> simplifiedVmDefinition(VmDefinition vmDef, private Map<String, Object> simplifiedVmDefinition(VmDefinition vmDef,
String user, List<String> roles) { String user, List<String> roles) {
// Convert RAM sizes to unitless numbers // Convert RAM sizes to unitless numbers
@ -270,9 +267,8 @@ public class VmMgmt extends FreeMarkerConlet<VmMgmt.VmsModel> {
* @throws IOException * @throws IOException
*/ */
@Handler(namedChannels = "manager") @Handler(namedChannels = "manager")
@SuppressWarnings({ "PMD.ConfusingTernary", "PMD.CognitiveComplexity", @SuppressWarnings({ "PMD.CognitiveComplexity",
"PMD.AvoidInstantiatingObjectsInLoops", "PMD.AvoidDuplicateLiterals", "PMD.AvoidInstantiatingObjectsInLoops" })
"PMD.ConfusingArgumentToVarargsMethod" })
public void onVmResourceChanged(VmResourceChanged event, VmChannel channel) public void onVmResourceChanged(VmResourceChanged event, VmChannel channel)
throws IOException { throws IOException {
var vmName = event.vmDefinition().name(); var vmName = event.vmDefinition().name();
@ -378,8 +374,6 @@ public class VmMgmt extends FreeMarkerConlet<VmMgmt.VmsModel> {
} }
@SuppressWarnings({ "PMD.AvoidLiteralsInIfCondition",
"PMD.LambdaCanBeMethodReference" })
private Summary evaluateSummary(boolean force) { private Summary evaluateSummary(boolean force) {
if (!force && cachedSummary != null) { if (!force && cachedSummary != null) {
return cachedSummary; return cachedSummary;
@ -402,8 +396,7 @@ public class VmMgmt extends FreeMarkerConlet<VmMgmt.VmsModel> {
} }
@Override @Override
@SuppressWarnings({ "PMD.AvoidDecimalLiteralsInBigDecimalConstructor", @SuppressWarnings({ "PMD.NcssCount" })
"PMD.NcssCount" })
protected void doUpdateConletState(NotifyConletModel event, protected void doUpdateConletState(NotifyConletModel event,
ConsoleConnection channel, VmsModel model) throws Exception { ConsoleConnection channel, VmsModel model) throws Exception {
event.stop(); event.stop();

Binary file not shown.

Before

Width:  |  Height:  |  Size: 42 KiB

After

Width:  |  Height:  |  Size: 69 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 14 KiB

Before After
Before After

View file

@ -0,0 +1 @@
<script defer src="https://gotit.mnl.de/script.js" data-website-id="14b277ad-d330-4a54-82f1-a77d111240ac"></script>

View file

@ -8,6 +8,7 @@
<link rel="stylesheet" href="stylesheets/styles.css"> <link rel="stylesheet" href="stylesheets/styles.css">
<link rel="stylesheet" href="stylesheets/pygment_trac.css"> <link rel="stylesheet" href="stylesheets/pygment_trac.css">
<meta name="viewport" content="width=device-width"> <meta name="viewport" content="width=device-width">
{% include umami.html %}
<!--[if lt IE 9]> <!--[if lt IE 9]>
<script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script> <script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script>
<![endif]--> <![endif]-->

View file

@ -12,10 +12,10 @@ layout: vm-operator
An overview display shows the current CPU and RAM usage and a graph An overview display shows the current CPU and RAM usage and a graph
with recent changes. with recent changes.
![VM-Operator GUI](VM-Operator-GUI-preview.png) ![VM-Operator admin GUI preview](VM-Operator-GUI-preview.png)
The detail display lists all VMs. From here you can start and stop The detail display lists all VMs. From here you can start and stop
the VMs and adjust the CPU and RAM usages (modifies the definition the VMs and adjust the CPU and RAM usages (modifies the definition
in kubernetes). in kubernetes).
![VM-Operator GUI](VM-Operator-GUI-view.png) ![VM-Operator admin GUI view](VM-Operator-GUI-view.png)

View file

@ -9,7 +9,7 @@ layout: vm-operator
When users log into the web GUI, they have already authenticated with the When users log into the web GUI, they have already authenticated with the
VM-Operator. In some environments, requiring an additional login on the VM-Operator. In some environments, requiring an additional login on the
guest OS can be cumbersome. To enhance the user experience, the VM-Operator guest OS can be annoying. To enhance the user experience, the VM-Operator
supports automatic login on the guest operating system, thus eliminating supports automatic login on the guest operating system, thus eliminating
the need for multiple logins. However, this feature requires specific the need for multiple logins. However, this feature requires specific
support from the guest OS. support from the guest OS.
@ -18,9 +18,9 @@ support from the guest OS.
Automatic login requires an agent running inside the guest OS. Similar Automatic login requires an agent running inside the guest OS. Similar
to QEMU's standard guest agent, the VM-Operator agent communicates with to QEMU's standard guest agent, the VM-Operator agent communicates with
the host via a tty device (`/dev/virtio-ports/org.jdrupes.vmop_agent.0`). On the host via a tty device (provided in the guest as
modern Linux systems, `udev` can detect this device and trigger the start `/dev/virtio-ports/org.jdrupes.vmop_agent.0`). On modern Linux systems, `udev` can
of an associated systemd service. detect this device and trigger the start of an associated systemd service.
Sample configuration files for a VM-Operator agent are available Sample configuration files for a VM-Operator agent are available
[here](https://github.com/mnlipp/VM-Operator/tree/main/dev-example/vmop-agent). [here](https://github.com/mnlipp/VM-Operator/tree/main/dev-example/vmop-agent).

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 588 KiB

After

Width:  |  Height:  |  Size: 591 KiB

Before After
Before After

View file

@ -9,7 +9,7 @@ layout: vm-operator
# Welcome to VM-Operator # Welcome to VM-Operator
![Overview picture](index-pic.svg) ![VM-Operator summary picture](index-pic.svg)
This project provides an easy to use and flexible solution for This project provides an easy to use and flexible solution for
running QEMU/KVM based virtual machines (VMs) in Kubernetes pods. running QEMU/KVM based virtual machines (VMs) in Kubernetes pods.

View file

@ -46,16 +46,20 @@ spec:
The `retention` specifies how long the assignment of a VM from the pool to The `retention` specifies how long the assignment of a VM from the pool to
a user remains valid after the user closes the console. This ensures that a user remains valid after the user closes the console. This ensures that
a user can resume work within this timeframe without the risk of another a user can resume work within this timeframe without the risk of another
user taking over the VM. The time is specified as user taking over the VM. The time is specified as an
[ISO 8601 duration](https://en.wikipedia.org/wiki/ISO_8601#Durations). [ISO 8601 duration](https://en.wikipedia.org/wiki/ISO_8601#Durations).
Specifying an ISO 8601 time is also supported, but if you consider
using an absolute time, check again whether a dedicated VM for the user
isn't the more appropriate choice.
Setting `loginOnAssignment` to `true` triggers automatic login of the Setting `loginOnAssignment` to `true` (defaults to `false`) triggers automatic
user (as described in [section auto login](auto-login.html)) when login of the user (as described in [section auto login](auto-login.html))
the VM is assigned. The `permissions` property specifies the actions when the VM is assigned. The `permissions` property specifies the actions
that users or roles can perform on assigned VMs. that users or roles can perform on assigned VMs. The `may` property defaults
to `[accessConsole]` if not specified.
VMs become members of one (or more) pools by adding the pool name to VMs become members of one (or more) pools by adding the pool name to
the `spec.pools` array, as shown below: the `spec.pools` array in the VM definition, as shown below:
```yaml ```yaml
apiVersion: "vmoperator.jdrupes.org/v1" apiVersion: "vmoperator.jdrupes.org/v1"

Some files were not shown because too many files have changed in this diff Show more