parent
6491742eb0
commit
ae3941707a
86 changed files with 12225 additions and 514 deletions
|
|
@ -9,6 +9,6 @@ trim_trailing_whitespace = true
|
|||
indent_size = 2
|
||||
indent_style = space
|
||||
|
||||
[*.gradle]
|
||||
[*.{gradle,js,ts}]
|
||||
indent_size = 4
|
||||
indent_style = space
|
||||
|
|
|
|||
2
.github/workflows/codeql.yml
vendored
2
.github/workflows/codeql.yml
vendored
|
|
@ -2,7 +2,7 @@ name: "CodeQL"
|
|||
|
||||
on:
|
||||
push:
|
||||
branches: [ "main" ]
|
||||
branches: [ "*" ]
|
||||
pull_request:
|
||||
# The branches below must be a subset of the branches above
|
||||
branches: [ "main" ]
|
||||
|
|
|
|||
4
.gitignore
vendored
4
.gitignore
vendored
|
|
@ -9,3 +9,7 @@ bin
|
|||
.classpath
|
||||
.project
|
||||
org.eclipse.jdt.core.prefs
|
||||
**/.externalToolBuilders/
|
||||
|
||||
# Node
|
||||
**/node_modules/
|
||||
|
|
|
|||
BIN
VM-Operator.png
Normal file
BIN
VM-Operator.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 25 KiB |
|
|
@ -10,6 +10,7 @@ plugins {
|
|||
id 'pl.allegro.tech.build.axion-release' version '1.15.0' apply false
|
||||
id 'org.jdrupes.vmoperator.java-doc-conventions'
|
||||
id 'eclipse'
|
||||
id "com.github.node-gradle.node" version "7.0.1"
|
||||
}
|
||||
|
||||
allprojects {
|
||||
|
|
|
|||
|
|
@ -1,24 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<projectDescription>
|
||||
<name>buildSrc</name>
|
||||
<comment></comment>
|
||||
<projects>
|
||||
</projects>
|
||||
<buildSpec>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.jdt.core.javabuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.buildship.core.gradleprojectbuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
</buildSpec>
|
||||
<natures>
|
||||
<nature>org.eclipse.jdt.groovy.core.groovyNature</nature>
|
||||
<nature>org.eclipse.jdt.core.javanature</nature>
|
||||
<nature>org.eclipse.buildship.core.gradleprojectnature</nature>
|
||||
</natures>
|
||||
</projectDescription>
|
||||
|
|
@ -45,7 +45,8 @@ eclipse {
|
|||
}
|
||||
|
||||
project.natures += 'org.eclipse.buildship.core.gradleprojectnature'
|
||||
project.buildCommand 'org.eclipse.buildship.core.gradleprojectbuilder'
|
||||
// Don't build, result not used by Eclipse anyway
|
||||
// project.buildCommand 'org.eclipse.buildship.core.gradleprojectbuilder'
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -58,22 +58,6 @@ java {
|
|||
}
|
||||
}
|
||||
|
||||
scmVersion {
|
||||
versionIncrementer 'incrementMinor'
|
||||
tag {
|
||||
def shortened = project.name.startsWith(project.group + ".") ?
|
||||
project.name.substring(project.group.length() + 1) : project.name
|
||||
var p = shortened.replace('.', '-') + "-"
|
||||
if (grgit.branch.current.name != "main"
|
||||
&& !grgit.branch.current.name.startsWith("release")) {
|
||||
p = p + grgit.branch.current.name.replace('/', '-') + "-"
|
||||
}
|
||||
prefix = p
|
||||
}
|
||||
}
|
||||
version = scmVersion.version
|
||||
ext.isSnapshot = version.endsWith('-SNAPSHOT')
|
||||
|
||||
jar {
|
||||
manifest {
|
||||
inputs.property("gitDescriptor", { grgit.describe(always: true) })
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
Moodle Tools Console
|
||||
Copyright (C) 2022 Michael N. Lipp
|
||||
JGrapes Event Driven Framework
|
||||
Copyright (C) 2016, 2018 Michael N. Lipp
|
||||
|
||||
This program is free software; you can redistribute it and/or modify it
|
||||
under the terms of the GNU Affero General Public License as published by
|
||||
|
|
@ -53,7 +53,7 @@
|
|||
<module name="RightCurly"/>
|
||||
<module name="RightCurly">
|
||||
<property name="option" value="alone"/>
|
||||
<property name="tokens" value="CLASS_DEF, METHOD_DEF, CTOR_DEF, LITERAL_FOR, LITERAL_WHILE, STATIC_INIT, INSTANCE_INIT"/>
|
||||
<property name="tokens" value="CLASS_DEF, METHOD_DEF, CTOR_DEF, LITERAL_FOR, LITERAL_WHILE, LITERAL_DO, STATIC_INIT, INSTANCE_INIT"/>
|
||||
</module>
|
||||
<module name="OneStatementPerLine"/>
|
||||
<module name="MultipleVariableDeclarations"/>
|
||||
|
|
@ -104,7 +104,7 @@
|
|||
<module name="NoFinalizer"/>
|
||||
<module name="AbbreviationAsWordInName">
|
||||
<property name="allowedAbbreviationLength" value="1"/>
|
||||
<property name="allowedAbbreviations" value="IO,MX,KV"/>
|
||||
<property name="allowedAbbreviations" value="IO,MX,KV,DTO"/>
|
||||
<property name="ignoreFinal" value="false"/>
|
||||
</module>
|
||||
<module name="OverloadMethodsDeclarationOrder"/>
|
||||
|
|
|
|||
|
|
@ -10,13 +10,15 @@ data:
|
|||
"/Manager": {}
|
||||
|
||||
logging.properties: |
|
||||
handlers=java.util.logging.ConsoleHandler
|
||||
handlers=java.util.logging.ConsoleHandler, \
|
||||
org.jgrapes.webconlet.logviewer.LogViewerHandler
|
||||
|
||||
org.jgrapes.level=FINE
|
||||
org.jgrapes.core.handlerTracking.level=FINER
|
||||
|
||||
org.jdrupes.vmoperator.manager.level=FINE
|
||||
|
||||
java.util.logging.ConsoleHandler.level=ALL
|
||||
java.util.logging.ConsoleHandler.formatter=java.util.logging.SimpleFormatter
|
||||
java.util.logging.SimpleFormatter.format=%1$tb %1$td %1$tT %4$s %5$s%6$s%n
|
||||
|
||||
org.jgrapes.webconlet.logviewer.LogViewerHandler.level=FINE
|
||||
|
||||
|
|
@ -1,8 +1,37 @@
|
|||
# Used for running manager outside Kubernetes.
|
||||
# Keep in sync with kustomize.yaml
|
||||
"/Manager":
|
||||
# The controller manages the VM
|
||||
"/Controller":
|
||||
namespace: vmop-dev
|
||||
"/Reconciler":
|
||||
runnerData:
|
||||
storageClassName: null
|
||||
"/GuiSocketServer":
|
||||
port: 8888
|
||||
"/GuiHttpServer":
|
||||
# This configures the GUI
|
||||
"/ConsoleWeblet":
|
||||
"/WebConsole":
|
||||
"/LoginConlet":
|
||||
users:
|
||||
admin:
|
||||
fullName: Administrator
|
||||
password: "$2b$05$NiBd74ZGdplLC63ePZf1f.UtjMKkbQ23cQoO2OKOFalDBHWAOy21."
|
||||
test:
|
||||
fullName: Test Account
|
||||
password: "$2b$05$hZaI/jToXf/d3BctZdT38Or7H7h6Pn2W3WiB49p5AyhDHFkkYCvo2"
|
||||
"/RoleConfigurator":
|
||||
rolesByUser:
|
||||
admin:
|
||||
- admin
|
||||
"*":
|
||||
- user
|
||||
replace: false
|
||||
"/RoleConletFilter":
|
||||
conletTypesByRole:
|
||||
user:
|
||||
- "!org.jgrapes.webconlet.sysinfo.SysInfoConlet"
|
||||
- "*"
|
||||
admin:
|
||||
- "*"
|
||||
|
|
|
|||
|
|
@ -6,6 +6,21 @@
|
|||
<script type="text/javascript">
|
||||
if (location.hostname.indexOf("github") !== -1) {
|
||||
document.getElementById("githubfooter").style.visibility="visible";
|
||||
var _paq = _paq || [];
|
||||
/* tracker methods like "setCustomDimension" should be called before "trackPageView" */
|
||||
_paq.push(["setDocumentTitle", document.domain + "/" + document.title]);
|
||||
_paq.push(["setCookieDomain", "*.mnlipp.github.io"]);
|
||||
_paq.push(["setDomains", ["*.mnlipp.github.io"]]);
|
||||
_paq.push(['disableCookies']);
|
||||
_paq.push(['trackPageView']);
|
||||
_paq.push(['enableLinkTracking']);
|
||||
(function() {
|
||||
var u="//piwik.mnl.de/";
|
||||
_paq.push(['setTrackerUrl', u+'piwik.php']);
|
||||
_paq.push(['setSiteId', '14']);
|
||||
var d=document, g=d.createElement('script'), s=d.getElementsByTagName('script')[0];
|
||||
g.type='text/javascript'; g.async=true; g.defer=true; g.src=u+'piwik.js'; s.parentNode.insertBefore(g,s);
|
||||
})();
|
||||
}
|
||||
</script>
|
||||
<noscript>
|
||||
|
|
|
|||
10
org.jdrupes.vmoperator.common/.checkstyle
Normal file
10
org.jdrupes.vmoperator.common/.checkstyle
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<fileset-config file-format-version="1.2.0" simple-config="false" sync-formatter="false">
|
||||
<local-check-config name="Project Checks" location="/VM-Operator/checkstyle.xml" type="project" description="">
|
||||
<additional-data name="protect-config-file" value="false"/>
|
||||
</local-check-config>
|
||||
<fileset name="all" enabled="true" check-config-name="Project Checks" local="true">
|
||||
<file-match-pattern match-pattern="." include-pattern="true"/>
|
||||
</fileset>
|
||||
</fileset-config>
|
||||
7
org.jdrupes.vmoperator.common/.eclipse-pmd
Normal file
7
org.jdrupes.vmoperator.common/.eclipse-pmd
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<eclipse-pmd xmlns="http://acanda.ch/eclipse-pmd/0.8" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://acanda.ch/eclipse-pmd/0.8 http://acanda.ch/eclipse-pmd/eclipse-pmd-0.8.xsd">
|
||||
<analysis enabled="true" />
|
||||
<rulesets>
|
||||
<ruleset name="Custom Rules" ref="moodle-tools-console/ruleset.xml" refcontext="workspace" />
|
||||
</rulesets>
|
||||
</eclipse-pmd>
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
arguments=
|
||||
auto.sync=false
|
||||
build.scans.enabled=false
|
||||
connection.gradle.distribution=GRADLE_DISTRIBUTION(WRAPPER)
|
||||
connection.project.dir=..
|
||||
eclipse.preferences.version=1
|
||||
gradle.user.home=
|
||||
java.home=
|
||||
jvm.arguments=
|
||||
offline.mode=false
|
||||
override.workspace.settings=false
|
||||
show.console.view=false
|
||||
show.executions.view=false
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
eclipse.preferences.version=1
|
||||
encoding/<project>=UTF-8
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
eclipse.preferences.version=1
|
||||
line.separator=\n
|
||||
14
org.jdrupes.vmoperator.common/build.gradle
Normal file
14
org.jdrupes.vmoperator.common/build.gradle
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
/*
|
||||
* This file was generated by the Gradle 'init' task.
|
||||
*
|
||||
* This project uses @Incubating APIs which are subject to change.
|
||||
*/
|
||||
|
||||
plugins {
|
||||
id 'org.jdrupes.vmoperator.java-library-conventions'
|
||||
}
|
||||
|
||||
dependencies {
|
||||
api project(':org.jdrupes.vmoperator.util')
|
||||
api 'io.kubernetes:client-java:[18.0.0,19)'
|
||||
}
|
||||
|
|
@ -16,7 +16,7 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.jdrupes.vmoperator.util;
|
||||
package org.jdrupes.vmoperator.common;
|
||||
|
||||
/**
|
||||
* Some constants.
|
||||
|
|
@ -16,7 +16,7 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.jdrupes.vmoperator.util;
|
||||
package org.jdrupes.vmoperator.common;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.BigInteger;
|
||||
|
|
@ -16,21 +16,26 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.jdrupes.vmoperator.util;
|
||||
package org.jdrupes.vmoperator.common;
|
||||
|
||||
import io.kubernetes.client.common.KubernetesListObject;
|
||||
import io.kubernetes.client.common.KubernetesObject;
|
||||
import io.kubernetes.client.custom.V1Patch;
|
||||
import io.kubernetes.client.openapi.ApiClient;
|
||||
import io.kubernetes.client.openapi.ApiException;
|
||||
import io.kubernetes.client.openapi.apis.ApisApi;
|
||||
import io.kubernetes.client.openapi.apis.CustomObjectsApi;
|
||||
import io.kubernetes.client.openapi.models.V1APIGroup;
|
||||
import io.kubernetes.client.openapi.models.V1ConfigMap;
|
||||
import io.kubernetes.client.openapi.models.V1ConfigMapList;
|
||||
import io.kubernetes.client.openapi.models.V1GroupVersionForDiscovery;
|
||||
import io.kubernetes.client.openapi.models.V1ObjectMeta;
|
||||
import io.kubernetes.client.openapi.models.V1PersistentVolumeClaim;
|
||||
import io.kubernetes.client.openapi.models.V1PersistentVolumeClaimList;
|
||||
import io.kubernetes.client.openapi.models.V1Pod;
|
||||
import io.kubernetes.client.openapi.models.V1PodList;
|
||||
import io.kubernetes.client.util.generic.GenericKubernetesApi;
|
||||
import io.kubernetes.client.util.generic.dynamic.DynamicKubernetesApi;
|
||||
import io.kubernetes.client.util.generic.options.DeleteOptions;
|
||||
import io.kubernetes.client.util.generic.options.PatchOptions;
|
||||
import java.util.Optional;
|
||||
|
|
@ -38,7 +43,8 @@ import java.util.Optional;
|
|||
/**
|
||||
* Helpers for K8s API.
|
||||
*/
|
||||
@SuppressWarnings({ "PMD.ShortClassName", "PMD.UseUtilityClass" })
|
||||
@SuppressWarnings({ "PMD.ShortClassName", "PMD.UseUtilityClass",
|
||||
"PMD.DataflowAnomalyAnalysis" })
|
||||
public class K8s {
|
||||
|
||||
/**
|
||||
|
|
@ -88,6 +94,45 @@ public class K8s {
|
|||
"v1", "pods", client);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the API for a custom resource.
|
||||
*
|
||||
* @param client the client
|
||||
* @param group the group
|
||||
* @param kind the kind
|
||||
* @param namespace the namespace
|
||||
* @param name the name
|
||||
* @return the dynamic kubernetes api
|
||||
* @throws ApiException the api exception
|
||||
*/
|
||||
@SuppressWarnings("PMD.UseObjectForClearerAPI")
|
||||
public static Optional<DynamicKubernetesApi> crApi(ApiClient client,
|
||||
String group, String kind, String namespace, String name)
|
||||
throws ApiException {
|
||||
var apis = new ApisApi(client).getAPIVersions();
|
||||
var crdVersions = apis.getGroups().stream()
|
||||
.filter(g -> g.getName().equals(group)).findFirst()
|
||||
.map(V1APIGroup::getVersions).stream().flatMap(l -> l.stream())
|
||||
.map(V1GroupVersionForDiscovery::getVersion).toList();
|
||||
var coa = new CustomObjectsApi(client);
|
||||
for (var crdVersion : crdVersions) {
|
||||
var crdApiRes = coa.getAPIResources(group, crdVersion)
|
||||
.getResources().stream().filter(r -> kind.equals(r.getKind()))
|
||||
.findFirst();
|
||||
if (crdApiRes.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
@SuppressWarnings("PMD.AvoidInstantiatingObjectsInLoops")
|
||||
var crApi = new DynamicKubernetesApi(group,
|
||||
crdVersion, crdApiRes.get().getName(), client);
|
||||
var customResource = crApi.get(namespace, name);
|
||||
if (customResource.isSuccess()) {
|
||||
return Optional.of(crApi);
|
||||
}
|
||||
}
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an object from its metadata.
|
||||
*
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
/*
|
||||
* VM-Operator
|
||||
* Copyright (C) 2023 Michael N. Lipp
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Classes and methods shared among the VM operator modules.
|
||||
*/
|
||||
package org.jdrupes.vmoperator.common;
|
||||
10
org.jdrupes.vmoperator.manager.events/.checkstyle
Normal file
10
org.jdrupes.vmoperator.manager.events/.checkstyle
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<fileset-config file-format-version="1.2.0" simple-config="false" sync-formatter="false">
|
||||
<local-check-config name="Project Checks" location="/VM-Operator/checkstyle.xml" type="project" description="">
|
||||
<additional-data name="protect-config-file" value="false"/>
|
||||
</local-check-config>
|
||||
<fileset name="all" enabled="true" check-config-name="Project Checks" local="true">
|
||||
<file-match-pattern match-pattern="." include-pattern="true"/>
|
||||
</fileset>
|
||||
</fileset-config>
|
||||
7
org.jdrupes.vmoperator.manager.events/.eclipse-pmd
Normal file
7
org.jdrupes.vmoperator.manager.events/.eclipse-pmd
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<eclipse-pmd xmlns="http://acanda.ch/eclipse-pmd/0.8" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://acanda.ch/eclipse-pmd/0.8 http://acanda.ch/eclipse-pmd/eclipse-pmd-0.8.xsd">
|
||||
<analysis enabled="true" />
|
||||
<rulesets>
|
||||
<ruleset name="Custom Rules" ref="moodle-tools-console/ruleset.xml" refcontext="workspace" />
|
||||
</rulesets>
|
||||
</eclipse-pmd>
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
arguments=
|
||||
auto.sync=false
|
||||
build.scans.enabled=false
|
||||
connection.gradle.distribution=GRADLE_DISTRIBUTION(WRAPPER)
|
||||
connection.project.dir=..
|
||||
eclipse.preferences.version=1
|
||||
gradle.user.home=
|
||||
java.home=
|
||||
jvm.arguments=
|
||||
offline.mode=false
|
||||
override.workspace.settings=false
|
||||
show.console.view=false
|
||||
show.executions.view=false
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
eclipse.preferences.version=1
|
||||
encoding/<project>=UTF-8
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
eclipse.preferences.version=1
|
||||
line.separator=\n
|
||||
14
org.jdrupes.vmoperator.manager.events/build.gradle
Normal file
14
org.jdrupes.vmoperator.manager.events/build.gradle
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
/*
|
||||
* This file was generated by the Gradle 'init' task.
|
||||
*
|
||||
* This project uses @Incubating APIs which are subject to change.
|
||||
*/
|
||||
|
||||
plugins {
|
||||
id 'org.jdrupes.vmoperator.java-library-conventions'
|
||||
}
|
||||
|
||||
dependencies {
|
||||
api 'org.jgrapes:org.jgrapes.core:[1.19.0,2)'
|
||||
api project(':org.jdrupes.vmoperator.common')
|
||||
}
|
||||
|
|
@ -0,0 +1,52 @@
|
|||
/*
|
||||
* VM-Operator
|
||||
* Copyright (C) 2023 Michael N. Lipp
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.jdrupes.vmoperator.manager.events;
|
||||
|
||||
import org.jgrapes.core.Channel;
|
||||
import org.jgrapes.core.Event;
|
||||
|
||||
/**
|
||||
* Starts a VM.
|
||||
*/
|
||||
@SuppressWarnings("PMD.DataClass")
|
||||
public class StartVm extends Event<Void> {
|
||||
|
||||
private final String name;
|
||||
|
||||
/**
|
||||
* Instantiates a new start vm event.
|
||||
*
|
||||
* @param channels the channels
|
||||
* @param name the name
|
||||
*/
|
||||
public StartVm(String name, Channel... channels) {
|
||||
super(channels);
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the name.
|
||||
*
|
||||
* @return the name
|
||||
*/
|
||||
public String name() {
|
||||
return name;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,52 @@
|
|||
/*
|
||||
* VM-Operator
|
||||
* Copyright (C) 2023 Michael N. Lipp
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.jdrupes.vmoperator.manager.events;
|
||||
|
||||
import org.jgrapes.core.Channel;
|
||||
import org.jgrapes.core.Event;
|
||||
|
||||
/**
|
||||
* Stops a VM.
|
||||
*/
|
||||
@SuppressWarnings("PMD.DataClass")
|
||||
public class StopVm extends Event<Void> {
|
||||
|
||||
private final String name;
|
||||
|
||||
/**
|
||||
* Instantiates a new start vm event.
|
||||
*
|
||||
* @param channels the channels
|
||||
* @param name the name
|
||||
*/
|
||||
public StopVm(String name, Channel... channels) {
|
||||
super(channels);
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the name.
|
||||
*
|
||||
* @return the name
|
||||
*/
|
||||
public String name() {
|
||||
return name;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -16,10 +16,10 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.jdrupes.vmoperator.manager;
|
||||
package org.jdrupes.vmoperator.manager.events;
|
||||
|
||||
import com.google.gson.JsonObject;
|
||||
import io.kubernetes.client.openapi.ApiClient;
|
||||
import io.kubernetes.client.util.generic.dynamic.DynamicKubernetesObject;
|
||||
import org.jgrapes.core.Channel;
|
||||
import org.jgrapes.core.EventPipeline;
|
||||
import org.jgrapes.core.Subchannel.DefaultSubchannel;
|
||||
|
|
@ -32,7 +32,7 @@ public class VmChannel extends DefaultSubchannel {
|
|||
|
||||
private final EventPipeline pipeline;
|
||||
private final ApiClient client;
|
||||
private JsonObject vmDefinition;
|
||||
private DynamicKubernetesObject vmDefinition;
|
||||
private long generation = -1;
|
||||
|
||||
/**
|
||||
|
|
@ -56,7 +56,7 @@ public class VmChannel extends DefaultSubchannel {
|
|||
* @return the watch channel
|
||||
*/
|
||||
@SuppressWarnings("PMD.LinguisticNaming")
|
||||
public VmChannel setVmDefinition(JsonObject definition) {
|
||||
public VmChannel setVmDefinition(DynamicKubernetesObject definition) {
|
||||
this.vmDefinition = definition;
|
||||
return this;
|
||||
}
|
||||
|
|
@ -66,7 +66,7 @@ public class VmChannel extends DefaultSubchannel {
|
|||
*
|
||||
* @return the json object
|
||||
*/
|
||||
public JsonObject vmDefinition() {
|
||||
public DynamicKubernetesObject vmDefinition() {
|
||||
return vmDefinition;
|
||||
}
|
||||
|
||||
|
|
@ -16,17 +16,22 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.jdrupes.vmoperator.manager;
|
||||
package org.jdrupes.vmoperator.manager.events;
|
||||
|
||||
import io.kubernetes.client.openapi.models.V1APIResource;
|
||||
import io.kubernetes.client.openapi.models.V1Namespace;
|
||||
import io.kubernetes.client.util.generic.dynamic.DynamicKubernetesObject;
|
||||
import org.jgrapes.core.Channel;
|
||||
import org.jgrapes.core.Components;
|
||||
import org.jgrapes.core.Event;
|
||||
|
||||
/**
|
||||
* Indicates a change in a VM definition.
|
||||
* Indicates a change in a VM definition. Note that the definition
|
||||
* consists of the metadata (mostly immutable), the "spec" and the
|
||||
* "status" parts. Consumers that are only interested in "spec"
|
||||
* changes should check {@link #specChanged()} before processing
|
||||
* the event any further.
|
||||
*/
|
||||
@SuppressWarnings("PMD.DataClass")
|
||||
public class VmDefChanged extends Event<Void> {
|
||||
|
||||
/**
|
||||
|
|
@ -37,20 +42,24 @@ public class VmDefChanged extends Event<Void> {
|
|||
}
|
||||
|
||||
private final Type type;
|
||||
private final boolean specChanged;
|
||||
private final V1APIResource crd;
|
||||
private final V1Namespace object;
|
||||
private final DynamicKubernetesObject vmDef;
|
||||
|
||||
/**
|
||||
* Instantiates a new VM changed event.
|
||||
*
|
||||
* @param type the type
|
||||
* @param specChanged the spec part changed
|
||||
* @param crd the crd
|
||||
* @param object the object
|
||||
* @param vmDefinition the VM definition
|
||||
*/
|
||||
public VmDefChanged(Type type, V1APIResource crd, V1Namespace object) {
|
||||
public VmDefChanged(Type type, boolean specChanged, V1APIResource crd,
|
||||
DynamicKubernetesObject vmDefinition) {
|
||||
this.type = type;
|
||||
this.specChanged = specChanged;
|
||||
this.crd = crd;
|
||||
this.object = object;
|
||||
this.vmDef = vmDefinition;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -62,6 +71,13 @@ public class VmDefChanged extends Event<Void> {
|
|||
return type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if the "spec" part changed.
|
||||
*/
|
||||
public boolean specChanged() {
|
||||
return specChanged;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the Crd.
|
||||
*
|
||||
|
|
@ -76,15 +92,15 @@ public class VmDefChanged extends Event<Void> {
|
|||
*
|
||||
* @return the object.
|
||||
*/
|
||||
public V1Namespace object() {
|
||||
return object;
|
||||
public DynamicKubernetesObject vmDefinition() {
|
||||
return vmDef;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
builder.append(Components.objectName(this)).append(" [")
|
||||
.append(object.getMetadata().getName()).append(' ').append(type);
|
||||
.append(vmDef.getMetadata().getName()).append(' ').append(type);
|
||||
if (channels() != null) {
|
||||
builder.append(", channels=");
|
||||
builder.append(Channel.toString(channels()));
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
/*
|
||||
* VM-Operator
|
||||
* Copyright (C) 2023 Michael N. Lipp
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Domain specific {@link org.jgrapes.core.Event}s (and
|
||||
* {@link org.jgrapes.core.Channel}s) used to communicate between
|
||||
* the core components of the {@link org.jgrapes.core.Manager} and
|
||||
* "plugin" components such as the conlets.
|
||||
*/
|
||||
package org.jdrupes.vmoperator.manager.events;
|
||||
|
|
@ -1,10 +1,10 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<fileset-config file-format-version="1.2.0" simple-config="false" sync-formatter="false">
|
||||
<local-check-config name="Project Checks" location="/VM-Operator/checkstyle.xml" type="project" description="">
|
||||
<local-check-config name="Project Checks" location="/VM-Operator/checkstyle.xml" type="project" description="">
|
||||
<additional-data name="protect-config-file" value="false"/>
|
||||
</local-check-config>
|
||||
<fileset name="all" enabled="true" check-config-name="Project Checks" local="true">
|
||||
<file-match-pattern match-pattern="^src/" include-pattern="true"/>
|
||||
<file-match-pattern match-pattern="." include-pattern="true"/>
|
||||
</fileset>
|
||||
</fileset-config>
|
||||
|
|
|
|||
|
|
@ -9,16 +9,30 @@ plugins {
|
|||
}
|
||||
|
||||
dependencies {
|
||||
implementation project(':org.jdrupes.vmoperator.manager.events')
|
||||
|
||||
implementation 'commons-cli:commons-cli:1.5.0'
|
||||
|
||||
implementation 'org.jgrapes:org.jgrapes.core:[1.19.0,2)'
|
||||
implementation 'org.jgrapes:org.jgrapes.io:[2.7.0,3)'
|
||||
implementation 'org.jgrapes:org.jgrapes.http:[3.1.0,4)'
|
||||
implementation 'org.jgrapes:org.jgrapes.util:[1.31.0,2)'
|
||||
implementation project(':org.jdrupes.vmoperator.util')
|
||||
|
||||
implementation 'commons-cli:commons-cli:1.5.0'
|
||||
implementation 'org.jgrapes:org.jgrapes.webconsole.base:[1.2.0,2)'
|
||||
implementation 'org.jgrapes:org.jgrapes.webconsole.vuejs:[1.3.0,2)'
|
||||
implementation 'org.jgrapes:org.jgrapes.webconsole.rbac:[1.0.0,2)'
|
||||
implementation 'org.jgrapes:org.jgrapes.webconlet.locallogin:[1.0.0,2)'
|
||||
|
||||
runtimeOnly 'org.jgrapes:org.jgrapes.webconlet.locallogin:[0.1.0,2)'
|
||||
runtimeOnly 'org.jgrapes:org.jgrapes.webconlet.sysinfo:[1.2.0,2)'
|
||||
runtimeOnly 'org.jgrapes:org.jgrapes.webconlet.logviewer:[0.1.0-SNAPSHOT,2)'
|
||||
|
||||
runtimeOnly 'com.electronwill.night-config:yaml:[3.6.7,3.7)'
|
||||
runtimeOnly 'org.eclipse.angus:angus-activation:[1.0.0,2.0.0)'
|
||||
runtimeOnly 'org.slf4j:slf4j-jdk14:[2.0.7,3)'
|
||||
runtimeOnly 'org.apache.logging.log4j:log4j-to-jul:2.20.0'
|
||||
|
||||
runtimeOnly project(':org.jdrupes.vmoperator.vmconlet')
|
||||
|
||||
testImplementation 'io.fabric8:kubernetes-client:[6.8.1,6.9)'
|
||||
}
|
||||
|
|
@ -97,3 +111,8 @@ test {
|
|||
? project.getProperty("k8s.testCluster") : null
|
||||
}
|
||||
|
||||
// Update favicon:
|
||||
// # Convert with inkscape to png because convert cannot handle svg
|
||||
// # background transparency, then
|
||||
// convert VM-Operator.png -background transparent \
|
||||
// -define icon:auto-resize=256,64,48,32,16 favicon.ico
|
||||
|
|
|
|||
|
|
@ -16,7 +16,8 @@
|
|||
# with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
|
||||
handlers=java.util.logging.ConsoleHandler
|
||||
handlers=java.util.logging.ConsoleHandler, \
|
||||
org.jgrapes.webconlet.logviewer.LogViewerHandler
|
||||
|
||||
org.jgrapes.level=FINE
|
||||
org.jgrapes.core.handlerTracking.level=FINER
|
||||
|
|
@ -26,3 +27,5 @@ org.jdrupes.vmoperator.manager.level=FINE
|
|||
java.util.logging.ConsoleHandler.level=ALL
|
||||
java.util.logging.ConsoleHandler.formatter=java.util.logging.SimpleFormatter
|
||||
java.util.logging.SimpleFormatter.format=%1$tb %1$td %1$tT %4$s %5$s%6$s%n
|
||||
|
||||
org.jgrapes.webconlet.logviewer.LogViewerHandler.level=CONFIG
|
||||
|
|
|
|||
Binary file not shown.
|
After Width: | Height: | Size: 25 KiB |
|
|
@ -0,0 +1,184 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
width="93.557968mm"
|
||||
height="91.220795mm"
|
||||
viewBox="0 0 331.50461 323.22329"
|
||||
id="svg2"
|
||||
version="1.1"
|
||||
inkscape:version="1.2.2 (b0a8486541, 2022-12-01)"
|
||||
sodipodi:docname="VM-Operator.svg"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/">
|
||||
<defs
|
||||
id="defs4" />
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="0.7"
|
||||
inkscape:cx="245"
|
||||
inkscape:cy="145.71429"
|
||||
inkscape:document-units="px"
|
||||
inkscape:current-layer="layer1"
|
||||
showgrid="false"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1011"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="32"
|
||||
inkscape:window-maximized="1"
|
||||
fit-margin-top="0"
|
||||
fit-margin-left="0"
|
||||
fit-margin-right="0"
|
||||
fit-margin-bottom="0"
|
||||
inkscape:showpageshadow="2"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:deskcolor="#d1d1d1" />
|
||||
<metadata
|
||||
id="metadata7">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:label="Ebene 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
transform="translate(799.83239,410.74206)">
|
||||
<g
|
||||
id="path300"
|
||||
inkscape:transform-center-x="-0.49891951"
|
||||
inkscape:transform-center-y="-10.814906"
|
||||
transform="matrix(0.93749998,0,0,0.93749998,-364.15225,128.12438)">
|
||||
<path
|
||||
sodipodi:type="star"
|
||||
style="fill:#326de6;fill-opacity:1;stroke:#ffffff;stroke-linecap:square;stroke-miterlimit:0;paint-order:fill markers stroke"
|
||||
id="path1033"
|
||||
inkscape:flatsided="false"
|
||||
sodipodi:sides="7"
|
||||
sodipodi:cx="-790.008"
|
||||
sodipodi:cy="-357.15076"
|
||||
sodipodi:r1="221.23064"
|
||||
sodipodi:r2="199.10757"
|
||||
sodipodi:arg1="1.1215879"
|
||||
sodipodi:arg2="1.5605315"
|
||||
inkscape:rounded="0"
|
||||
inkscape:randomized="0"
|
||||
d="m -693.93801,-157.86816 -94.02622,-0.18551 -97.95052,0.26412 -58.47935,-73.62832 -61.2776,-76.41613 21.10362,-91.6275 21.53854,-95.55347 84.79518,-40.6293 88.13578,-42.7371 84.6342,40.96358 88.36497,42.26117 20.74194,91.71007 22.05354,95.43592 -58.76943,73.39699 z"
|
||||
transform="matrix(0.81788201,0,0,0.81788201,358.19384,-101.37507)"
|
||||
inkscape:transform-center-x="1.2804791"
|
||||
inkscape:transform-center-y="-8.9686433" />
|
||||
</g>
|
||||
<g
|
||||
aria-label="VM"
|
||||
id="text300"
|
||||
style="font-size:16.6665px;-inkscape-font-specification:'sans-serif, Normal';letter-spacing:0px;word-spacing:0px;fill:none;stroke:#ffffff;stroke-width:1.04165">
|
||||
<g
|
||||
id="path305">
|
||||
<path
|
||||
style="color:#000000;-inkscape-font-specification:'Nimbus Sans Narrow, Bold Semi-Condensed';fill:#ff6600;stroke:none;-inkscape-stroke:none"
|
||||
d="m -698.0792,-217.35754 -25.39961,-109.59835 h -26.39961 l 39.59941,143.59784 h 23.39965 l 39.99939,-143.59784 h -25.59961 z"
|
||||
id="path312" />
|
||||
<path
|
||||
style="color:#000000;-inkscape-font-specification:'Nimbus Sans Narrow, Bold Semi-Condensed';fill:#ffffff;stroke:none;-inkscape-stroke:none"
|
||||
d="m -750.5625,-327.47656 39.88672,144.63867 h 24.19141 l 40.29101,-144.63867 h -26.69922 l -25.18554,107.82226 -24.98633,-107.82226 z m 1.36719,1.04101 h 25.30273 l 25.30664,109.19532 1.01367,0.002 25.50586,-109.19727 h 24.50196 l -39.71094,142.55664 h -22.60742 z"
|
||||
id="path325" />
|
||||
</g>
|
||||
<g
|
||||
id="path307">
|
||||
<path
|
||||
style="color:#000000;-inkscape-font-specification:'Nimbus Sans Narrow, Bold Semi-Condensed';fill:#ff6600;stroke:none;-inkscape-stroke:none"
|
||||
d="m -518.28172,-326.95589 h -35.39947 l -21.19968,113.99829 -21.59968,-113.99829 h -35.79946 v 143.59784 h 22.79966 v -121.79817 l 21.79967,121.79817 h 24.19963 l 22.39967,-121.79817 v 121.79817 h 22.79966 z"
|
||||
id="path318" />
|
||||
<path
|
||||
style="color:#000000;-inkscape-font-specification:'Nimbus Sans Narrow, Bold Semi-Condensed';fill:#ffffff;stroke:none;-inkscape-stroke:none"
|
||||
d="m -632.80078,-327.47656 v 144.63867 h 23.8418 v -116.45313 l 20.84179,116.45313 h 25.07032 l 21.44531,-116.60742 v 116.60742 h 23.83984 v -144.63867 h -0.51953 -35.83203 l -20.77149,111.69726 -21.16406,-111.69726 z m 1.04101,1.04101 h 34.84766 l 21.51953,113.57422 1.02344,-0.002 21.12109,-113.57227 h 34.44532 v 142.55664 h -21.75782 v -121.27734 l -1.0332,-0.0937 -22.32031,121.37109 h -23.33008 l -21.72266,-121.36914 -1.03515,0.0918 v 121.27734 h -21.75782 z"
|
||||
id="path320" />
|
||||
</g>
|
||||
</g>
|
||||
<circle
|
||||
style="fill:#6606b5;fill-opacity:1;stroke:#ffffff;stroke-width:1.07634;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="path4136"
|
||||
cx="-691.58337"
|
||||
cy="-161.52753"
|
||||
r="9.2055216" />
|
||||
<circle
|
||||
style="fill:#6606b5;fill-opacity:1;stroke:#ffffff;stroke-width:1.07634;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="path4136-8"
|
||||
cx="-671.12665"
|
||||
cy="-161.52753"
|
||||
r="9.2055216" />
|
||||
<circle
|
||||
style="fill:#6606b5;fill-opacity:1;stroke:#ffffff;stroke-width:1.07634;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="path4136-1"
|
||||
cx="-650.66992"
|
||||
cy="-161.52753"
|
||||
r="9.2055216" />
|
||||
<circle
|
||||
style="fill:#6606b5;fill-opacity:1;stroke:#ffffff;stroke-width:1.07634;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="path4136-2"
|
||||
cx="-630.2132"
|
||||
cy="-161.52753"
|
||||
r="9.2055216" />
|
||||
<circle
|
||||
style="fill:#6606b5;fill-opacity:1;stroke:#ffffff;stroke-width:1.07634;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="path4136-8-8"
|
||||
cx="-681.66187"
|
||||
cy="-144.03705"
|
||||
r="9.2055216" />
|
||||
<circle
|
||||
style="fill:#6606b5;fill-opacity:1;stroke:#ffffff;stroke-width:1.07634;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="path4136-1-9"
|
||||
cx="-661.20514"
|
||||
cy="-144.03705"
|
||||
r="9.2055216" />
|
||||
<circle
|
||||
style="fill:#6606b5;fill-opacity:1;stroke:#ffffff;stroke-width:1.07634;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="path4136-2-3"
|
||||
cx="-640.74841"
|
||||
cy="-144.03705"
|
||||
r="9.2055216" />
|
||||
<circle
|
||||
style="fill:#6606b5;fill-opacity:1;stroke:#ffffff;stroke-width:1.07634;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="path4136-8-6"
|
||||
cx="-671.53577"
|
||||
cy="-126.23969"
|
||||
r="9.2055216" />
|
||||
<circle
|
||||
style="fill:#6606b5;fill-opacity:1;stroke:#ffffff;stroke-width:1.07634;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="path4136-1-8"
|
||||
cx="-651.07904"
|
||||
cy="-126.23969"
|
||||
r="9.2055216" />
|
||||
<circle
|
||||
style="fill:#6606b5;fill-opacity:1;stroke:#ffffff;stroke-width:1.07634;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="path4136-2-0"
|
||||
cx="-661.61426"
|
||||
cy="-109.05605"
|
||||
r="9.2055216" />
|
||||
<g
|
||||
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:180px;line-height:125%;font-family:Arial;-inkscape-font-specification:'Arial Bold';letter-spacing:0px;word-spacing:0px;fill:#238220;fill-opacity:1;stroke:#ffffff;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
id="flowRoot4814"
|
||||
transform="matrix(0.25165808,0,0,0.24991064,-709.96916,-218.52595)">
|
||||
<path
|
||||
d="m 210.10352,57.161102 h 25.92773 V 138.7236 q 0,15.9961 -2.8125,24.60938 -3.7793,11.25 -13.71094,18.10547 -9.93164,6.76757 -26.1914,6.76757 -19.07227,0 -29.35547,-10.63476 -10.28321,-10.72266 -10.3711,-31.37696 l 24.52149,-2.8125 q 0.43945,11.07422 3.25195,15.64454 4.21875,6.94336 12.83203,6.94336 8.70117,0 12.30469,-4.92188 3.60352,-5.00977 3.60352,-20.6543 z"
|
||||
style="fill:#238220;fill-opacity:1;stroke:#ffffff;stroke-opacity:1"
|
||||
id="path4823"
|
||||
inkscape:connector-curvature="0" />
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 8.5 KiB |
|
|
@ -0,0 +1,2 @@
|
|||
<a class="navbar-brand" href="#"><img style="height: 1.25em; padding-right: 0.25em;"
|
||||
src="${renderSupport.consoleResource('VM-Operator.svg')}"><span>${_("consoleTitle")}<span></a>
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 58 KiB |
|
|
@ -0,0 +1,19 @@
|
|||
#
|
||||
# VM-Operator
|
||||
# Copyright (C) 2023 Michael N. Lipp
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it
|
||||
# under the terms of the GNU Affero General Public License as published by
|
||||
# the Free Software Foundation; either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful, but
|
||||
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License
|
||||
# for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License along
|
||||
# with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
|
||||
consoleTitle = VM-Operator
|
||||
|
|
@ -32,9 +32,11 @@ import java.io.IOException;
|
|||
import java.io.StringWriter;
|
||||
import java.util.Map;
|
||||
import java.util.logging.Logger;
|
||||
import org.jdrupes.vmoperator.common.K8s;
|
||||
import static org.jdrupes.vmoperator.manager.Constants.APP_NAME;
|
||||
import static org.jdrupes.vmoperator.manager.Constants.VM_OP_NAME;
|
||||
import org.jdrupes.vmoperator.util.K8s;
|
||||
import org.jdrupes.vmoperator.manager.events.VmChannel;
|
||||
import org.jdrupes.vmoperator.manager.events.VmDefChanged;
|
||||
import org.yaml.snakeyaml.LoaderOptions;
|
||||
import org.yaml.snakeyaml.Yaml;
|
||||
import org.yaml.snakeyaml.constructor.SafeConstructor;
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ package org.jdrupes.vmoperator.manager;
|
|||
/**
|
||||
* Some constants.
|
||||
*/
|
||||
public class Constants extends org.jdrupes.vmoperator.util.Constants {
|
||||
public class Constants extends org.jdrupes.vmoperator.common.Constants {
|
||||
|
||||
/** The Constant APP_NAME. */
|
||||
public static final String APP_NAME = "vm-runner";
|
||||
|
|
|
|||
|
|
@ -18,15 +18,28 @@
|
|||
|
||||
package org.jdrupes.vmoperator.manager;
|
||||
|
||||
import io.kubernetes.client.custom.V1Patch;
|
||||
import io.kubernetes.client.openapi.ApiException;
|
||||
import io.kubernetes.client.openapi.Configuration;
|
||||
import io.kubernetes.client.util.Config;
|
||||
import io.kubernetes.client.util.generic.options.PatchOptions;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.logging.Level;
|
||||
import static org.jdrupes.vmoperator.common.Constants.VM_OP_GROUP;
|
||||
import static org.jdrupes.vmoperator.common.Constants.VM_OP_KIND_VM;
|
||||
import org.jdrupes.vmoperator.common.K8s;
|
||||
import org.jdrupes.vmoperator.manager.events.StartVm;
|
||||
import org.jdrupes.vmoperator.manager.events.StopVm;
|
||||
import org.jdrupes.vmoperator.manager.events.VmDefChanged;
|
||||
import org.jgrapes.core.Channel;
|
||||
import org.jgrapes.core.Component;
|
||||
import org.jgrapes.core.annotation.Handler;
|
||||
import org.jgrapes.core.events.HandlingError;
|
||||
import org.jgrapes.core.events.Start;
|
||||
import org.jgrapes.core.events.Stop;
|
||||
import org.jgrapes.util.events.ConfigurationUpdate;
|
||||
|
||||
/**
|
||||
* Implements a controller as defined in the
|
||||
|
|
@ -66,6 +79,8 @@ import org.jgrapes.core.events.Start;
|
|||
*/
|
||||
public class Controller extends Component {
|
||||
|
||||
private String namespace;
|
||||
|
||||
/**
|
||||
* Creates a new instance.
|
||||
*/
|
||||
|
|
@ -90,6 +105,20 @@ public class Controller extends Component {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure the component.
|
||||
*
|
||||
* @param event the event
|
||||
*/
|
||||
@Handler
|
||||
public void onConfigurationUpdate(ConfigurationUpdate event) {
|
||||
event.structured(componentPath()).ifPresent(c -> {
|
||||
if (c.containsKey("namespace")) {
|
||||
namespace = (String) c.get("namespace");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the start event. Has higher priority because it configures
|
||||
* the default Kubernetes client.
|
||||
|
|
@ -103,5 +132,73 @@ public class Controller extends Component {
|
|||
// Make sure to use thread specific client
|
||||
// https://github.com/kubernetes-client/java/issues/100
|
||||
Configuration.setDefaultApiClient(null);
|
||||
|
||||
// Verify that a namespace has been configured
|
||||
if (namespace == null) {
|
||||
var path = Path
|
||||
.of("/var/run/secrets/kubernetes.io/serviceaccount/namespace");
|
||||
if (Files.isReadable(path)) {
|
||||
namespace = Files.lines(path).findFirst().orElse(null);
|
||||
}
|
||||
}
|
||||
if (namespace == null) {
|
||||
logger.severe(() -> "Namespace to control not configured and"
|
||||
+ " no file in kubernetes directory.");
|
||||
event.cancel(true);
|
||||
fire(new Stop());
|
||||
return;
|
||||
}
|
||||
logger.fine(() -> "Controlling namespace \"" + namespace + "\".");
|
||||
}
|
||||
|
||||
/**
|
||||
* On start vm.
|
||||
*
|
||||
* @param event the event
|
||||
* @throws ApiException the api exception
|
||||
* @throws IOException Signals that an I/O exception has occurred.
|
||||
*/
|
||||
@Handler
|
||||
public void onStartVm(StartVm event) throws ApiException, IOException {
|
||||
patchRunning(event.name(), true);
|
||||
}
|
||||
|
||||
/**
|
||||
* On stop vm.
|
||||
*
|
||||
* @param event the event
|
||||
* @throws ApiException the api exception
|
||||
* @throws IOException Signals that an I/O exception has occurred.
|
||||
*/
|
||||
@Handler
|
||||
public void onStopVm(StopVm event) throws ApiException, IOException {
|
||||
patchRunning(event.name(), false);
|
||||
}
|
||||
|
||||
private void patchRunning(String name, boolean running)
|
||||
throws ApiException, IOException {
|
||||
var crApi = K8s.crApi(Config.defaultClient(), VM_OP_GROUP,
|
||||
VM_OP_KIND_VM, namespace, name);
|
||||
if (crApi.isEmpty()) {
|
||||
logger.warning(() -> "Trying to patch " + namespace + "/" + name
|
||||
+ " which does not exist.");
|
||||
return;
|
||||
}
|
||||
|
||||
// Patch running
|
||||
PatchOptions patchOpts = new PatchOptions();
|
||||
patchOpts.setFieldManager("kubernetes-java-kubectl-apply");
|
||||
var res = crApi.get().patch(namespace, name,
|
||||
V1Patch.PATCH_FORMAT_JSON_PATCH,
|
||||
new V1Patch("[{\"op\": \"replace\", \"path\": "
|
||||
+ "\"/spec/vm/state\", "
|
||||
+ "\"value\": \"" + (running ? "Running" : "Stopped")
|
||||
+ "\"}]"),
|
||||
patchOpts);
|
||||
if (!res.isSuccess()) {
|
||||
logger.warning(
|
||||
() -> "Cannot patch pod annotations: " + res.getStatus());
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -33,8 +33,10 @@ import java.util.Collections;
|
|||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.logging.Logger;
|
||||
import org.jdrupes.vmoperator.common.K8s;
|
||||
import org.jdrupes.vmoperator.manager.events.VmChannel;
|
||||
import org.jdrupes.vmoperator.manager.events.VmDefChanged;
|
||||
import org.jdrupes.vmoperator.util.GsonPtr;
|
||||
import org.jdrupes.vmoperator.util.K8s;
|
||||
import org.yaml.snakeyaml.LoaderOptions;
|
||||
import org.yaml.snakeyaml.Yaml;
|
||||
import org.yaml.snakeyaml.constructor.SafeConstructor;
|
||||
|
|
@ -120,7 +122,7 @@ import org.yaml.snakeyaml.constructor.SafeConstructor;
|
|||
= json.deserialize(json.serialize(asmData), JsonObject.class);
|
||||
|
||||
// Get metadata from VM definition
|
||||
var vmMeta = GsonPtr.to(channel.vmDefinition()).to("spec")
|
||||
var vmMeta = GsonPtr.to(channel.vmDefinition().getRaw()).to("spec")
|
||||
.get(JsonObject.class, LOAD_BALANCER_SERVICE)
|
||||
.map(JsonObject::deepCopy).orElseGet(() -> new JsonObject());
|
||||
|
||||
|
|
|
|||
|
|
@ -21,7 +21,12 @@ package org.jdrupes.vmoperator.manager;
|
|||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.nio.file.Files;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.LogManager;
|
||||
import java.util.logging.Logger;
|
||||
|
|
@ -35,39 +40,34 @@ import org.jdrupes.vmoperator.util.FsdUtils;
|
|||
import org.jgrapes.core.Channel;
|
||||
import org.jgrapes.core.Component;
|
||||
import org.jgrapes.core.Components;
|
||||
import org.jgrapes.core.NamedChannel;
|
||||
import org.jgrapes.core.annotation.Handler;
|
||||
import org.jgrapes.core.events.HandlingError;
|
||||
import org.jgrapes.core.events.Stop;
|
||||
import org.jgrapes.http.HttpServer;
|
||||
import org.jgrapes.http.InMemorySessionManager;
|
||||
import org.jgrapes.http.LanguageSelector;
|
||||
import org.jgrapes.http.events.Request;
|
||||
import org.jgrapes.io.NioDispatcher;
|
||||
import org.jgrapes.io.util.PermitsPool;
|
||||
import org.jgrapes.net.SocketServer;
|
||||
import org.jgrapes.util.ComponentCollector;
|
||||
import org.jgrapes.util.FileSystemWatcher;
|
||||
import org.jgrapes.util.YamlConfigurationStore;
|
||||
import org.jgrapes.util.events.WatchFile;
|
||||
import org.jgrapes.webconlet.locallogin.LoginConlet;
|
||||
import org.jgrapes.webconsole.base.BrowserLocalBackedKVStore;
|
||||
import org.jgrapes.webconsole.base.ConletComponentFactory;
|
||||
import org.jgrapes.webconsole.base.ConsoleWeblet;
|
||||
import org.jgrapes.webconsole.base.KVStoreBasedConsolePolicy;
|
||||
import org.jgrapes.webconsole.base.PageResourceProviderFactory;
|
||||
import org.jgrapes.webconsole.base.WebConsole;
|
||||
import org.jgrapes.webconsole.rbac.RoleConfigurator;
|
||||
import org.jgrapes.webconsole.rbac.RoleConletFilter;
|
||||
import org.jgrapes.webconsole.vuejs.VueJsConsoleWeblet;
|
||||
|
||||
/**
|
||||
* The application class. In framework term, this is the root component.
|
||||
* Two of its child components, the {@link Controller} and the WebGui
|
||||
* implement user-visible functions. The others are used internally.
|
||||
*
|
||||
* 
|
||||
*
|
||||
* @startuml manager-components.svg
|
||||
* skinparam component {
|
||||
* BackGroundColor #FEFECE
|
||||
* BorderColor #A80036
|
||||
* BorderThickness 1.25
|
||||
* BackgroundColor<<internal>> #F1F1F1
|
||||
* BorderColor<<internal>> #181818
|
||||
* BorderThickness<<internal>> 1
|
||||
* }
|
||||
*
|
||||
* [Manager]
|
||||
* [Manager] *--> [Controller]
|
||||
* [Manager] *--> [WebGui]
|
||||
* [NioDispatcher] <<internal>>
|
||||
* [Manager] *--> [NioDispatcher] <<internal>>
|
||||
* [FileSystemWatcher] <<internal>>
|
||||
* [Manager] *--> [FileSystemWatcher] <<internal>>
|
||||
* @enduml
|
||||
* The application class.
|
||||
*/
|
||||
public class Manager extends Component {
|
||||
|
||||
|
|
@ -79,22 +79,78 @@ public class Manager extends Component {
|
|||
*
|
||||
* @throws IOException Signals that an I/O exception has occurred.
|
||||
*/
|
||||
@SuppressWarnings("PMD.TooFewBranchesForASwitchStatement")
|
||||
public Manager(CommandLine cmdLine) throws IOException {
|
||||
// Prepare component tree
|
||||
attach(new NioDispatcher());
|
||||
attach(new FileSystemWatcher(channel()));
|
||||
attach(new Controller(channel()));
|
||||
Channel mgrChannel = new NamedChannel("manager");
|
||||
attach(new FileSystemWatcher(mgrChannel));
|
||||
attach(new Controller(mgrChannel));
|
||||
|
||||
// Configuration store with file in /etc/opt (default)
|
||||
File config = new File(cmdLine.getOptionValue('c',
|
||||
"/etc/opt/" + VM_OP_NAME.replace("-", "") + "/config.yaml"));
|
||||
File cfgFile = new File(cmdLine.getOptionValue('c',
|
||||
"/etc/opt/" + VM_OP_NAME.replace("-", "") + "/config.yaml"))
|
||||
.getCanonicalFile();
|
||||
logger.config(() -> "Using configuration from: " + cfgFile.getPath());
|
||||
// Don't rely on night config to produce a good exception
|
||||
// for this simple case
|
||||
if (!Files.isReadable(config.toPath())) {
|
||||
throw new IOException("Cannot read configuration file " + config);
|
||||
if (!Files.isReadable(cfgFile.toPath())) {
|
||||
throw new IOException("Cannot read configuration file " + cfgFile);
|
||||
}
|
||||
attach(new YamlConfigurationStore(channel(), config, false));
|
||||
fire(new WatchFile(config.toPath()));
|
||||
attach(new YamlConfigurationStore(mgrChannel, cfgFile, false));
|
||||
fire(new WatchFile(cfgFile.toPath()));
|
||||
|
||||
// Prepare GUI
|
||||
Channel httpTransport = new NamedChannel("guiTransport");
|
||||
attach(new SocketServer(httpTransport)
|
||||
.setConnectionLimiter(new PermitsPool(300))
|
||||
.setMinimalPurgeableTime(1000)
|
||||
.setServerAddress(new InetSocketAddress(8080))
|
||||
.setName("GuiSocketServer"));
|
||||
|
||||
// Create an HTTP server as converter between transport and application
|
||||
// layer.
|
||||
Channel httpChannel = new NamedChannel("guiHttp");
|
||||
HttpServer guiHttpServer = attach(new HttpServer(httpChannel,
|
||||
httpTransport, Request.In.Get.class, Request.In.Post.class));
|
||||
guiHttpServer.setName("GuiHttpServer");
|
||||
|
||||
// Build HTTP application layer
|
||||
guiHttpServer.attach(new InMemorySessionManager(httpChannel));
|
||||
guiHttpServer.attach(new LanguageSelector(httpChannel));
|
||||
URI rootUri;
|
||||
try {
|
||||
rootUri = new URI("/");
|
||||
} catch (URISyntaxException e) {
|
||||
// Cannot happen
|
||||
return;
|
||||
}
|
||||
ConsoleWeblet consoleWeblet = guiHttpServer
|
||||
.attach(new VueJsConsoleWeblet(httpChannel, Channel.SELF, rootUri))
|
||||
.prependClassTemplateLoader(getClass())
|
||||
.prependResourceBundleProvider(getClass())
|
||||
.prependConsoleResourceProvider(getClass());
|
||||
consoleWeblet.setName("ConsoleWeblet");
|
||||
WebConsole console = consoleWeblet.console();
|
||||
console.attach(new BrowserLocalBackedKVStore(
|
||||
console.channel(), consoleWeblet.prefix().getPath()));
|
||||
console.attach(new KVStoreBasedConsolePolicy(console.channel()));
|
||||
console.attach(new RoleConfigurator(console.channel()));
|
||||
console.attach(new RoleConletFilter(console.channel()));
|
||||
console.attach(new LoginConlet(console.channel()));
|
||||
// Add all available page resource providers
|
||||
console.attach(new ComponentCollector<>(
|
||||
PageResourceProviderFactory.class, console.channel()));
|
||||
// Add all available conlets
|
||||
console.attach(new ComponentCollector<>(
|
||||
ConletComponentFactory.class, console.channel(), type -> {
|
||||
if (LoginConlet.class.getName().equals(type)) {
|
||||
// Explicitly added, see above
|
||||
return Collections.emptyList();
|
||||
} else {
|
||||
return Arrays.asList(Collections.emptyMap());
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -148,10 +204,12 @@ public class Manager extends Component {
|
|||
@SuppressWarnings("PMD.SignatureDeclareThrowsException")
|
||||
public static void main(String[] args) {
|
||||
try {
|
||||
// Instance logger is not available yet.
|
||||
var logger = Logger.getLogger(Manager.class.getName());
|
||||
logger.fine(() -> "Version: "
|
||||
logger.config(() -> "Version: "
|
||||
+ Manager.class.getPackage().getImplementationVersion());
|
||||
logger.fine(() -> "running on " + System.getProperty("java.vm.name")
|
||||
logger.config(() -> "running on "
|
||||
+ System.getProperty("java.vm.name")
|
||||
+ " (" + System.getProperty("java.vm.version") + ")"
|
||||
+ " from " + System.getProperty("java.vm.vendor"));
|
||||
|
||||
|
|
@ -165,7 +223,7 @@ public class Manager extends Component {
|
|||
// The Manager is the root component
|
||||
app = new Manager(cmd);
|
||||
|
||||
// Prepare Stop
|
||||
// Prepare generation of Stop event
|
||||
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
|
||||
try {
|
||||
app.fire(new Stop());
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ import freemarker.template.TemplateModelException;
|
|||
import freemarker.template.TemplateNotFoundException;
|
||||
import io.kubernetes.client.custom.Quantity;
|
||||
import io.kubernetes.client.openapi.ApiException;
|
||||
import io.kubernetes.client.util.generic.dynamic.DynamicKubernetesApi;
|
||||
import io.kubernetes.client.util.generic.dynamic.DynamicKubernetesObject;
|
||||
import java.io.IOException;
|
||||
import java.math.BigDecimal;
|
||||
import java.math.BigInteger;
|
||||
|
|
@ -43,12 +43,12 @@ import java.util.HashMap;
|
|||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import static org.jdrupes.vmoperator.manager.Constants.VM_OP_GROUP;
|
||||
import org.jdrupes.vmoperator.manager.VmDefChanged.Type;
|
||||
import org.jdrupes.vmoperator.util.Convertions;
|
||||
import org.jdrupes.vmoperator.common.Convertions;
|
||||
import org.jdrupes.vmoperator.manager.events.VmChannel;
|
||||
import org.jdrupes.vmoperator.manager.events.VmDefChanged;
|
||||
import org.jdrupes.vmoperator.manager.events.VmDefChanged.Type;
|
||||
import org.jdrupes.vmoperator.util.ExtendedObjectWrapper;
|
||||
import org.jdrupes.vmoperator.util.GsonPtr;
|
||||
import org.jdrupes.vmoperator.util.K8s;
|
||||
import org.jgrapes.core.Channel;
|
||||
import org.jgrapes.core.Component;
|
||||
import org.jgrapes.core.annotation.Handler;
|
||||
|
|
@ -129,7 +129,7 @@ public class Reconciler extends Component {
|
|||
@SuppressWarnings("PMD.SingularField")
|
||||
private final Configuration fmConfig;
|
||||
private final ConfigMapReconciler cmReconciler;
|
||||
private final StatefuleSetReconciler stsReconciler;
|
||||
private final StatefulSetReconciler stsReconciler;
|
||||
private final LoadBalancerReconciler lbReconciler;
|
||||
@SuppressWarnings("PMD.UseConcurrentHashMap")
|
||||
private final Map<String, Object> config = new HashMap<>();
|
||||
|
|
@ -153,7 +153,7 @@ public class Reconciler extends Component {
|
|||
fmConfig.setClassForTemplateLoading(Reconciler.class, "");
|
||||
|
||||
cmReconciler = new ConfigMapReconciler(fmConfig);
|
||||
stsReconciler = new StatefuleSetReconciler(fmConfig);
|
||||
stsReconciler = new StatefulSetReconciler(fmConfig);
|
||||
lbReconciler = new LoadBalancerReconciler(fmConfig);
|
||||
}
|
||||
|
||||
|
|
@ -186,38 +186,65 @@ public class Reconciler extends Component {
|
|||
@SuppressWarnings("PMD.ConfusingTernary")
|
||||
public void onVmDefChanged(VmDefChanged event, VmChannel channel)
|
||||
throws ApiException, TemplateException, IOException {
|
||||
var defMeta = event.object().getMetadata();
|
||||
// We're only interested in "spec" changes.
|
||||
if (!event.specChanged()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Ownership relationships takes care of deletions
|
||||
var defMeta = event.vmDefinition().getMetadata();
|
||||
if (event.type() == Type.DELETED) {
|
||||
logger.fine(() -> "VM \"" + defMeta.getName() + "\" deleted");
|
||||
return;
|
||||
}
|
||||
|
||||
// Get full definition and associate with channel
|
||||
var apiVersion = K8s.version(event.object().getApiVersion());
|
||||
DynamicKubernetesApi vmCrApi = new DynamicKubernetesApi(VM_OP_GROUP,
|
||||
apiVersion, event.crd().getName(), channel.client());
|
||||
K8s.get(vmCrApi, defMeta).ifPresent(def -> channel
|
||||
.setVmDefinition(patchCr(def.getRaw().deepCopy())));
|
||||
|
||||
// Reconcile
|
||||
Map<String, Object> model = prepareModel(channel.vmDefinition());
|
||||
// Reconcile, use "augmented" vm definition for model
|
||||
Map<String, Object> model = prepareModel(patchCr(event.vmDefinition()));
|
||||
var configMap = cmReconciler.reconcile(event, model, channel);
|
||||
model.put("cm", configMap.getRaw());
|
||||
stsReconciler.reconcile(event, model, channel);
|
||||
lbReconciler.reconcile(event, model, channel);
|
||||
}
|
||||
|
||||
private DynamicKubernetesObject patchCr(DynamicKubernetesObject vmDef) {
|
||||
var json = vmDef.getRaw().deepCopy();
|
||||
// Adjust cdromImage path
|
||||
var disks
|
||||
= GsonPtr.to(json).to("spec", "vm", "disks").get(JsonArray.class);
|
||||
for (var disk : disks) {
|
||||
var cdrom = (JsonObject) ((JsonObject) disk).get("cdrom");
|
||||
if (cdrom == null) {
|
||||
continue;
|
||||
}
|
||||
String image = cdrom.get("image").getAsString();
|
||||
if (image.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
try {
|
||||
@SuppressWarnings("PMD.AvoidInstantiatingObjectsInLoops")
|
||||
var imageUri = new URI("file://" + Constants.IMAGE_REPO_PATH
|
||||
+ "/").resolve(image);
|
||||
if ("file".equals(imageUri.getScheme())) {
|
||||
cdrom.addProperty("image", imageUri.getPath());
|
||||
} else {
|
||||
cdrom.addProperty("image", imageUri.toString());
|
||||
}
|
||||
} catch (URISyntaxException e) {
|
||||
logger.warning(() -> "Invalid CDROM image: " + image);
|
||||
}
|
||||
}
|
||||
return new DynamicKubernetesObject(json);
|
||||
}
|
||||
|
||||
@SuppressWarnings("PMD.CognitiveComplexity")
|
||||
private Map<String, Object> prepareModel(JsonObject vmDef)
|
||||
private Map<String, Object> prepareModel(DynamicKubernetesObject vmDef)
|
||||
throws TemplateModelException {
|
||||
@SuppressWarnings("PMD.UseConcurrentHashMap")
|
||||
Map<String, Object> model = new HashMap<>();
|
||||
model.put("managerVersion",
|
||||
Optional.ofNullable(Reconciler.class.getPackage()
|
||||
.getImplementationVersion()).orElse("(Unknown)"));
|
||||
model.put("cr", vmDef);
|
||||
model.put("cr", vmDef.getRaw());
|
||||
model.put("constants",
|
||||
(TemplateHashModel) new DefaultObjectWrapperBuilder(
|
||||
Configuration.VERSION_2_3_32)
|
||||
|
|
@ -274,33 +301,4 @@ public class Reconciler extends Component {
|
|||
return model;
|
||||
}
|
||||
|
||||
private JsonObject patchCr(JsonObject vmDef) {
|
||||
// Adjust cdromImage path
|
||||
var disks
|
||||
= GsonPtr.to(vmDef).to("spec", "vm", "disks").get(JsonArray.class);
|
||||
for (var disk : disks) {
|
||||
var cdrom = (JsonObject) ((JsonObject) disk).get("cdrom");
|
||||
if (cdrom == null) {
|
||||
continue;
|
||||
}
|
||||
String image = cdrom.get("image").getAsString();
|
||||
if (image.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
try {
|
||||
@SuppressWarnings("PMD.AvoidInstantiatingObjectsInLoops")
|
||||
var imageUri = new URI("file://" + Constants.IMAGE_REPO_PATH
|
||||
+ "/").resolve(image);
|
||||
if ("file".equals(imageUri.getScheme())) {
|
||||
cdrom.addProperty("image", imageUri.getPath());
|
||||
} else {
|
||||
cdrom.addProperty("image", imageUri.toString());
|
||||
}
|
||||
} catch (URISyntaxException e) {
|
||||
logger.warning(() -> "Invalid CDROM image: " + image);
|
||||
}
|
||||
}
|
||||
return vmDef;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -29,8 +29,10 @@ import java.io.IOException;
|
|||
import java.io.StringWriter;
|
||||
import java.util.Map;
|
||||
import java.util.logging.Logger;
|
||||
import org.jdrupes.vmoperator.common.K8s;
|
||||
import org.jdrupes.vmoperator.manager.events.VmChannel;
|
||||
import org.jdrupes.vmoperator.manager.events.VmDefChanged;
|
||||
import org.jdrupes.vmoperator.util.GsonPtr;
|
||||
import org.jdrupes.vmoperator.util.K8s;
|
||||
import org.yaml.snakeyaml.LoaderOptions;
|
||||
import org.yaml.snakeyaml.Yaml;
|
||||
import org.yaml.snakeyaml.constructor.SafeConstructor;
|
||||
|
|
@ -39,7 +41,7 @@ import org.yaml.snakeyaml.constructor.SafeConstructor;
|
|||
* Delegee for reconciling the stateful set (effectively the pod).
|
||||
*/
|
||||
@SuppressWarnings("PMD.DataflowAnomalyAnalysis")
|
||||
/* default */ class StatefuleSetReconciler {
|
||||
/* default */ class StatefulSetReconciler {
|
||||
|
||||
protected final Logger logger = Logger.getLogger(getClass().getName());
|
||||
private final Configuration fmConfig;
|
||||
|
|
@ -49,7 +51,7 @@ import org.yaml.snakeyaml.constructor.SafeConstructor;
|
|||
*
|
||||
* @param fmConfig the fm config
|
||||
*/
|
||||
public StatefuleSetReconciler(Configuration fmConfig) {
|
||||
public StatefulSetReconciler(Configuration fmConfig) {
|
||||
this.fmConfig = fmConfig;
|
||||
}
|
||||
|
||||
|
|
@ -68,7 +70,7 @@ import org.yaml.snakeyaml.constructor.SafeConstructor;
|
|||
throws IOException, TemplateException, ApiException {
|
||||
DynamicKubernetesApi stsApi = new DynamicKubernetesApi("apps", "v1",
|
||||
"statefulsets", channel.client());
|
||||
var metadata = event.object().getMetadata();
|
||||
var metadata = event.vmDefinition().getMetadata();
|
||||
|
||||
// Combine template and data and parse result
|
||||
var fmTemplate = fmConfig.getTemplate("runnerSts.ftl.yaml");
|
||||
|
|
@ -44,11 +44,14 @@ import java.util.Map;
|
|||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.logging.Level;
|
||||
import static org.jdrupes.vmoperator.common.Constants.VM_OP_GROUP;
|
||||
import org.jdrupes.vmoperator.common.K8s;
|
||||
import static org.jdrupes.vmoperator.manager.Constants.APP_NAME;
|
||||
import static org.jdrupes.vmoperator.manager.Constants.VM_OP_GROUP;
|
||||
import static org.jdrupes.vmoperator.manager.Constants.VM_OP_KIND_VM;
|
||||
import static org.jdrupes.vmoperator.manager.Constants.VM_OP_NAME;
|
||||
import org.jdrupes.vmoperator.manager.VmDefChanged.Type;
|
||||
import org.jdrupes.vmoperator.manager.events.VmChannel;
|
||||
import org.jdrupes.vmoperator.manager.events.VmDefChanged;
|
||||
import org.jdrupes.vmoperator.manager.events.VmDefChanged.Type;
|
||||
import org.jgrapes.core.Channel;
|
||||
import org.jgrapes.core.Component;
|
||||
import org.jgrapes.core.Components;
|
||||
|
|
@ -107,15 +110,8 @@ public class VmWatcher extends Component {
|
|||
namespaceToWatch = Files.lines(path).findFirst().orElse(null);
|
||||
}
|
||||
}
|
||||
if (namespaceToWatch == null) {
|
||||
logger.severe(() -> "Namespace to watch not configured and"
|
||||
+ " no file in kubernetes directory.");
|
||||
event.cancel(true);
|
||||
fire(new Stop());
|
||||
return;
|
||||
}
|
||||
logger
|
||||
.fine(() -> "Controlling namespace \"" + namespaceToWatch + "\".");
|
||||
// Availability already checked by Controller.onStart
|
||||
logger.fine(() -> "Watching namespace \"" + namespaceToWatch + "\".");
|
||||
|
||||
// Get all our API versions
|
||||
var client = Config.defaultClient();
|
||||
|
|
@ -140,8 +136,7 @@ public class VmWatcher extends Component {
|
|||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings({ "PMD.AvoidInstantiatingObjectsInLoops",
|
||||
"PMD.CognitiveComplexity" })
|
||||
@SuppressWarnings("PMD.CognitiveComplexity")
|
||||
private void purge(ApiClient client, CustomObjectsApi coa,
|
||||
List<String> vmOpApiVersions) throws ApiException {
|
||||
// Get existing CRs (VMs)
|
||||
|
|
@ -161,6 +156,8 @@ public class VmWatcher extends Component {
|
|||
+ "app.kubernetes.io/name=" + APP_NAME);
|
||||
for (String resource : List.of("apps/v1/statefulsets",
|
||||
"v1/configmaps", "v1/secrets")) {
|
||||
@SuppressWarnings({ "PMD.AvoidInstantiatingObjectsInLoops",
|
||||
"PMD.AvoidDuplicateLiterals" })
|
||||
var resParts = new LinkedList<>(List.of(resource.split("/")));
|
||||
var group = resParts.size() == 3 ? resParts.poll() : "";
|
||||
var version = resParts.poll();
|
||||
|
|
@ -271,13 +268,20 @@ public class VmWatcher extends Component {
|
|||
return;
|
||||
}
|
||||
|
||||
// Filter duplicates
|
||||
if (!"DELETED".equals(item.type) && !channel
|
||||
.setGeneration(item.object.getMetadata().getGeneration())) {
|
||||
return;
|
||||
}
|
||||
// if (event.type() == Type.DELETED) {
|
||||
|
||||
// Get full definition and associate with channel as backup
|
||||
var apiVersion = K8s.version(item.object.getApiVersion());
|
||||
DynamicKubernetesApi vmCrApi = new DynamicKubernetesApi(VM_OP_GROUP,
|
||||
apiVersion, vmsCrd.getName(), channel.client());
|
||||
var vmDef = K8s.get(vmCrApi, metadata);
|
||||
vmDef.ifPresent(def -> channel.setVmDefinition(def));
|
||||
|
||||
// Create and fire event
|
||||
channel.pipeline().fire(new VmDefChanged(VmDefChanged.Type
|
||||
.valueOf(item.type), vmsCrd, item.object), channel);
|
||||
.valueOf(item.type),
|
||||
channel.setGeneration(item.object.getMetadata().getGeneration()),
|
||||
vmsCrd, vmDef.orElse(channel.vmDefinition())), channel);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -289,7 +293,7 @@ public class VmWatcher extends Component {
|
|||
@Handler(priority = -10_000)
|
||||
public void onVmDefChanged(VmDefChanged event, VmChannel channel) {
|
||||
if (event.type() == Type.DELETED) {
|
||||
channels.remove(event.object().getMetadata().getName());
|
||||
channels.remove(event.vmDefinition().getMetadata().getName());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -16,4 +16,151 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.jdrupes.vmoperator.manager;
|
||||
/**
|
||||
* The following diagram shows the components of the manager application.
|
||||
*
|
||||
* In framework terms, the {@link org.jdrupes.vmoperator.manager.Manager}
|
||||
* is the root component of the application. Two of its child components,
|
||||
* the {@link org.jdrupes.vmoperator.manager.Controller} and the WebGui
|
||||
* provide the functions that are apparent to the user.
|
||||
*
|
||||
* The position of the components in the component tree is important
|
||||
* when writing the configuration file for the manager and therefore
|
||||
* shown below. In order to keep the diagram readable, the
|
||||
* components attached to the
|
||||
* {@link org.jgrapes.webconsole.base.WebConsole} are shown in a
|
||||
* separate diagram further down.
|
||||
*
|
||||
* 
|
||||
*
|
||||
* Component hierarchy of the web console:
|
||||
*
|
||||
* 
|
||||
*
|
||||
* The components marked as "<<internal>>" have no
|
||||
* configuration options or use their default values when used
|
||||
* in this application.
|
||||
*
|
||||
* As an example, the following YAML configures a different port for the
|
||||
* GUI and some users. The relationship to the component tree should
|
||||
* be obvious.
|
||||
* ```
|
||||
* "/Manager":
|
||||
* "/GuiSocketServer":
|
||||
* port: 8888
|
||||
* "/GuiHttpServer":
|
||||
* "/ConsoleWeblet":
|
||||
* "/WebConsole":
|
||||
* "/LoginConlet":
|
||||
* users:
|
||||
* ...
|
||||
* ```
|
||||
*
|
||||
* Developers may also be interested in the usage of channels
|
||||
* by the application's component:
|
||||
*
|
||||
* 
|
||||
*
|
||||
* @startuml manager-components.svg
|
||||
* skinparam component {
|
||||
* BackGroundColor #FEFECE
|
||||
* BorderColor #A80036
|
||||
* BorderThickness 1.25
|
||||
* BackgroundColor<<internal>> #F1F1F1
|
||||
* BorderColor<<internal>> #181818
|
||||
* BorderThickness<<internal>> 1
|
||||
* }
|
||||
* skinparam packageStyle rectangle
|
||||
*
|
||||
* Component NioDispatcher as NioDispatcher <<internal>>
|
||||
* [Manager] *-up- [NioDispatcher]
|
||||
* Component FileSystemWatcher as FileSystemWatcher <<internal>>
|
||||
* [Manager] *-up- [FileSystemWatcher]
|
||||
* Component YamlConfigurationStore as YamlConfigurationStore <<internal>>
|
||||
* [Manager] *-left- [YamlConfigurationStore]
|
||||
* [YamlConfigurationStore] *-right[hidden]- [Controller]
|
||||
*
|
||||
* [Manager] *-- [Controller]
|
||||
* [Controller] *-- [VmWatcher]
|
||||
* [Controller] *-- [Reconciler]
|
||||
* [Controller] -right[hidden]- [GuiHttpServer]
|
||||
*
|
||||
* [Manager] *-down- [GuiSocketServer:8080]
|
||||
* [Manager] *-- [GuiHttpServer]
|
||||
* Component PreferencesStore as PreferencesStore <<internal>>
|
||||
* [GuiHttpServer] *-up- [PreferencesStore]
|
||||
* Component InMemorySessionManager as InMemorySessionManager <<internal>>
|
||||
* [GuiHttpServer] *-up- [InMemorySessionManager]
|
||||
* Component LanguageSelector as LanguageSelector <<internal>>
|
||||
* [GuiHttpServer] *-right- [LanguageSelector]
|
||||
*
|
||||
* package "Conceptual WebConsole" {
|
||||
* [ConsoleWeblet] *-- [WebConsole]
|
||||
* }
|
||||
* [GuiHttpServer] *-- [ConsoleWeblet]
|
||||
* @enduml
|
||||
*
|
||||
* @startuml console-components.svg
|
||||
* skinparam component {
|
||||
* BackGroundColor #FEFECE
|
||||
* BorderColor #A80036
|
||||
* BorderThickness 1.25
|
||||
* BackgroundColor<<internal>> #F1F1F1
|
||||
* BorderColor<<internal>> #181818
|
||||
* BorderThickness<<internal>> 1
|
||||
* }
|
||||
* skinparam packageStyle rectangle
|
||||
*
|
||||
* Component BrowserLocalBackedKVStore as BrowserLocalBackedKVStore <<internal>>
|
||||
* [WebConsole] *-up- [BrowserLocalBackedKVStore]
|
||||
* Component KVStoreBasedConsolePolicy as KVStoreBasedConsolePolicy <<internal>>
|
||||
* [WebConsole] *-up- [KVStoreBasedConsolePolicy]
|
||||
*
|
||||
* [WebConsole] *-- [RoleConfigurator]
|
||||
* [WebConsole] *-- [RoleConletFilter]
|
||||
* [WebConsole] *-left- [LoginConlet]
|
||||
*
|
||||
* Component "ComponentCollector\nfor page resources" as cpr <<internal>>
|
||||
* [WebConsole] *-- [cpr]
|
||||
* Component "ComponentCollector\nfor conlets" as cc <<internal>>
|
||||
* [WebConsole] *-- [cc]
|
||||
*
|
||||
* package "Providers and Conlets" {
|
||||
* [Some component]
|
||||
* }
|
||||
*
|
||||
* [cpr] *-- [Some component]
|
||||
* [cc] *-- [Some component]
|
||||
* @enduml
|
||||
*
|
||||
* @startuml app-channels.svg
|
||||
* skinparam packageStyle rectangle
|
||||
*
|
||||
* () "manager" as mgr
|
||||
* mgr .left. [FileSystemWatcher]
|
||||
* mgr .right. [YamlConfigurationStore]
|
||||
* mgr .. [Controller]
|
||||
* mgr .up. [VmWatcher]
|
||||
* mgr .. [Reconciler]
|
||||
*
|
||||
* () "guiTransport" as hT
|
||||
* hT .up. [GuiSocketServer:8080]
|
||||
* hT .down. [GuiHttpServer]
|
||||
*
|
||||
* [YamlConfigurationStore] -right[hidden]- hT
|
||||
*
|
||||
* () "guiHttp" as http
|
||||
* http .up. [GuiHttpServer]
|
||||
*
|
||||
* [PreferencesStore] .right. http
|
||||
* [InMemorySessionManager] .up. http
|
||||
* [LanguageSelector] .up. http
|
||||
*
|
||||
* package "Conceptual WebConsole" {
|
||||
* [ConsoleWeblet] .left. http
|
||||
* [ConsoleWeblet] *-down- [WebConsole]
|
||||
* }
|
||||
*
|
||||
* @enduml
|
||||
*/
|
||||
package org.jdrupes.vmoperator.manager;
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<fileset-config file-format-version="1.2.0" simple-config="false" sync-formatter="false">
|
||||
<local-check-config name="Project Checks" location="/VM-Operator/checkstyle.xml" type="project" description="">
|
||||
<local-check-config name="Project Checks" location="/VM-Operator/checkstyle.xml" type="project" description="">
|
||||
<additional-data name="protect-config-file" value="false"/>
|
||||
</local-check-config>
|
||||
<fileset name="all" enabled="true" check-config-name="Project Checks" local="true">
|
||||
<file-match-pattern match-pattern="^src/" include-pattern="true"/>
|
||||
<file-match-pattern match-pattern="." include-pattern="true"/>
|
||||
</fileset>
|
||||
</fileset-config>
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ dependencies {
|
|||
implementation 'org.jgrapes:org.jgrapes.io:[2.7.0,3)'
|
||||
implementation 'org.jgrapes:org.jgrapes.http:[3.1.0,4)'
|
||||
implementation 'org.jgrapes:org.jgrapes.util:[1.31.0,2)'
|
||||
implementation project(':org.jdrupes.vmoperator.util')
|
||||
implementation project(':org.jdrupes.vmoperator.common')
|
||||
|
||||
implementation 'commons-cli:commons-cli:1.5.0'
|
||||
implementation 'com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:[2.15.1,3]'
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ import java.util.Set;
|
|||
import java.util.UUID;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
import org.jdrupes.vmoperator.util.Convertions;
|
||||
import org.jdrupes.vmoperator.common.Convertions;
|
||||
import org.jdrupes.vmoperator.util.Dto;
|
||||
import org.jdrupes.vmoperator.util.FsdUtils;
|
||||
|
||||
|
|
|
|||
|
|
@ -38,13 +38,13 @@ import java.util.Map;
|
|||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.logging.Level;
|
||||
import static org.jdrupes.vmoperator.common.Constants.VM_OP_GROUP;
|
||||
import static org.jdrupes.vmoperator.common.Constants.VM_OP_KIND_VM;
|
||||
import org.jdrupes.vmoperator.runner.qemu.events.BalloonChangeEvent;
|
||||
import org.jdrupes.vmoperator.runner.qemu.events.HotpluggableCpuStatus;
|
||||
import org.jdrupes.vmoperator.runner.qemu.events.RunnerConfigurationUpdate;
|
||||
import org.jdrupes.vmoperator.runner.qemu.events.RunnerStateChange;
|
||||
import org.jdrupes.vmoperator.runner.qemu.events.RunnerStateChange.State;
|
||||
import static org.jdrupes.vmoperator.util.Constants.VM_OP_GROUP;
|
||||
import static org.jdrupes.vmoperator.util.Constants.VM_OP_KIND_VM;
|
||||
import org.jdrupes.vmoperator.util.GsonPtr;
|
||||
import org.jgrapes.core.Channel;
|
||||
import org.jgrapes.core.Component;
|
||||
|
|
@ -138,6 +138,9 @@ public class StatusUpdater extends Component {
|
|||
@SuppressWarnings({ "PMD.DataflowAnomalyAnalysis",
|
||||
"PMD.AvoidInstantiatingObjectsInLoops", "PMD.AvoidDuplicateLiterals" })
|
||||
public void onStart(Start event) throws IOException, ApiException {
|
||||
if (namespace == null) {
|
||||
return;
|
||||
}
|
||||
var client = Config.defaultClient();
|
||||
var apis = new ApisApi(client).getAPIVersions();
|
||||
var crdVersions = apis.getGroups().stream()
|
||||
|
|
@ -154,7 +157,7 @@ public class StatusUpdater extends Component {
|
|||
}
|
||||
var crApi = new DynamicKubernetesApi(VM_OP_GROUP,
|
||||
crdVersion, crdApiRes.get().getName(), client);
|
||||
var vmCr = crApi.get(namespace, vmName).throwsApiException();
|
||||
var vmCr = crApi.get(namespace, vmName);
|
||||
if (vmCr.isSuccess()) {
|
||||
vmCrApi = crApi;
|
||||
observedGeneration
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<fileset-config file-format-version="1.2.0" simple-config="false" sync-formatter="false">
|
||||
<local-check-config name="Project Checks" location="/VM-Operator/checkstyle.xml" type="project" description="">
|
||||
<local-check-config name="Project Checks" location="/VM-Operator/checkstyle.xml" type="project" description="">
|
||||
<additional-data name="protect-config-file" value="false"/>
|
||||
</local-check-config>
|
||||
<fileset name="all" enabled="true" check-config-name="Project Checks" local="true">
|
||||
<file-match-pattern match-pattern="^src/" include-pattern="true"/>
|
||||
<file-match-pattern match-pattern="." include-pattern="true"/>
|
||||
</fileset>
|
||||
</fileset-config>
|
||||
|
|
|
|||
|
|
@ -1,318 +0,0 @@
|
|||
#
|
||||
#Mon Oct 09 18:47:59 CEST 2023
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_ellipsis=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_declarations=insert
|
||||
org.eclipse.jdt.core.formatter.insert_new_line_in_empty_annotation_declaration=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_allocation_expression=do not insert
|
||||
org.eclipse.jdt.core.formatter.parentheses_positions_in_for_statment=common_lines
|
||||
org.eclipse.jdt.core.formatter.comment.new_lines_at_block_boundaries=true
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_parameters=insert
|
||||
org.eclipse.jdt.core.formatter.comment.insert_new_line_for_parameter=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_package=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_enum_constant=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_while=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_annotation_type_member_declaration=do not insert
|
||||
org.eclipse.jdt.core.formatter.comment.format_javadoc_comments=false
|
||||
org.eclipse.jdt.core.formatter.indentation.size=4
|
||||
org.eclipse.jdt.core.formatter.parentheses_positions_in_enum_constant_declaration=common_lines
|
||||
org.eclipse.jdt.core.formatter.insert_new_line_in_empty_anonymous_type_declaration=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_for=insert
|
||||
org.eclipse.jdt.core.formatter.continuation_indentation=2
|
||||
org.eclipse.jdt.core.formatter.blank_lines_after_package=1
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_local_declarations=insert
|
||||
org.eclipse.jdt.core.formatter.alignment_for_arguments_in_enum_constant=16
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_parameterized_type_reference=do not insert
|
||||
org.eclipse.jdt.core.formatter.wrap_before_or_operator_multicatch=true
|
||||
org.eclipse.jdt.core.formatter.comment.indent_root_tags=false
|
||||
org.eclipse.jdt.core.formatter.enabling_tag=@formatter\:on
|
||||
org.eclipse.jdt.core.formatter.comment.count_line_length_from_starting_position=false
|
||||
org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_method_declaration=16
|
||||
org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_parameter=do not insert
|
||||
org.eclipse.jdt.core.formatter.keep_then_statement_on_same_line=false
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_explicitconstructorcall_arguments=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_prefix_operator=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_closing_brace_in_array_initializer=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_arguments=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_method=insert
|
||||
org.eclipse.jdt.core.formatter.alignment_for_parameterized_type_references=16
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_parenthesized_expression=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_enum_constant=insert
|
||||
org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_and_in_type_parameter=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_invocation=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_assignment_operator=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_type_declaration=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_for=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_local_variable=insert
|
||||
org.eclipse.jdt.core.formatter.comment.preserve_white_space_between_code_and_line_comments=true
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_invocation=do not insert
|
||||
org.eclipse.jdt.core.formatter.alignment_for_union_type_in_multicatch=16
|
||||
org.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_method_body=0
|
||||
org.eclipse.jdt.core.formatter.keep_else_statement_on_same_line=false
|
||||
org.eclipse.jdt.core.formatter.parentheses_positions_in_catch_clause=common_lines
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_parameterized_type_reference=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_array_initializer=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_annotation=do not insert
|
||||
org.eclipse.jdt.core.formatter.alignment_for_arguments_in_explicit_constructor_call=20
|
||||
org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_annotation_declaration_header=true
|
||||
org.eclipse.jdt.core.formatter.brace_position_for_block=end_of_line
|
||||
org.eclipse.jdt.core.formatter.put_empty_statement_on_new_line=true
|
||||
org.eclipse.jdt.core.compiler.debug.lineNumber=generate
|
||||
org.eclipse.jdt.core.formatter.alignment_for_parameters_in_constructor_declaration=16
|
||||
org.eclipse.jdt.core.formatter.alignment_for_type_parameters=16
|
||||
org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
|
||||
org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_block_comment=false
|
||||
org.eclipse.jdt.core.formatter.insert_new_line_at_end_of_file_if_missing=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_array_initializer=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_unary_operator=do not insert
|
||||
org.eclipse.jdt.core.formatter.format_line_comment_starting_on_first_column=false
|
||||
org.eclipse.jdt.core.formatter.parentheses_positions_in_annotation=common_lines
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_ellipsis=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_try_resources=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_colon_in_assert=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_and_in_type_parameter=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_parenthesized_expression=do not insert
|
||||
org.eclipse.jdt.core.formatter.align_type_members_on_columns=false
|
||||
org.eclipse.jdt.core.formatter.alignment_for_module_statements=16
|
||||
org.eclipse.jdt.core.formatter.alignment_for_assignment=20
|
||||
org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_type_header=true
|
||||
org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_declaration=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_enum_constant=do not insert
|
||||
org.eclipse.jdt.core.formatter.blank_lines_before_first_class_body_declaration=0
|
||||
org.eclipse.jdt.core.formatter.format_guardian_clause_on_one_line=false
|
||||
org.eclipse.jdt.core.formatter.insert_new_line_before_closing_brace_in_array_initializer=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_parameters=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_if=insert
|
||||
org.eclipse.jdt.core.formatter.brace_position_for_block_in_case=end_of_line
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_constructor_declaration=do not insert
|
||||
org.eclipse.jdt.core.formatter.comment.format_header=false
|
||||
org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression=20
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_switch=do not insert
|
||||
org.eclipse.jdt.core.formatter.alignment_for_method_declaration=16
|
||||
org.eclipse.jdt.core.formatter.comment.new_lines_at_javadoc_boundaries=true
|
||||
org.eclipse.jdt.core.formatter.align_fields_grouping_blank_lines=2147483647
|
||||
org.eclipse.jdt.core.formatter.brace_position_for_annotation_type_declaration=end_of_line
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_colon_in_for=insert
|
||||
org.eclipse.jdt.core.formatter.alignment_for_resources_in_try=16
|
||||
org.eclipse.jdt.core.formatter.alignment_for_selector_in_method_invocation=20
|
||||
org.eclipse.jdt.core.formatter.never_indent_block_comments_on_first_column=true
|
||||
org.eclipse.jdt.core.compiler.source=17
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_synchronized=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_constant=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_allocation_expression=insert
|
||||
org.eclipse.jdt.core.formatter.comment.format_source_code=false
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_array_initializer=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation=do not insert
|
||||
org.eclipse.jdt.core.formatter.blank_lines_before_field=0
|
||||
org.eclipse.jdt.core.formatter.blank_lines_before_method=1
|
||||
org.eclipse.jdt.core.compiler.debug.sourceFile=generate
|
||||
org.eclipse.jdt.core.formatter.alignment_for_superclass_in_type_declaration=16
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_throw=insert
|
||||
org.eclipse.jdt.core.compiler.codegen.targetPlatform=17
|
||||
org.eclipse.jdt.core.formatter.insert_new_line_after_type_annotation=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_opening_brace_in_array_initializer=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_parenthesized_expression=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation_type_declaration=do not insert
|
||||
org.eclipse.jdt.core.formatter.comment.format_html=false
|
||||
org.eclipse.jdt.core.formatter.parentheses_positions_in_method_delcaration=common_lines
|
||||
org.eclipse.jdt.core.formatter.alignment_for_compact_if=16
|
||||
org.eclipse.jdt.core.formatter.indent_empty_lines=false
|
||||
org.eclipse.jdt.core.formatter.alignment_for_type_arguments=16
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_unary_operator=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_enum_constant=do not insert
|
||||
org.eclipse.jdt.core.formatter.alignment_for_arguments_in_annotation=20
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_declarations=do not insert
|
||||
org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_switch=false
|
||||
org.eclipse.jdt.core.formatter.insert_new_line_before_else_in_if_statement=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_assignment_operator=insert
|
||||
org.eclipse.jdt.core.formatter.insert_new_line_after_label=do not insert
|
||||
org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_declaration_header=true
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_colon_in_conditional=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_parameters=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_cast=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_new_line_before_while_in_do_statement=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_type_reference=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_parameterized_type_reference=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_new_line_after_opening_brace_in_array_initializer=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_declaration=insert
|
||||
org.eclipse.jdt.core.formatter.indent_breaks_compare_to_cases=true
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_declaration=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_try=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_lambda_arrow=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_declaration=do not insert
|
||||
org.eclipse.jdt.core.formatter.keep_imple_if_on_one_line=false
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_declaration=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_between_brackets_in_array_type_reference=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_parameters=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_for=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_allocation_expression=do not insert
|
||||
org.eclipse.jdt.core.formatter.alignment_for_multiple_fields=16
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_constant_arguments=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_prefix_operator=do not insert
|
||||
org.eclipse.jdt.core.formatter.brace_position_for_array_initializer=end_of_line
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_method_declaration=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_catch=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_parameters=insert
|
||||
org.eclipse.jdt.core.compiler.compliance=17
|
||||
org.eclipse.jdt.core.formatter.insert_space_between_empty_braces_in_array_initializer=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_local_declarations=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_annotation_type_declaration=insert
|
||||
org.eclipse.jdt.core.formatter.wrap_outer_expressions_when_nested=true
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_closing_paren_in_cast=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_synchronized=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation_type_member_declaration=do not insert
|
||||
org.eclipse.jdt.core.formatter.alignment_for_expressions_in_for_loop_header=16
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_while=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_parameters=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_lambda_arrow=insert
|
||||
org.eclipse.jdt.core.formatter.join_lines_in_comments=false
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_field_declarations=do not insert
|
||||
org.eclipse.jdt.core.formatter.blank_lines_between_import_groups=1
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_at_in_annotation_type_declaration=insert
|
||||
org.eclipse.jdt.core.formatter.parentheses_positions_in_method_invocation=common_lines
|
||||
org.eclipse.jdt.core.formatter.blank_lines_after_imports=1
|
||||
org.eclipse.jdt.core.formatter.comment.insert_new_line_before_root_tags=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_throws=do not insert
|
||||
org.eclipse.jdt.core.formatter.parentheses_positions_in_switch_statement=common_lines
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_postfix_operator=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_increments=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_arguments=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_inits=do not insert
|
||||
org.eclipse.jdt.core.formatter.disabling_tag=@formatter\:off
|
||||
org.eclipse.jdt.core.formatter.alignment_for_enum_constants=16
|
||||
org.eclipse.jdt.core.formatter.blank_lines_before_imports=1
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_binary_operator=insert
|
||||
org.eclipse.jdt.core.formatter.parentheses_positions_in_if_while_statement=common_lines
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_closing_brace_in_block=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_return=insert
|
||||
org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_field=insert
|
||||
org.eclipse.jdt.core.formatter.insert_new_line_in_empty_block=insert
|
||||
org.eclipse.jdt.core.formatter.blank_lines_between_type_declarations=1
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_for=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_catch=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_switch=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_anonymous_type_declaration=insert
|
||||
org.eclipse.jdt.core.formatter.never_indent_line_comments_on_first_column=true
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_inits=insert
|
||||
org.eclipse.jdt.core.formatter.indent_statements_compare_to_block=true
|
||||
org.eclipse.jdt.core.formatter.brace_position_for_anonymous_type_declaration=end_of_line
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_question_in_wildcard=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_invocation_arguments=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_switch=insert
|
||||
org.eclipse.jdt.core.formatter.comment.line_length=80
|
||||
org.eclipse.jdt.core.formatter.use_on_off_tags=false
|
||||
org.eclipse.jdt.core.formatter.insert_space_between_empty_brackets_in_array_allocation_expression=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_constant=insert
|
||||
org.eclipse.jdt.core.formatter.brace_position_for_method_declaration=end_of_line
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_colon_in_for=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_arguments=insert
|
||||
org.eclipse.jdt.core.formatter.alignment_for_binary_expression=20
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_field_declarations=insert
|
||||
org.eclipse.jdt.core.compiler.debug.localVariable=generate
|
||||
org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_superinterfaces=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_colon_in_default=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_question_in_conditional=insert
|
||||
org.eclipse.jdt.core.formatter.brace_position_for_constructor_declaration=end_of_line
|
||||
org.eclipse.jdt.core.formatter.compact_else_if=true
|
||||
org.eclipse.jdt.core.formatter.brace_position_for_lambda_body=end_of_line
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_parameters=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_catch=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_invocation=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_invocation_arguments=insert
|
||||
org.eclipse.jdt.core.formatter.alignment_for_arguments_in_method_invocation=20
|
||||
org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_constructor_declaration=16
|
||||
org.eclipse.jdt.core.formatter.insert_new_line_before_catch_in_try_statement=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_try=insert
|
||||
org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_javadoc_comment=false
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_binary_operator=insert
|
||||
org.eclipse.jdt.core.formatter.alignment_for_expressions_in_array_initializer=16
|
||||
org.eclipse.jdt.core.formatter.number_of_empty_lines_to_preserve=1
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_colon_in_case=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_if=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_arguments=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_new_line_in_empty_type_declaration=insert
|
||||
org.eclipse.jdt.core.formatter.comment.format_line_comments=true
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_colon_in_labeled_statement=insert
|
||||
org.eclipse.jdt.core.formatter.insert_new_line_in_empty_method_body=insert
|
||||
org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_type_declaration=16
|
||||
org.eclipse.jdt.core.formatter.alignment_for_conditional_expression=84
|
||||
org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_type=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_block=insert
|
||||
org.eclipse.jdt.core.formatter.brace_position_for_enum_declaration=end_of_line
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_invocation=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_while=insert
|
||||
org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
|
||||
org.eclipse.jdt.core.formatter.join_wrapped_lines=false
|
||||
org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_constructor_declaration=do not insert
|
||||
org.eclipse.jdt.core.formatter.wrap_before_conditional_operator=true
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_allocation_expression=do not insert
|
||||
org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_cases=true
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_synchronized=do not insert
|
||||
org.eclipse.jdt.core.formatter.use_tabs_only_for_leading_indentations=true
|
||||
org.eclipse.jdt.core.formatter.parentheses_positions_in_try_clause=common_lines
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_throws=insert
|
||||
org.eclipse.jdt.core.formatter.tabulation.size=4
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_reference=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_colon_in_conditional=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_try=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_try_resources=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_question_in_wildcard=do not insert
|
||||
org.eclipse.jdt.core.formatter.continuation_indentation_for_array_initializer=1
|
||||
org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_enum_declaration=16
|
||||
org.eclipse.jdt.core.formatter.wrap_before_assignment_operator=true
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_colon_in_labeled_statement=do not insert
|
||||
org.eclipse.jdt.core.formatter.brace_position_for_switch=end_of_line
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_superinterfaces=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_parameters=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_parameters=insert
|
||||
eclipse.preferences.version=1
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_parameterized_type_reference=do not insert
|
||||
org.eclipse.jdt.core.formatter.keep_empty_array_initializer_on_one_line=false
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_constructor_declaration=do not insert
|
||||
org.eclipse.jdt.core.formatter.blank_lines_before_new_chunk=1
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_allocation_expression=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_constructor_declaration=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_parameterized_type_reference=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_arguments=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_colon_in_assert=insert
|
||||
org.eclipse.jdt.core.formatter.blank_lines_before_member_type=1
|
||||
org.eclipse.jdt.core.formatter.alignment_for_arguments_in_qualified_allocation_expression=20
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_if=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_semicolon=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_postfix_operator=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_arguments=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_cast=do not insert
|
||||
org.eclipse.jdt.core.formatter.comment.format_block_comments=true
|
||||
org.eclipse.jdt.core.formatter.alignment_for_parameters_in_method_declaration=16
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_throws=insert
|
||||
org.eclipse.jdt.core.formatter.indent_statements_compare_to_body=true
|
||||
org.eclipse.jdt.core.formatter.wrap_before_binary_operator=true
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_reference=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_annotation=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_constant_arguments=do not insert
|
||||
org.eclipse.jdt.core.formatter.parentheses_positions_in_lambda_declaration=common_lines
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_colon_in_case=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_reference=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_declaration=do not insert
|
||||
org.eclipse.jdt.core.formatter.brace_position_for_enum_constant=end_of_line
|
||||
org.eclipse.jdt.core.formatter.brace_position_for_type_declaration=end_of_line
|
||||
org.eclipse.jdt.core.formatter.blank_lines_before_package=0
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_for=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_increments=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_enum_constant=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_explicitconstructorcall_arguments=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_annotation=do not insert
|
||||
org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_constant_header=true
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_constructor_declaration=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_throws=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_parameters=do not insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_question_in_conditional=insert
|
||||
org.eclipse.jdt.core.javaFormatter=org.eclipse.jdt.core.defaultJavaFormatter
|
||||
org.eclipse.jdt.core.formatter.comment.indent_parameter_description=true
|
||||
org.eclipse.jdt.core.formatter.insert_new_line_before_finally_in_try_statement=do not insert
|
||||
org.eclipse.jdt.core.formatter.tabulation.char=space
|
||||
org.eclipse.jdt.core.formatter.lineSplit=80
|
||||
org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_switch=insert
|
||||
org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_annotation=do not insert
|
||||
|
|
@ -10,5 +10,5 @@ plugins {
|
|||
|
||||
dependencies {
|
||||
api 'org.freemarker:freemarker:[2.3.32,2.4)'
|
||||
api 'io.kubernetes:client-java:[18.0.0,19)'
|
||||
api 'com.google.code.gson:gson:2.10.1'
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ import com.google.gson.JsonArray;
|
|||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonPrimitive;
|
||||
import java.math.BigInteger;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
|
|
@ -175,6 +176,17 @@ public class GsonPtr {
|
|||
.map(JsonPrimitive::getAsInt);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the Integer value of the selected {@link JsonPrimitive}.
|
||||
*
|
||||
* @param selectors the selectors
|
||||
* @return the as string
|
||||
*/
|
||||
public Optional<BigInteger> getAsBigInteger(Object... selectors) {
|
||||
return get(JsonPrimitive.class, selectors)
|
||||
.map(JsonPrimitive::getAsBigInteger);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the Long value of the selected {@link JsonPrimitive}.
|
||||
*
|
||||
|
|
@ -186,6 +198,17 @@ public class GsonPtr {
|
|||
.map(JsonPrimitive::getAsLong);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the boolean value of the selected {@link JsonPrimitive}.
|
||||
*
|
||||
* @param selectors the selectors
|
||||
* @return the boolean
|
||||
*/
|
||||
public Optional<Boolean> getAsBoolean(Object... selectors) {
|
||||
return get(JsonPrimitive.class, selectors)
|
||||
.map(JsonPrimitive::getAsBoolean);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the selected value. This pointer must point to a
|
||||
* {@link JsonObject} or {@link JsonArray}. The selector must
|
||||
|
|
|
|||
|
|
@ -16,4 +16,8 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/**
|
||||
* General utility classes and methods that are independent of the
|
||||
* specific domain (VM operator).
|
||||
*/
|
||||
package org.jdrupes.vmoperator.util;
|
||||
10
org.jdrupes.vmoperator.vmconlet/.checkstyle
Normal file
10
org.jdrupes.vmoperator.vmconlet/.checkstyle
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<fileset-config file-format-version="1.2.0" simple-config="false" sync-formatter="false">
|
||||
<local-check-config name="Project Checks" location="/VM-Operator/checkstyle.xml" type="project" description="">
|
||||
<additional-data name="protect-config-file" value="false"/>
|
||||
</local-check-config>
|
||||
<fileset name="all" enabled="true" check-config-name="Project Checks" local="true">
|
||||
<file-match-pattern match-pattern="." include-pattern="true"/>
|
||||
</fileset>
|
||||
</fileset-config>
|
||||
7
org.jdrupes.vmoperator.vmconlet/.eclipse-pmd
Normal file
7
org.jdrupes.vmoperator.vmconlet/.eclipse-pmd
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<eclipse-pmd xmlns="http://acanda.ch/eclipse-pmd/0.8" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://acanda.ch/eclipse-pmd/0.8 http://acanda.ch/eclipse-pmd/eclipse-pmd-0.8.xsd">
|
||||
<analysis enabled="true" />
|
||||
<rulesets>
|
||||
<ruleset name="Custom Rules" ref="moodle-tools-console/ruleset.xml" refcontext="workspace" />
|
||||
</rulesets>
|
||||
</eclipse-pmd>
|
||||
4
org.jdrupes.vmoperator.vmconlet/.gitignore
vendored
Normal file
4
org.jdrupes.vmoperator.vmconlet/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
/bin/
|
||||
/bin_test/
|
||||
/generated/
|
||||
/build/
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
build.commands=org.eclipse.jdt.core.javabuilder
|
||||
connection.arguments=
|
||||
connection.gradle.distribution=GRADLE_DISTRIBUTION(WRAPPER)
|
||||
connection.java.home=null
|
||||
connection.jvm.arguments=
|
||||
connection.project.dir=..
|
||||
derived.resources=.gradle,generated
|
||||
eclipse.preferences.version=1
|
||||
natures=org.eclipse.jdt.groovy.core.groovyNature,org.eclipse.jdt.core.javanature
|
||||
project.path=\:org.jgrapes.osgi.conlets.services
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
eclipse.preferences.version=1
|
||||
encoding/<project>=UTF-8
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
eclipse.preferences.version=1
|
||||
line.separator=\n
|
||||
|
|
@ -0,0 +1,63 @@
|
|||
eclipse.preferences.version=1
|
||||
editor_save_participant_org.eclipse.jdt.ui.postsavelistener.cleanup=true
|
||||
formatter_profile=_JGrapes
|
||||
formatter_settings_version=13
|
||||
sp_cleanup.add_default_serial_version_id=true
|
||||
sp_cleanup.add_generated_serial_version_id=false
|
||||
sp_cleanup.add_missing_annotations=true
|
||||
sp_cleanup.add_missing_deprecated_annotations=true
|
||||
sp_cleanup.add_missing_methods=false
|
||||
sp_cleanup.add_missing_nls_tags=false
|
||||
sp_cleanup.add_missing_override_annotations=true
|
||||
sp_cleanup.add_missing_override_annotations_interface_methods=true
|
||||
sp_cleanup.add_serial_version_id=false
|
||||
sp_cleanup.always_use_blocks=true
|
||||
sp_cleanup.always_use_parentheses_in_expressions=false
|
||||
sp_cleanup.always_use_this_for_non_static_field_access=false
|
||||
sp_cleanup.always_use_this_for_non_static_method_access=false
|
||||
sp_cleanup.convert_functional_interfaces=false
|
||||
sp_cleanup.convert_to_enhanced_for_loop=false
|
||||
sp_cleanup.correct_indentation=false
|
||||
sp_cleanup.format_source_code=true
|
||||
sp_cleanup.format_source_code_changes_only=false
|
||||
sp_cleanup.insert_inferred_type_arguments=false
|
||||
sp_cleanup.make_local_variable_final=true
|
||||
sp_cleanup.make_parameters_final=false
|
||||
sp_cleanup.make_private_fields_final=true
|
||||
sp_cleanup.make_type_abstract_if_missing_method=false
|
||||
sp_cleanup.make_variable_declarations_final=false
|
||||
sp_cleanup.never_use_blocks=false
|
||||
sp_cleanup.never_use_parentheses_in_expressions=true
|
||||
sp_cleanup.on_save_use_additional_actions=false
|
||||
sp_cleanup.organize_imports=false
|
||||
sp_cleanup.qualify_static_field_accesses_with_declaring_class=false
|
||||
sp_cleanup.qualify_static_member_accesses_through_instances_with_declaring_class=true
|
||||
sp_cleanup.qualify_static_member_accesses_through_subtypes_with_declaring_class=true
|
||||
sp_cleanup.qualify_static_member_accesses_with_declaring_class=false
|
||||
sp_cleanup.qualify_static_method_accesses_with_declaring_class=false
|
||||
sp_cleanup.remove_private_constructors=true
|
||||
sp_cleanup.remove_redundant_type_arguments=false
|
||||
sp_cleanup.remove_trailing_whitespaces=false
|
||||
sp_cleanup.remove_trailing_whitespaces_all=true
|
||||
sp_cleanup.remove_trailing_whitespaces_ignore_empty=false
|
||||
sp_cleanup.remove_unnecessary_casts=true
|
||||
sp_cleanup.remove_unnecessary_nls_tags=false
|
||||
sp_cleanup.remove_unused_imports=false
|
||||
sp_cleanup.remove_unused_local_variables=false
|
||||
sp_cleanup.remove_unused_private_fields=true
|
||||
sp_cleanup.remove_unused_private_members=false
|
||||
sp_cleanup.remove_unused_private_methods=true
|
||||
sp_cleanup.remove_unused_private_types=true
|
||||
sp_cleanup.sort_members=false
|
||||
sp_cleanup.sort_members_all=false
|
||||
sp_cleanup.use_anonymous_class_creation=false
|
||||
sp_cleanup.use_blocks=false
|
||||
sp_cleanup.use_blocks_only_for_return_and_throw=false
|
||||
sp_cleanup.use_lambda=true
|
||||
sp_cleanup.use_parentheses_in_expressions=false
|
||||
sp_cleanup.use_this_for_non_static_field_access=false
|
||||
sp_cleanup.use_this_for_non_static_field_access_only_if_necessary=true
|
||||
sp_cleanup.use_this_for_non_static_method_access=false
|
||||
sp_cleanup.use_this_for_non_static_method_access_only_if_necessary=true
|
||||
sp_jautodoc.cleanup.add_header=false
|
||||
sp_jautodoc.cleanup.replace_header=false
|
||||
54
org.jdrupes.vmoperator.vmconlet/build.gradle
Normal file
54
org.jdrupes.vmoperator.vmconlet/build.gradle
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
plugins {
|
||||
id 'org.jdrupes.vmoperator.java-library-conventions'
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation project(':org.jdrupes.vmoperator.manager.events')
|
||||
|
||||
implementation 'org.jgrapes:org.jgrapes.webconsole.base:[1.2.0,2)'
|
||||
implementation 'org.jgrapes:org.jgrapes.webconsole.provider.vue:[1,2)'
|
||||
implementation 'org.jgrapes:org.jgrapes.webconsole.provider.jgwcvuecomponents:[1,2)'
|
||||
}
|
||||
|
||||
apply plugin: 'com.github.node-gradle.node'
|
||||
|
||||
node {
|
||||
download = true
|
||||
}
|
||||
|
||||
task extractDependencies(type: Copy) {
|
||||
from configurations.compileClasspath
|
||||
.findAll{ it.name.contains('.provider.')
|
||||
|| it.name.contains('org.jgrapes.webconsole.base')
|
||||
}
|
||||
.collect{ zipTree (it) }
|
||||
into 'build/unpacked'
|
||||
duplicatesStrategy 'include'
|
||||
}
|
||||
|
||||
task compileTs(type: NodeTask) {
|
||||
dependsOn ':npmInstall'
|
||||
dependsOn extractDependencies
|
||||
inputs.dir project.file('src')
|
||||
inputs.file project.file('tsconfig.json')
|
||||
inputs.file project.file('rollup.config.mjs')
|
||||
outputs.dir project.file('build/generated/resources')
|
||||
script = file("${rootProject.rootDir}/node_modules/rollup/dist/bin/rollup")
|
||||
args = ["-c"]
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
main {
|
||||
resources {
|
||||
srcDir project.file('build/generated/resources')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
processResources {
|
||||
dependsOn compileTs
|
||||
}
|
||||
|
||||
eclipse {
|
||||
autoBuildTasks compileTs
|
||||
}
|
||||
1
org.jdrupes.vmoperator.vmconlet/package.json
Normal file
1
org.jdrupes.vmoperator.vmconlet/package.json
Normal file
|
|
@ -0,0 +1 @@
|
|||
{}
|
||||
|
|
@ -0,0 +1 @@
|
|||
org.jdrupes.vmoperator.vmconlet.VmConletFactory
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
/*
|
||||
* Moodle Tools Console
|
||||
* Copyright (C) 2022 Michael N. Lipp
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
const l10nBundles = new Map();
|
||||
let entries = null;
|
||||
// <#list supportedLanguages() as l>
|
||||
entries = new Map();
|
||||
l10nBundles.set("${l.locale.toLanguageTag()}", entries);
|
||||
// <#list l.l10nBundle.keys as key>
|
||||
entries.set("${key}", "${l.l10nBundle.getString(key)}");
|
||||
// </#list>
|
||||
// </#list>
|
||||
|
||||
export default l10nBundles;
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
<div class="jdrupes-vmoperator-vmconlet jdrupes-vmoperator-vmconlet-preview"
|
||||
data-jgwc-on-load="orgJDrupesVmOperatorVmConlet.initPreview"
|
||||
data-jgwc-on-unload="JGConsole.jgwc.unmountVueApps">
|
||||
<div>Preview</div>
|
||||
</div>
|
||||
|
|
@ -0,0 +1,84 @@
|
|||
<div class="jdrupes-vmoperator-vmconlet jdrupes-vmoperator-vmconlet-view"
|
||||
data-jgwc-on-load="orgJDrupesVmOperatorVmConlet.initView"
|
||||
data-jgwc-on-unload="JGConsole.jgwc.unmountVueApps">
|
||||
<div class="jdrupes-vmoperator-vmconlet-view-search">
|
||||
<form>
|
||||
<label class="form__label--horizontal">
|
||||
<span>{{ localize("Filter") }}</span>
|
||||
<input type="text" class="form__input-text--with-remove"
|
||||
v-on:input="controller.updateFilter($event)">
|
||||
<span role="button" tabindex="0" class="fa fa-remove"
|
||||
v-on:click="controller.clearFilter($event)"></span>
|
||||
</label>
|
||||
</form>
|
||||
</div>
|
||||
<table
|
||||
class="table--basic--striped jdrupes-vmoperator-vmconlet-view-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th v-for="key in controller.keys"
|
||||
class="sortable" v-on:click="controller.sortBy(key)">
|
||||
{{ localize(controller.label(key)) }}<span v-if="controller.sortedByAsc(key)"
|
||||
role="button" tabindex="0">▲</span><span
|
||||
v-if="controller.sortedByDesc(key)" role="button"
|
||||
tabindex="0">▼</span>
|
||||
</th>
|
||||
<th>
|
||||
{{ localize("vmActions") }}
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<template v-for="(entry, rowIndex) in filteredData">
|
||||
<tr :class="[(rowIndex % 2) ? 'odd' : 'even']"
|
||||
:aria-expanded="(entry.name in detailsByName) ? 'true' : 'false'">
|
||||
<td v-for="key in controller.keys"
|
||||
v-bind:title="key == 'name' ? entry['name']: false"
|
||||
v-bind:rowspan="(key == 'name') && $aash.isDisclosed(scopedId(rowIndex)) ? 2 : false">
|
||||
<aash-disclosure-button v-if="key === 'name'" :type="'div'"
|
||||
:id-ref="scopedId(rowIndex)">
|
||||
<span v-html="controller.breakBeforeDots(entry[key])"></span>
|
||||
</aash-disclosure-button>
|
||||
<span v-else-if="key === 'running'"
|
||||
v-html="localize(entry[key] ? 'Yes' : 'No')"></span>
|
||||
<span v-else-if="key === 'currentRam'"
|
||||
v-html="formatMemory(BigInt(entry[key]))"></span>
|
||||
<span v-else
|
||||
v-html="controller.breakBeforeDots(entry[key])"></span>
|
||||
</td>
|
||||
<td class="jdrupes-vmoperator-vmconlet-view-action-list">
|
||||
<span role="button" v-if="!entry['running']"
|
||||
tabindex="0" class="fa fa-play" :title="localize('Start VM')"
|
||||
v-on:click="vmAction(entry.name, 'start')"></span>
|
||||
<span role="button" v-if="entry['running']"
|
||||
tabindex="0" class="fa fa-stop" :title="localize('Stop VM')"
|
||||
v-on:click="vmAction(entry.name, 'stop')"></span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr :id="scopedId(rowIndex)" v-if="$aash.isDisclosed(scopedId(rowIndex))"
|
||||
:class="[(rowIndex % 2) ? 'odd' : 'even']">
|
||||
<td colspan="4" class="details">
|
||||
<table class="table--basic table--basic--autoStriped">
|
||||
<tr>
|
||||
<td>{{ localize("maximumCpus") }}</td>
|
||||
<td>{{ entry.spec.vm.maximumCpus }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{{ localize("requestedCpus") }}</td>
|
||||
<td>{{ entry.spec.vm.maximumCpus }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{{ localize("maximumRam") }}</td>
|
||||
<td>{{ formatMemory(BigInt(entry.spec.vm.maximumRam)) }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{{ localize("requestedRam") }}</td>
|
||||
<td>{{ formatMemory(BigInt(entry.spec.vm.maximumRam)) }}</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</template>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
conletName = VM Viewer
|
||||
|
||||
currentCpus = Current CPUs
|
||||
currentRam = Current RAM
|
||||
maximumCpus = Maximum CPUs
|
||||
maximumRam = Maximum RAM
|
||||
requestedCpus = Requested CPUs
|
||||
requestedRam = Requested RAM
|
||||
running = Running
|
||||
vmActions = Actions
|
||||
vmname = Name
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
conletName = VM Anzeige
|
||||
|
||||
running = Gestartet
|
||||
currentCpus = Aktuelle CPUs
|
||||
currentRam = Akuelles RAM
|
||||
maximumCpus = Maximale CPUs
|
||||
maximumRam = Maximales RAM
|
||||
requestedCpus = Angeforderte CPUs
|
||||
requestedRam = Angefordertes RAM
|
||||
vmActions = Aktionen
|
||||
vmname = Name
|
||||
|
||||
Start\ VM = VM Starten
|
||||
Stop\ VM = VM Anhalten
|
||||
|
||||
Yes = Ja
|
||||
No = Nein
|
||||
35
org.jdrupes.vmoperator.vmconlet/rollup.config.mjs
Normal file
35
org.jdrupes.vmoperator.vmconlet/rollup.config.mjs
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
import typescript from 'rollup-plugin-typescript2';
|
||||
import postcss from 'rollup-plugin-postcss';
|
||||
|
||||
let packagePath = "org/jdrupes/vmoperator/vmconlet";
|
||||
let baseName = "VmConlet"
|
||||
let module = "build/generated/resources/" + packagePath
|
||||
+ "/" + baseName + "-functions.js";
|
||||
|
||||
let pathsMap = {
|
||||
"aash-plugin": "../../page-resource/aash-vue-components/lib/aash-vue-components.js",
|
||||
"jgconsole": "../../console-base-resource/jgconsole.js",
|
||||
"jgwc": "../../page-resource/jgwc-vue-components/jgwc-components.js",
|
||||
"l10nBundles": "./" + baseName + "-l10nBundles.ftl.js",
|
||||
"vue": "../../page-resource/vue/vue.esm-browser.js"
|
||||
}
|
||||
|
||||
export default {
|
||||
external: ['vue', 'aash-plugin', 'jgconsole', 'jgwc', 'l10nBundles'],
|
||||
input: "src/" + packagePath + "/browser/" + baseName + "-functions.ts",
|
||||
output: [
|
||||
{
|
||||
format: "esm",
|
||||
file: module,
|
||||
sourcemap: true,
|
||||
sourcemapPathTransform: (relativeSourcePath, _sourcemapPath) => {
|
||||
return relativeSourcePath.replace(/^([^/]*\/){12}/, "./");
|
||||
},
|
||||
paths: pathsMap
|
||||
}
|
||||
],
|
||||
plugins: [
|
||||
typescript(),
|
||||
postcss()
|
||||
]
|
||||
};
|
||||
|
|
@ -0,0 +1,247 @@
|
|||
/*
|
||||
* VM-Operator
|
||||
* Copyright (C) 2023 Michael N. Lipp
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.jdrupes.vmoperator.vmconlet;
|
||||
|
||||
import com.google.gson.JsonObject;
|
||||
import freemarker.core.ParseException;
|
||||
import freemarker.template.MalformedTemplateNameException;
|
||||
import freemarker.template.Template;
|
||||
import freemarker.template.TemplateNotFoundException;
|
||||
import io.kubernetes.client.custom.Quantity;
|
||||
import io.kubernetes.client.util.generic.dynamic.DynamicKubernetesObject;
|
||||
import java.io.IOException;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import org.jdrupes.json.JsonBeanDecoder;
|
||||
import org.jdrupes.json.JsonDecodeException;
|
||||
import org.jdrupes.vmoperator.manager.events.StartVm;
|
||||
import org.jdrupes.vmoperator.manager.events.StopVm;
|
||||
import org.jdrupes.vmoperator.manager.events.VmChannel;
|
||||
import org.jdrupes.vmoperator.manager.events.VmDefChanged;
|
||||
import org.jdrupes.vmoperator.manager.events.VmDefChanged.Type;
|
||||
import org.jdrupes.vmoperator.util.GsonPtr;
|
||||
import org.jgrapes.core.Channel;
|
||||
import org.jgrapes.core.Event;
|
||||
import org.jgrapes.core.Manager;
|
||||
import org.jgrapes.core.NamedChannel;
|
||||
import org.jgrapes.core.annotation.Handler;
|
||||
import org.jgrapes.webconsole.base.Conlet.RenderMode;
|
||||
import org.jgrapes.webconsole.base.ConletBaseModel;
|
||||
import org.jgrapes.webconsole.base.ConsoleConnection;
|
||||
import org.jgrapes.webconsole.base.events.AddConletRequest;
|
||||
import org.jgrapes.webconsole.base.events.AddConletType;
|
||||
import org.jgrapes.webconsole.base.events.AddPageResources.ScriptResource;
|
||||
import org.jgrapes.webconsole.base.events.ConsoleReady;
|
||||
import org.jgrapes.webconsole.base.events.NotifyConletModel;
|
||||
import org.jgrapes.webconsole.base.events.NotifyConletView;
|
||||
import org.jgrapes.webconsole.base.events.RenderConlet;
|
||||
import org.jgrapes.webconsole.base.events.RenderConletRequestBase;
|
||||
import org.jgrapes.webconsole.base.events.SetLocale;
|
||||
import org.jgrapes.webconsole.base.freemarker.FreeMarkerConlet;
|
||||
|
||||
/**
|
||||
*/
|
||||
@SuppressWarnings("PMD.DataflowAnomalyAnalysis")
|
||||
public class VmConlet extends FreeMarkerConlet<VmConlet.VmsModel> {
|
||||
|
||||
private static final Set<RenderMode> MODES = RenderMode.asSet(
|
||||
RenderMode.Preview, RenderMode.View);
|
||||
private final Map<String, DynamicKubernetesObject> vmInfos
|
||||
= new ConcurrentHashMap<>();
|
||||
|
||||
/**
|
||||
* Creates a new component with its channel set to the given channel.
|
||||
*
|
||||
* @param componentChannel the channel that the component's handlers listen
|
||||
* on by default and that {@link Manager#fire(Event, Channel...)}
|
||||
* sends the event to
|
||||
*/
|
||||
public VmConlet(Channel componentChannel) {
|
||||
super(componentChannel);
|
||||
}
|
||||
|
||||
/**
|
||||
* On {@link ConsoleReady}, fire the {@link AddConletType}.
|
||||
*
|
||||
* @param event the event
|
||||
* @param channel the channel
|
||||
* @throws TemplateNotFoundException the template not found exception
|
||||
* @throws MalformedTemplateNameException the malformed template name
|
||||
* exception
|
||||
* @throws ParseException the parse exception
|
||||
* @throws IOException Signals that an I/O exception has occurred.
|
||||
*/
|
||||
@Handler
|
||||
public void onConsoleReady(ConsoleReady event, ConsoleConnection channel)
|
||||
throws TemplateNotFoundException, MalformedTemplateNameException,
|
||||
ParseException, IOException {
|
||||
// Add conlet resources to page
|
||||
channel.respond(new AddConletType(type())
|
||||
.setDisplayNames(
|
||||
localizations(channel.supportedLocales(), "conletName"))
|
||||
.addRenderMode(RenderMode.Preview)
|
||||
.addScript(new ScriptResource().setScriptType("module")
|
||||
.setScriptUri(event.renderSupport().conletResource(
|
||||
type(), "VmConlet-functions.js"))));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Optional<VmsModel> createNewState(AddConletRequest event,
|
||||
ConsoleConnection connection, String conletId) throws Exception {
|
||||
return Optional.of(new VmsModel(conletId));
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("PMD.AvoidInstantiatingObjectsInLoops")
|
||||
protected Set<RenderMode> doRenderConlet(RenderConletRequestBase<?> event,
|
||||
ConsoleConnection channel, String conletId, VmsModel conletState)
|
||||
throws Exception {
|
||||
Set<RenderMode> renderedAs = new HashSet<>();
|
||||
boolean sendData = false;
|
||||
if (event.renderAs().contains(RenderMode.Preview)) {
|
||||
Template tpl
|
||||
= freemarkerConfig().getTemplate("VmConlet-preview.ftl.html");
|
||||
channel.respond(new RenderConlet(type(), conletId,
|
||||
processTemplate(event, tpl,
|
||||
fmModel(event, channel, conletId, conletState)))
|
||||
.setRenderAs(
|
||||
RenderMode.Preview.addModifiers(event.renderAs()))
|
||||
.setSupportedModes(MODES));
|
||||
renderedAs.add(RenderMode.View);
|
||||
sendData = true;
|
||||
}
|
||||
if (event.renderAs().contains(RenderMode.View)) {
|
||||
Template tpl
|
||||
= freemarkerConfig().getTemplate("VmConlet-view.ftl.html");
|
||||
channel.respond(new RenderConlet(type(), conletId,
|
||||
processTemplate(event, tpl,
|
||||
fmModel(event, channel, conletId, conletState)))
|
||||
.setRenderAs(
|
||||
RenderMode.View.addModifiers(event.renderAs()))
|
||||
.setSupportedModes(MODES));
|
||||
renderedAs.add(RenderMode.View);
|
||||
sendData = true;
|
||||
}
|
||||
if (sendData) {
|
||||
for (var vmInfo : vmInfos.values()) {
|
||||
var def = JsonBeanDecoder.create(vmInfo.getRaw().toString())
|
||||
.readObject();
|
||||
channel.respond(new NotifyConletView(type(),
|
||||
conletId, "updateVm", def));
|
||||
}
|
||||
}
|
||||
|
||||
return renderedAs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Track the VM definitions.
|
||||
*
|
||||
* @param event the event
|
||||
* @param channel the channel
|
||||
* @throws JsonDecodeException
|
||||
*/
|
||||
@Handler(namedChannels = "manager")
|
||||
@SuppressWarnings({ "PMD.ConfusingTernary",
|
||||
"PMD.AvoidInstantiatingObjectsInLoops" })
|
||||
public void onVmDefChanged(VmDefChanged event, VmChannel channel)
|
||||
throws JsonDecodeException {
|
||||
if (event.type() == Type.DELETED) {
|
||||
vmInfos.remove(event.vmDefinition().getMetadata().getName());
|
||||
for (var entry : conletIdsByConsoleConnection().entrySet()) {
|
||||
for (String conletId : entry.getValue()) {
|
||||
entry.getKey().respond(new NotifyConletView(type(),
|
||||
conletId, "removeVm"));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
var vmDef = new DynamicKubernetesObject(
|
||||
event.vmDefinition().getRaw().deepCopy());
|
||||
GsonPtr.to(vmDef.getRaw()).to("metadata").get(JsonObject.class)
|
||||
.remove("managedFields");
|
||||
var vmSpec = GsonPtr.to(vmDef.getRaw()).to("spec", "vm");
|
||||
vmSpec.set("maximumRam", Quantity.fromString(
|
||||
vmSpec.getAsString("maximumRam").orElse("0")).getNumber()
|
||||
.toBigInteger().toString());
|
||||
vmSpec.set("currentRam", Quantity.fromString(
|
||||
vmSpec.getAsString("currentRam").orElse("0")).getNumber()
|
||||
.toBigInteger().toString());
|
||||
var status = GsonPtr.to(vmDef.getRaw()).to("status");
|
||||
status.set("ram", Quantity.fromString(
|
||||
status.getAsString("ram").orElse("0")).getNumber()
|
||||
.toBigInteger().toString());
|
||||
String vmName = event.vmDefinition().getMetadata().getName();
|
||||
vmInfos.put(vmName, vmDef);
|
||||
|
||||
// Extract running
|
||||
var def = JsonBeanDecoder.create(vmDef.getRaw().toString())
|
||||
.readObject();
|
||||
for (var entry : conletIdsByConsoleConnection().entrySet()) {
|
||||
for (String conletId : entry.getValue()) {
|
||||
entry.getKey().respond(new NotifyConletView(type(),
|
||||
conletId, "updateVm", def));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doUpdateConletState(NotifyConletModel event,
|
||||
ConsoleConnection channel, VmsModel conletState)
|
||||
throws Exception {
|
||||
event.stop();
|
||||
switch (event.method()) {
|
||||
case "start":
|
||||
fire(new StartVm(event.params().asString(0),
|
||||
new NamedChannel("manager")));
|
||||
break;
|
||||
case "stop":
|
||||
fire(new StopVm(event.params().asString(0),
|
||||
new NamedChannel("manager")));
|
||||
break;
|
||||
default:// ignore
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean doSetLocale(SetLocale event, ConsoleConnection channel,
|
||||
String conletId) throws Exception {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* The Class VmsModel.
|
||||
*/
|
||||
public class VmsModel extends ConletBaseModel {
|
||||
|
||||
/**
|
||||
* Instantiates a new vms model.
|
||||
*
|
||||
* @param conletId the conlet id
|
||||
*/
|
||||
public VmsModel(String conletId) {
|
||||
super(conletId);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,54 @@
|
|||
/*
|
||||
* VM-Operator
|
||||
* Copyright (C) 2023 Michael N. Lipp
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.jdrupes.vmoperator.vmconlet;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import org.jgrapes.core.Channel;
|
||||
import org.jgrapes.core.ComponentType;
|
||||
import org.jgrapes.webconsole.base.ConletComponentFactory;
|
||||
|
||||
/**
|
||||
* The factory service for {@link VmConlet}s.
|
||||
*/
|
||||
public class VmConletFactory implements ConletComponentFactory {
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see org.jgrapes.core.ComponentFactory#componentType()
|
||||
*/
|
||||
@Override
|
||||
public Class<? extends ComponentType> componentType() {
|
||||
return VmConlet.class;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see org.jgrapes.core.ComponentFactory#create(org.jgrapes.core.Channel,
|
||||
* java.util.Map)
|
||||
*/
|
||||
@Override
|
||||
public Optional<ComponentType> create(Channel componentChannel,
|
||||
Map<?, ?> properties) {
|
||||
return Optional.of(new VmConlet(componentChannel));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,148 @@
|
|||
/*
|
||||
* VM-Operator
|
||||
* Copyright (C) 2023 Michael N. Lipp
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { reactive, ref, createApp, computed, onMounted } from "vue";
|
||||
import JGConsole from "jgconsole";
|
||||
import JgwcPlugin, { JGWC } from "jgwc";
|
||||
import { provideApi, getApi } from "aash-plugin";
|
||||
import l10nBundles from "l10nBundles";
|
||||
|
||||
import "./VmConlet-style.scss";
|
||||
|
||||
//
|
||||
// Helpers
|
||||
//
|
||||
let unitMap = new Map<string, bigint>();
|
||||
let unitMappings = new Array<{ key: string; value: bigint }>();
|
||||
let memorySize = /^\\s*(\\d+(\\.\\d+)?)\\s*([A-Za-z]*)\\s*/;
|
||||
|
||||
// SI units and common abbreviations
|
||||
let factor = BigInt("1");
|
||||
unitMap.set("", factor);
|
||||
let scale = BigInt("1000");
|
||||
for (let unit of ["B", "kB", "MB", "GB", "TB", "PB", "EB"]) {
|
||||
unitMap.set(unit, factor);
|
||||
factor = factor * scale;
|
||||
}
|
||||
// Binary units
|
||||
factor = BigInt("1024");
|
||||
scale = BigInt("1024");
|
||||
for (let unit of ["KiB", "MiB", "GiB", "TiB", "PiB", "EiB"]) {
|
||||
unitMap.set(unit, factor);
|
||||
factor = factor * scale;
|
||||
}
|
||||
unitMap.forEach((value: bigint, key: string) => {
|
||||
unitMappings.push({ key, value });
|
||||
});
|
||||
unitMappings.sort((a, b) => a.value < b.value ? 1 : a.value > b.value ? -1 : 0);
|
||||
|
||||
function formatMemory(size: bigint): string {
|
||||
for (let mapping of unitMappings) {
|
||||
if (size >= mapping.value
|
||||
&& (size % mapping.value) === BigInt("0")) {
|
||||
return (size / mapping.value + " " + mapping.key).trim();
|
||||
}
|
||||
}
|
||||
return size.toString();
|
||||
}
|
||||
|
||||
// For global access
|
||||
declare global {
|
||||
interface Window {
|
||||
orgJDrupesVmOperatorVmConlet: any;
|
||||
}
|
||||
}
|
||||
|
||||
window.orgJDrupesVmOperatorVmConlet = {};
|
||||
|
||||
let vmInfos = reactive(new Map());
|
||||
|
||||
window.orgJDrupesVmOperatorVmConlet.initPreview
|
||||
= (previewDom: HTMLElement, isUpdate: boolean) => {
|
||||
const app = createApp({});
|
||||
app.use(JgwcPlugin, []);
|
||||
app.config.globalProperties.window = window;
|
||||
app.mount(previewDom);
|
||||
};
|
||||
|
||||
window.orgJDrupesVmOperatorVmConlet.initView = (viewDom: HTMLElement,
|
||||
isUpdate: boolean) => {
|
||||
const app = createApp({
|
||||
setup(_props: any) {
|
||||
const conletId: string
|
||||
= (<HTMLElement>viewDom.parentNode!).dataset["conletId"]!;
|
||||
|
||||
const localize = (key: string) => {
|
||||
return JGConsole.localize(
|
||||
l10nBundles, JGWC.lang() || "en", key);
|
||||
};
|
||||
|
||||
const controller = reactive(new JGConsole.TableController([
|
||||
["name", "vmname"],
|
||||
["running", "running"],
|
||||
["currentCpus", "currentCpus"],
|
||||
["currentRam", "currentRam"]
|
||||
], {
|
||||
sortKey: "name",
|
||||
sortOrder: "up"
|
||||
}));
|
||||
|
||||
let filteredData = computed(() => {
|
||||
let infos = Array.from(vmInfos.values());
|
||||
return controller.filter(infos);
|
||||
});
|
||||
|
||||
const vmAction = (vmName: string, action: string) => {
|
||||
JGConsole.notifyConletModel(conletId, action, vmName);
|
||||
};
|
||||
|
||||
const idScope = JGWC.createIdScope();
|
||||
const detailsByName = reactive(new Set());
|
||||
|
||||
return {
|
||||
controller, vmInfos, filteredData, detailsByName,
|
||||
localize, formatMemory, vmAction,
|
||||
scopedId: (id: string) => { return idScope.scopedId(id); }
|
||||
}
|
||||
}
|
||||
});
|
||||
app.use(JgwcPlugin);
|
||||
app.config.globalProperties.window = window;
|
||||
app.mount(viewDom);
|
||||
};
|
||||
|
||||
JGConsole.registerConletFunction("org.jdrupes.vmoperator.vmconlet.VmConlet",
|
||||
"updateVm", function(conletId: String, vmDefinition: any) {
|
||||
// Add some short-cuts for table controller
|
||||
vmDefinition.name = vmDefinition.metadata.name;
|
||||
vmDefinition.currentCpus = vmDefinition.status.cpus;
|
||||
vmDefinition.currentRam = vmDefinition.status.ram;
|
||||
for (let condition of vmDefinition.status.conditions) {
|
||||
if (condition.type === "Running") {
|
||||
vmDefinition.running = condition.status === "True";
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
vmInfos.set(vmDefinition.name, vmDefinition);
|
||||
});
|
||||
|
||||
JGConsole.registerConletFunction("org.jdrupes.vmoperator.vmconlet.VmConlet",
|
||||
"removeVm", function(conletId: String, vmName: String) {
|
||||
vmInfos.delete(vmName);
|
||||
});
|
||||
|
|
@ -0,0 +1,51 @@
|
|||
/*
|
||||
* VM-Operator
|
||||
* Copyright (C) 2023 Michael N. Lipp
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Conlet specific styles.
|
||||
*/
|
||||
|
||||
.jdrupes-vmoperator-vmconlet-view-search {
|
||||
display: flex;
|
||||
justify-content: flex-end
|
||||
}
|
||||
|
||||
.jdrupes-vmoperator-vmconlet-view-search form {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.jdrupes-vmoperator-vmconlet-view-action-list {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.jdrupes-vmoperator-vmconlet-view-action-list [role=button]:not(:last-child) {
|
||||
margin-right: 0.5em;
|
||||
}
|
||||
|
||||
.jdrupes-vmoperator-vmconlet-view td {
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
.jdrupes-vmoperator-vmconlet-view td:not([colspan]):first-child {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.jdrupes-vmoperator-vmconlet-view table td.details {
|
||||
padding-left: 1em;
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1 @@
|
|||
export default new Map<string, Map<string, string>>();
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
/*
|
||||
* VM-Operator
|
||||
* Copyright (C) 2023 Michael N. Lipp
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.jdrupes.vmoperator.vmconlet;
|
||||
23
org.jdrupes.vmoperator.vmconlet/tsconfig.json
Normal file
23
org.jdrupes.vmoperator.vmconlet/tsconfig.json
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"target": "es2015",
|
||||
"module": "es2015",
|
||||
"sourceMap": true,
|
||||
"inlineSources": true,
|
||||
"declaration": true,
|
||||
"importHelpers": true,
|
||||
"strict": true,
|
||||
"moduleResolution": "node",
|
||||
"experimentalDecorators": true,
|
||||
"lib": ["DOM", "ES2020"],
|
||||
"paths": {
|
||||
"aash-plugin": ["./build/unpacked/org/jgrapes/webconsole/provider/jgwcvuecomponents/aash-vue-components/lib/AashPlugin"],
|
||||
"jgconsole": ["./build/unpacked/org/jgrapes/webconsole/base/JGConsole"],
|
||||
"jgwc": ["./build/unpacked/org/jgrapes/webconsole/provider/jgwcvuecomponents/jgwc-vue-components/jgwc-components"],
|
||||
"l10nBundles": ["./src/org/jdrupes/vmoperator/vmconlet/browser/l10nBundles-stub"],
|
||||
"vue": ["./build/unpacked/org/jgrapes/webconsole/provider/vue/vue/vue"]
|
||||
}
|
||||
},
|
||||
"include": ["src/**/*.ts"],
|
||||
"exclude": ["node_modules", "l10nBundles-stub.ts"]
|
||||
}
|
||||
10226
package-lock.json
generated
Normal file
10226
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load diff
33
package.json
Normal file
33
package.json
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
{
|
||||
"devDependencies": {
|
||||
"@rollup/plugin-node-resolve": "^15.0.1",
|
||||
"@rollup/plugin-replace": "^5.0.2",
|
||||
"@rollup/plugin-terser": "^0.4.0",
|
||||
"documentation": "^14.0.1",
|
||||
"install": "^0.13.0",
|
||||
"jsdoc": "^4.0.2",
|
||||
"node-sass": "^9.0.0",
|
||||
"npm": "^8.11.0",
|
||||
"rollup": "^3.17.2",
|
||||
"rollup-plugin-peer-deps-external": "^2.2.3",
|
||||
"rollup-plugin-postcss": "^4.0.2",
|
||||
"rollup-plugin-typescript2": "^0.36.0",
|
||||
"rollup-plugin-vue": "^6.0.0",
|
||||
"sass": "^1.49.9",
|
||||
"terser": "^5.14.2",
|
||||
"tslib": "^2.3.1",
|
||||
"typedoc": "^0.25.1",
|
||||
"typedoc-plugin-missing-exports": "^2.1.0",
|
||||
"typescript": "^5.2.2"
|
||||
},
|
||||
"eslintConfig": {
|
||||
"root": true,
|
||||
"extends": "eslint:recommended",
|
||||
"rules": {
|
||||
"quotes": "off"
|
||||
}
|
||||
},
|
||||
"eslintIgnore": [
|
||||
"node_modules/**"
|
||||
]
|
||||
}
|
||||
|
|
@ -11,5 +11,8 @@
|
|||
rootProject.name = 'VM-Operator'
|
||||
|
||||
include 'org.jdrupes.vmoperator.manager'
|
||||
include 'org.jdrupes.vmoperator.manager.events'
|
||||
include 'org.jdrupes.vmoperator.vmconlet'
|
||||
include 'org.jdrupes.vmoperator.runner.qemu'
|
||||
include 'org.jdrupes.vmoperator.common'
|
||||
include 'org.jdrupes.vmoperator.util'
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue