diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml
index f47366a..547c1a4 100644
--- a/.github/workflows/gradle.yml
+++ b/.github/workflows/gradle.yml
@@ -22,10 +22,10 @@ jobs:
fetch-depth: 0
- name: Install graphviz
run: sudo apt-get install graphviz
- - name: Set up JDK 21
+ - name: Set up JDK 17
uses: actions/setup-java@v3
with:
- java-version: '21'
+ java-version: '17'
distribution: 'temurin'
- name: Build with Gradle
- run: ./gradlew -Pwebsite.push.token=${{ secrets.WEBSITE_PUSH_TOKEN }} stage
+ run: ./gradlew -Prepo.access.token=${{ secrets.REPO_ACCESS_TOKEN }} stage
diff --git a/.github/workflows/jekyll.yml b/.github/workflows/jekyll.yml
deleted file mode 100644
index d0e4ec9..0000000
--- a/.github/workflows/jekyll.yml
+++ /dev/null
@@ -1,89 +0,0 @@
-# This workflow uses actions that are not certified by GitHub.
-# They are provided by a third-party and are governed by
-# separate terms of service, privacy policy, and support
-# documentation.
-
-# Sample workflow for building and deploying a Jekyll site to GitHub Pages
-name: Deploy Jekyll site to Pages
-
-on:
- # Runs on pushes targeting the default branch
- push:
- branches: ["main"]
-
- # Allows you to run this workflow manually from the Actions tab
- workflow_dispatch:
-
-# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages
-permissions:
- contents: read
- pages: write
- id-token: write
-
-# Allow only one concurrent deployment, skipping runs queued between
-# the run in-progress and latest queued. However, do NOT cancel
-# in-progress runs as we want to allow these production deployments
-# to complete.
-concurrency:
- group: "pages"
- cancel-in-progress: false
-
-jobs:
- # Build job
- build:
- runs-on: ubuntu-latest
- steps:
- - name: Checkout
- uses: actions/checkout@v4
- with:
- fetch-depth: 0
- - name: Setup Ruby
- uses: ruby/setup-ruby@v1
- with:
- ruby-version: '3.3' # Not needed with a .ruby-version file
- bundler-cache: true # runs 'bundle install' and caches installed gems automatically
- cache-version: 0 # Increment this number if you need to re-download cached gems
- working-directory: webpages
- - name: Setup Pages
- id: pages
- uses: actions/configure-pages@v5
- - name: Build with Jekyll
- # Outputs to the './_site' directory by default
- run: cd webpages && bundle exec jekyll build
- env:
- JEKYLL_ENV: production
- - name: Install graphviz
- run: sudo apt-get install graphviz
- - name: Set up JDK 21
- uses: actions/setup-java@v3
- with:
- java-version: '21'
- distribution: 'temurin'
- - name: Build apidocs
- run: ./gradlew apidocs
- - name: Copy javadoc
- run: cp -a build/javadoc webpages/_site/
- - name: Generate the sitemap
- uses: cicirello/generate-sitemap@v1
- with:
- path-to-root: webpages/_site
- base-url-path: https://vm-operator.jdrupes.org
- - name: Index pagefind
- run: cd webpages && npx pagefind --source "_site"
- - name: Upload artifact
- # Automatically uploads an artifact from the './_site' directory by default
- uses: actions/upload-pages-artifact@v3
- with:
- path: './webpages/_site'
-
- # Deployment job
- deploy:
- environment:
- name: github-pages
- url: ${{ steps.deployment.outputs.page_url }}
- runs-on: ubuntu-latest
- needs: build
- steps:
- - name: Deploy to GitHub Pages
- id: deployment
- uses: actions/deploy-pages@v4
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index beab0c4..3e6b3c9 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -18,9 +18,10 @@ jobs:
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v4
+ - uses: actions/checkout@v3
with:
fetch-depth: 0
+ ref: main
- name: Install graphviz
run: sudo apt-get install graphviz
- name: Install podman
@@ -31,10 +32,10 @@ jobs:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- - name: Set up JDK 21
+ - name: Set up JDK 17
uses: actions/setup-java@v3
with:
- java-version: '21'
+ java-version: '17'
distribution: 'temurin'
- name: Push with Gradle
- run: ./gradlew -Pwebsite.push.token=${{ secrets.WEBSITE_PUSH_TOKEN }} -Pdocker.registry=ghcr.io/${{ github.actor }} stage publishImage
+ run: ./gradlew -Prepo.access.token=${{ secrets.REPO_ACCESS_TOKEN }} -Pdocker.registry=ghcr.io/${{ github.actor }} stage pushImages
diff --git a/.markdownlint.yaml b/.markdownlint.yaml
deleted file mode 100644
index 6ed5002..0000000
--- a/.markdownlint.yaml
+++ /dev/null
@@ -1,30 +0,0 @@
-# See [rules](https://github.com/DavidAnson/markdownlint/blob/main/schema/.markdownlint.yaml)
-
-# Default state for all rules
-default: true
-
-# MD007/ul-indent : Unordered list indentation :
-# https://github.com/DavidAnson/markdownlint/blob/v0.37.4/doc/md007.md
-MD007:
- # Spaces for indent
- indent: 2
- # Whether to indent the first level of the list
- start_indented: true
- # Spaces for first level indent (when start_indented is set)
- start_indent: 2
-
-# MD025/single-title/single-h1 : Multiple top-level headings in the same document :
-# https://github.com/DavidAnson/markdownlint/blob/v0.37.4/doc/md025.md
-MD025:
- # Heading level
- level: 1
- # RegExp for matching title in front matter (disable)
- front_matter_title: ""
-
-# MD036/no-emphasis-as-heading : Emphasis used instead of a heading :
-# https://github.com/DavidAnson/markdownlint/blob/v0.37.4/doc/md036.md
-MD036: false
-
-# MD043/required-headings : Required heading structure :
-# https://github.com/DavidAnson/markdownlint/blob/v0.37.4/doc/md043.md
-MD043: false
diff --git a/.settings/org.eclipse.buildship.core.prefs b/.settings/org.eclipse.buildship.core.prefs
index 72733d9..d0fed22 100644
--- a/.settings/org.eclipse.buildship.core.prefs
+++ b/.settings/org.eclipse.buildship.core.prefs
@@ -1,4 +1,4 @@
-arguments=--init-script /home/mnl/.config/Code/User/globalStorage/redhat.java/1.24.0/config_linux/org.eclipse.osgi/55/0/.cp/gradle/init/init.gradle --init-script /home/mnl/.config/Code/User/globalStorage/redhat.java/1.24.0/config_linux/org.eclipse.osgi/55/0/.cp/gradle/protobuf/init.gradle
+arguments=--init-script /home/mnl/.config/Code/User/globalStorage/redhat.java/1.18.0/config_linux/org.eclipse.osgi/51/0/.cp/gradle/init/init.gradle --init-script /home/mnl/.config/Code/User/globalStorage/redhat.java/1.18.0/config_linux/org.eclipse.osgi/51/0/.cp/gradle/protobuf/init.gradle
auto.sync=false
build.scans.enabled=false
connection.gradle.distribution=GRADLE_DISTRIBUTION(WRAPPER)
diff --git a/.woodpecker/build.yaml b/.woodpecker/build.yaml
deleted file mode 100644
index 56a575c..0000000
--- a/.woodpecker/build.yaml
+++ /dev/null
@@ -1,38 +0,0 @@
-when:
-- event: push
- evaluate: 'CI_SYSTEM_HOST == "woodpecker.mnl.de"'
-
-clone:
-- name: git
- image: woodpeckerci/plugin-git
- settings:
- partial: false
- tags: true
- depth: 0
-
-steps:
-- name: prepare
- image: alpine
- commands:
- # Because we run the next step as user 1000 to make podman work:
- - mkdir /woodpecker/workflow
- - chown 1000:1000 /woodpecker/workflow
- - chown -R 1000:1000 $CI_WORKSPACE
-
-- name: build-jars
- image: registry.mnl.de/mnl/jdk21-builder:v4
- environment:
- HOME: /woodpecker/workflow
- REGISTRY: registry.mnl.de
- REGISTRY_USER: mnl
- REGISTRY_TOKEN:
- from_secret: REGISTRY_TOKEN
- commands:
- - echo $REGISTRY_TOKEN | podman login -u $REGISTRY_USER --password-stdin $REGISTRY
- - ./gradlew -Pdocker.registry=$REGISTRY/$REGISTRY_USER build apidocs publishImage
- backend_options:
- kubernetes:
- securityContext:
- privileged: true
- runAsUser: 1000
- runAsGroup: 1000
diff --git a/README.md b/README.md
index 09fcd25..1895bbb 100644
--- a/README.md
+++ b/README.md
@@ -1,25 +1,13 @@
-[](https://github.com/mnlipp/VM-Operator/actions/workflows/gradle.yml)
+[](https://github.com/mnlipp/VM-Operator/actions/workflows/gradle.yml)
[](https://app.codacy.com/gh/mnlipp/VM-Operator/dashboard?utm_source=gh&utm_medium=referral&utm_content=&utm_campaign=Badge_grade)


-# Run QEMU/KVM in Kubernetes Pods
+# Run Qemu in Kubernetes Pods
-
+The goal of this project is to provide the means for running Qemu
+based VMs in Kubernetes pods.
-This project provides an easy to use and flexible solution for running
-QEMU/KVM based VMs in Kubernetes pods.
-
-The central component of this solution is the kubernetes operator that
-manages "runners". These run in pods and are used to start and manage
-the QEMU/KVM process for the VMs (optionally together with a SW-TPM).
-
-A web GUI for administrators provides an overview of the VMs together
-with some basic control over the VMs. A web GUI for users provides an
-interface to access and optionally start, stop and reset the VMs.
-
-Advanced features of the operator include pooling of VMs and automatic
-login.
-
-See the [project's home page](https://vm-operator.jdrupes.org/)
+See the [project's home page](https://mnlipp.github.io/VM-Operator/)
for details.
+
diff --git a/build.gradle b/build.gradle
index eb8e59a..1a11881 100644
--- a/build.gradle
+++ b/build.gradle
@@ -5,10 +5,9 @@ buildscript {
}
plugins {
- id 'org.ajoberstar.grgit' version '5.2.0'
+ id 'org.ajoberstar.grgit' version '5.2.0' apply false
id 'org.ajoberstar.git-publish' version '4.2.0' apply false
- id 'pl.allegro.tech.build.axion-release' version '1.17.2' apply false
- id 'org.jdrupes.vmoperator.versioning-conventions'
+ 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"
@@ -19,7 +18,7 @@ allprojects {
}
task stage {
- description = 'To be executed by CI.'
+ description = 'To be executed by CI, build and update JavaDoc.'
group = 'build'
// Build everything first
@@ -27,6 +26,11 @@ task stage {
dependsOn subprojects.tasks.collect {
tc -> tc.findByName("build") }.flatten()
}
+
+ if (JavaVersion.current() == JavaVersion.VERSION_17) {
+ // Publish JavaDoc
+ dependsOn gitPublishPush
+ }
}
eclipse {
diff --git a/buildSrc/.settings/org.eclipse.jdt.core.prefs b/buildSrc/.settings/org.eclipse.jdt.core.prefs
index b25073a..68fda12 100644
--- a/buildSrc/.settings/org.eclipse.jdt.core.prefs
+++ b/buildSrc/.settings/org.eclipse.jdt.core.prefs
@@ -1,7 +1,9 @@
-#
-#Wed Oct 02 14:48:43 CEST 2024
eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.annotation.nonnull=org.eclipse.jdt.annotation.NonNull
+org.eclipse.jdt.core.compiler.annotation.nullable=org.eclipse.jdt.annotation.Nullable
+org.eclipse.jdt.core.compiler.annotation.nullanalysis=disabled
org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
+org.eclipse.jdt.core.compiler.codegen.methodParameters=do not generate
org.eclipse.jdt.core.compiler.codegen.targetPlatform=21
org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
org.eclipse.jdt.core.compiler.compliance=21
@@ -9,5 +11,12 @@ org.eclipse.jdt.core.compiler.debug.lineNumber=generate
org.eclipse.jdt.core.compiler.debug.localVariable=generate
org.eclipse.jdt.core.compiler.debug.sourceFile=generate
org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
+org.eclipse.jdt.core.compiler.problem.enablePreviewFeatures=disabled
org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
+org.eclipse.jdt.core.compiler.problem.nullAnnotationInferenceConflict=error
+org.eclipse.jdt.core.compiler.problem.nullReference=warning
+org.eclipse.jdt.core.compiler.problem.nullSpecViolation=error
+org.eclipse.jdt.core.compiler.problem.potentialNullReference=ignore
+org.eclipse.jdt.core.compiler.problem.reportPreviewFeatures=warning
+org.eclipse.jdt.core.compiler.release=disabled
org.eclipse.jdt.core.compiler.source=21
diff --git a/buildSrc/.settings/org.eclipse.jdt.groovy.core.prefs b/buildSrc/.settings/org.eclipse.jdt.groovy.core.prefs
index 71b5e37..bf0ca13 100644
--- a/buildSrc/.settings/org.eclipse.jdt.groovy.core.prefs
+++ b/buildSrc/.settings/org.eclipse.jdt.groovy.core.prefs
@@ -1,3 +1,3 @@
eclipse.preferences.version=1
-groovy.compiler.level=-1
+groovy.compiler.level=40
groovy.script.filters=**/*.dsld,y,**/*.gradle,n
diff --git a/buildSrc/build.gradle b/buildSrc/build.gradle
index 4a5db6d..a9fb634 100644
--- a/buildSrc/build.gradle
+++ b/buildSrc/build.gradle
@@ -1,3 +1,9 @@
+/*
+ * This file was generated by the Gradle 'init' task.
+ *
+ * This project uses @Incubating APIs which are subject to change.
+ */
+
plugins {
// Support convention plugins written in Groovy. Convention plugins
// are build scripts in 'src/main' that automatically become available
@@ -8,24 +14,52 @@ plugins {
id 'eclipse'
}
+repositories {
+ // Use the plugin portal to apply community plugins in convention plugins.
+ gradlePluginPortal()
+}
+
sourceSets {
- main {
- groovy {
- srcDirs = ['src']
- }
- resources {
- srcDirs = ['resources']
- }
- }
+ main {
+ groovy {
+ srcDirs = ['src']
+ }
+ }
+
+ test {
+ groovy {
+ srcDirs = ['test']
+ }
+ }
}
eclipse {
+ project {
+ file {
+ // closure executed after .project content is loaded from existing file
+ // and before gradle build information is merged
+ beforeMerged { project ->
+ project.natures.clear()
+ project.buildCommands.clear()
+ }
+
+ project.natures += 'org.eclipse.buildship.core.gradleprojectnature'
+ // Don't build, result not used by Eclipse anyway
+ // project.buildCommand 'org.eclipse.buildship.core.gradleprojectbuilder'
+ }
+ }
+
+ classpath {
+ downloadJavadoc = true
+ downloadSources = true
+ }
+
jdt {
file {
withProperties { properties ->
def formatterPrefs = new Properties()
- rootProject.file("../gradle/org.eclipse.jdt.core.formatter.prefs")
+ rootProject.file("gradle/org.eclipse.jdt.core.formatter.prefs")
.withInputStream { formatterPrefs.load(it) }
properties.putAll(formatterPrefs)
}
diff --git a/buildSrc/settings.gradle b/buildSrc/settings.gradle
new file mode 100644
index 0000000..3f67e42
--- /dev/null
+++ b/buildSrc/settings.gradle
@@ -0,0 +1,7 @@
+/*
+ * This file was generated by the Gradle 'init' task.
+ *
+ * This settings file is used to specify which projects to include in your build-logic build.
+ */
+
+rootProject.name = 'buildSrc'
diff --git a/buildSrc/src/org.jdrupes.vmoperator.java-common-conventions.gradle b/buildSrc/src/org.jdrupes.vmoperator.java-common-conventions.gradle
index 605dc09..50aebae 100644
--- a/buildSrc/src/org.jdrupes.vmoperator.java-common-conventions.gradle
+++ b/buildSrc/src/org.jdrupes.vmoperator.java-common-conventions.gradle
@@ -5,11 +5,6 @@
*/
plugins {
- // Apply the common versioning conventions.
- // Put this at the start, because accessing project.version before
- // this is applied makes things fail.
- id 'org.jdrupes.vmoperator.versioning-conventions'
-
// Apply the java Plugin to add support for Java.
id 'java'
@@ -18,11 +13,15 @@ plugins {
// Access to git information
id 'org.ajoberstar.grgit'
+
+ // Apply the common versioning conventions.
+ id 'org.jdrupes.vmoperator.versioning-conventions'
}
repositories {
// Use Maven Central for resolving dependencies.
mavenCentral()
+ mavenLocal()
}
dependencies {
@@ -55,25 +54,21 @@ sourceSets {
java {
toolchain {
- languageVersion = JavaLanguageVersion.of(21)
+ languageVersion = JavaLanguageVersion.of(17)
}
}
jar {
manifest {
- def matchExpr = [ project.tagName + "*" ]
-
- inputs.property("gitDescriptor",
- { grgit.describe(always: true, match: matchExpr) })
+ inputs.property("gitDescriptor", { grgit.describe(always: true) })
// Set Git revision information in the manifests of built bundles
- def gitDesc = grgit.describe(always: true, match: matchExpr)
attributes([
"Implementation-Title": project.name,
- "Implementation-Version": "$project.version (built from ${gitDesc})",
+ "Implementation-Version": "$project.version (built from ${grgit.describe(always: true)})",
"Implementation-Vendor": grgit.repository.jgit.repository.config.getString("user", null, "name")
+ " (" + grgit.repository.jgit.repository.config.getString("user", null, "email") + ")",
- "Git-Descriptor": gitDesc,
+ "Git-Descriptor": grgit.describe(always: true),
"Git-SHA": grgit.head().id,
])
}
diff --git a/buildSrc/src/org.jdrupes.vmoperator.java-doc-conventions.gradle b/buildSrc/src/org.jdrupes.vmoperator.java-doc-conventions.gradle
index 6af8fa7..95d7eff 100644
--- a/buildSrc/src/org.jdrupes.vmoperator.java-doc-conventions.gradle
+++ b/buildSrc/src/org.jdrupes.vmoperator.java-doc-conventions.gradle
@@ -22,28 +22,31 @@ configurations {
}
dependencies {
- markdownDoclet "org.jdrupes.mdoclet:doclet:4.0.0"
- javadocTaglets "org.jdrupes.taglets:plantuml-taglet:3.0.0"
+ markdownDoclet "org.jdrupes.mdoclet:doclet:3.1.0"
+ javadocTaglets "org.jdrupes.taglets:plantuml-taglet:2.1.0"
+}
+
+task javadocResources(type: Copy) {
+ into file(docDestinationDir)
+ from ("${rootProject.rootDir}/misc") {
+ include '*.woff2'
+ }
}
task apidocs (type: JavaExec) {
// Does not work on JitPack, no /usr/bin/dot
- enabled = JavaVersion.current() == JavaVersion.VERSION_21
+ enabled = JavaVersion.current() == JavaVersion.VERSION_17
+
+ dependsOn javadocResources
outputs.dir(docDestinationDir)
inputs.file rootProject.file('overview.md')
- inputs.file "${rootProject.rootDir}/misc/javadoc-overwrites.css"
+ inputs.file "${rootProject.rootDir}/misc/stylesheet.css"
- jvmArgs = ['--add-exports=jdk.compiler/com.sun.tools.doclint=ALL-UNNAMED',
- '--add-exports=jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED',
- '--add-exports=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED',
- '--add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED',
- '--add-exports=jdk.javadoc/jdk.javadoc.internal.tool=ALL-UNNAMED',
- '--add-exports=jdk.javadoc/jdk.javadoc.internal.doclets.toolkit=ALL-UNNAMED',
- '--add-opens=jdk.javadoc/jdk.javadoc.internal.doclets.toolkit.resources.releases=ALL-UNNAMED',
- '-Duser.language=en', '-Duser.region=US']
- mainClass = 'jdk.javadoc.internal.tool.Main'
+ jvmArgs = ['--add-exports=jdk.javadoc/jdk.javadoc.internal.tool=ALL-UNNAMED',
+ '--add-exports=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED']
+ main = 'jdk.javadoc.internal.tool.Main'
gradle.projectsEvaluated {
// Make sure that other projects' compileClasspaths are resolved
@@ -66,8 +69,8 @@ task apidocs (type: JavaExec) {
'-package',
'-use',
'-linksource',
- '-link', 'https://docs.oracle.com/en/java/javase/21/docs/api/',
- '-link', 'https://jgrapes.org/latest-release/javadoc/',
+ '-link', 'https://docs.oracle.com/en/java/javase/17/docs/api/',
+ '-link', 'https://mnlipp.github.io/jgrapes/latest-release/javadoc/',
'-link', 'https://freemarker.apache.org/docs/api/',
'--add-exports', 'jdk.javadoc/jdk.javadoc.internal.tool=ALL-UNNAMED',
'--add-exports', 'jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED',
@@ -85,7 +88,7 @@ task apidocs (type: JavaExec) {
'-bottom', rootProject.file("misc/javadoc.bottom.txt").text,
'--allow-script-in-comments',
'-Xdoclint:-html',
- '--add-stylesheet', "${rootProject.rootDir}/misc/javadoc-overwrites.css",
+ '--main-stylesheet', "${rootProject.rootDir}/misc/stylesheet.css",
'--add-exports=jdk.javadoc/jdk.javadoc.internal.doclets.formats.html=ALL-UNNAMED',
'-quiet'
]
@@ -94,27 +97,34 @@ task apidocs (type: JavaExec) {
ignoreExitValue true
}
-task testJavadoc(type: Javadoc) {
- enabled = JavaVersion.current() == JavaVersion.VERSION_21
-
- source = fileTree(dir: 'testfiles', include: '**/*.java')
- destinationDir = project.file("build/testfiles-gradle")
- options.docletpath = configurations.markdownDoclet.files.asType(List)
- options.doclet = 'org.jdrupes.mdoclet.MDoclet'
- options.overview = 'testfiles/overview.md'
- options.addStringOption('Xdoclint:-html', '-quiet')
-
- options.setJFlags([
- '--add-exports=jdk.compiler/com.sun.tools.doclint=ALL-UNNAMED',
- '--add-exports=jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED',
- '--add-exports=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED',
- '--add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED',
- '--add-exports=jdk.javadoc/jdk.javadoc.internal.tool=ALL-UNNAMED',
- '--add-exports=jdk.javadoc/jdk.javadoc.internal.doclets.toolkit=ALL-UNNAMED',
- '--add-opens=jdk.javadoc/jdk.javadoc.internal.doclets.toolkit.resources.releases=ALL-UNNAMED'])
-}
// Prepare github authentication for plugins
if (System.properties['org.ajoberstar.grgit.auth.username'] == null) {
System.setProperty('org.ajoberstar.grgit.auth.username',
- project.rootProject.properties['website.push.token'] ?: "nouser")
+ project.rootProject.properties['repo.access.token'] ?: "nouser")
+}
+
+gitPublish {
+ repoUri = 'https://github.com/mnlipp/VM-Operator.git'
+ branch = 'gh-pages'
+ contents {
+ from("${rootProject.buildDir}/javadoc") {
+ into 'javadoc'
+ }
+ if (!findProject(':org.jdrupes.vmoperator.runner.qemu').isSnapshot
+ && !findProject(':org.jdrupes.vmoperator.manager').isSnapshot) {
+ from("${rootProject.buildDir}/javadoc") {
+ into 'latest-release/javadoc'
+ }
+ }
+ }
+ preserve { include '**/*' }
+ commitMessage = "Updated."
+}
+
+gradle.projectsEvaluated {
+ tasks.gitPublishReset.mustRunAfter subprojects.tasks
+ .collect { tc -> tc.findByName("build") }.flatten()
+ tasks.gitPublishReset.mustRunAfter subprojects.tasks
+ .collect { tc -> tc.findByName("test") }.flatten()
+ tasks.gitPublishCopy.dependsOn apidocs
}
diff --git a/buildSrc/src/org.jdrupes.vmoperator.versioning-conventions.gradle b/buildSrc/src/org.jdrupes.vmoperator.versioning-conventions.gradle
index 49b6f74..114db51 100644
--- a/buildSrc/src/org.jdrupes.vmoperator.versioning-conventions.gradle
+++ b/buildSrc/src/org.jdrupes.vmoperator.versioning-conventions.gradle
@@ -11,26 +11,21 @@ plugins {
id 'pl.allegro.tech.build.axion-release'
}
-def shortened = project.name.startsWith(project.group + ".") ?
- project.name.substring(project.group.length() + 1) : project.name
-if (shortened == "manager") {
- shortened = "manager-app";
-}
-var tagName = shortened.replace('.', '-') + "-"
-if (grgit.branch.current.name != "main"
- && grgit.branch.current.name != "HEAD"
- && !grgit.branch.current.name.startsWith("testing")
- && !grgit.branch.current.name.startsWith("release")
- && !grgit.branch.current.name.startsWith("develop")) {
- tagName = tagName + grgit.branch.current.name.replace('/', '-') + "-"
-}
-project.ext.tagName = tagName
-
scmVersion {
versionIncrementer 'incrementMinor'
tag {
- prefix = project.tagName
+ def shortened = project.name.startsWith(project.group + ".") ?
+ project.name.substring(project.group.length() + 1) : project.name
+ if (shortened == "manager") {
+ shortened = "manager-app";
+ }
+ 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
}
}
-project.version = scmVersion.version
+version = scmVersion.version
ext.isSnapshot = version.endsWith('-SNAPSHOT')
diff --git a/checkstyle.xml b/checkstyle.xml
index 088e543..b5a60d3 100644
--- a/checkstyle.xml
+++ b/checkstyle.xml
@@ -30,11 +30,8 @@
-
-
-
@@ -53,9 +50,10 @@
+
-
+
diff --git a/deploy/crds/vmpools-crd.yaml b/deploy/crds/vmpools-crd.yaml
deleted file mode 100644
index 2144940..0000000
--- a/deploy/crds/vmpools-crd.yaml
+++ /dev/null
@@ -1,74 +0,0 @@
-apiVersion: apiextensions.k8s.io/v1
-kind: CustomResourceDefinition
-metadata:
- name: vmpools.vmoperator.jdrupes.org
-spec:
- group: vmoperator.jdrupes.org
- # list of versions supported by this CustomResourceDefinition
- versions:
- - name: v1
- served: true
- storage: true
- schema:
- openAPIV3Schema:
- type: object
- properties:
- spec:
- type: object
- properties:
- retention:
- description: >-
- Defines the timeout for assignments. The time may be
- specified as ISO 8601 time or duration. When specifying
- a duration, it will be added to the last time the VM's
- console was used to obtain the timeout.
- type: string
- pattern: '^(?:\d{4}-(?:0[1-9]|1[0-2])-(?:0[1-9]|[12]\d|3[01])T(?:[01]\d|2[0-3]):[0-5]\d:[0-5]\d(?:\.\d{1,9})?(?:Z|[+-](?:[01]\d|2[0-3])(?:|:?[0-5]\d))|P(?:\d+Y)?(?:\d+M)?(?:\d+W)?(?:\d+D)?(?:T(?:\d+[Hh])?(?:\d+[Mm])?(?:\d+(?:\.\d{1,9})?[Ss])?)?)$'
- default: "PT1h"
- loginOnAssignment:
- description: >-
- If set to true, the user will be automatically logged in
- to the VM's console when the VM is assigned to him.
- type: boolean
- default: false
- permissions:
- type: array
- description: >-
- Defines permissions for accessing and manipulating the Pool.
- items:
- type: object
- description: >-
- Permissions can be granted to a user or to a role.
- oneOf:
- - required:
- - user
- - required:
- - role
- properties:
- user:
- type: string
- role:
- type: string
- may:
- type: array
- items:
- type: string
- enum:
- - start
- - stop
- - reset
- - accessConsole
- - "*"
- default: ["accessConsole"]
- required:
- - permissions
- # either Namespaced or Cluster
- scope: Namespaced
- names:
- # plural name to be used in the URL: /apis///
- plural: vmpools
- # singular name to be used as an alias on the CLI and for display
- singular: vmpool
- # kind is normally the CamelCased singular type. Your resource manifests use this.
- kind: VmPool
- listKind: VmPoolList
diff --git a/deploy/crds/vms-crd.yaml b/deploy/crds/vms-crd.yaml
index c2a7a66..7e321da 100644
--- a/deploy/crds/vms-crd.yaml
+++ b/deploy/crds/vms-crd.yaml
@@ -933,12 +933,6 @@ spec:
update:
type: boolean
default: true
- guestShutdownStops:
- description: >-
- If true, sets the VM's state to "Stopped" when
- the VM terminates due to a shutdown by the guest.
- type: boolean
- default: false
loadBalancerService:
description: >-
Data to be merged with the loadBalancerService
@@ -971,71 +965,6 @@ spec:
additionalProperties:
type: string
nullable: true
- cloudInit:
- type: object
- description: >-
- Provides data for generating a cloud-init ISO
- image that is attached to the VM.
- properties:
- metaData:
- description: Copied to cloud-init's meta-data file.
- type: object
- additionalProperties:
- type: string
- userData:
- description: Copied to cloud-init's user-data file.
- type: object
- x-kubernetes-preserve-unknown-fields: true
- networkConfig:
- description: Copied to cloud-init's network-config file.
- type: object
- x-kubernetes-preserve-unknown-fields: true
- permissions:
- type: array
- description: >-
- Defines permissions for accessing and manipulating the VM.
- The meaning of most permissions should be obvious. The
- difference between "accessConsole" and "takeConsole" is
- that "takeConsole" allows the user to take control of
- the console even if it is already in use by another user.
- items:
- type: object
- description: >-
- Permissions can be granted to a user or to a role.
- oneOf:
- - required:
- - user
- - required:
- - role
- properties:
- user:
- type: string
- role:
- type: string
- may:
- type: array
- items:
- type: string
- enum:
- - start
- - stop
- - reset
- - accessConsole
- - takeConsole
- - "*"
- default: []
- pools:
- type: array
- description: >-
- List of pools this VM belongs to.
- items:
- type: string
- default: []
- loggingProperties:
- type: string
- description: >-
- Override the default logging properties for
- the runner for this VM.
vm:
type: object
description: Defines the VM.
@@ -1427,36 +1356,13 @@ spec:
display:
type: object
properties:
- outputs:
- type: integer
- default: 1
- loggedInUser:
- description: >-
- The name of a user that should be automatically
- logged in on the display. Note that this requires
- support from an agent in the guest OS.
- type: string
spice:
type: object
properties:
port:
- description: >-
- Port number used for the Spice server.
type: integer
default: 5900
- server:
- description: >-
- Server (address) to use for connecting
- to the spice server. Defaults to the address
- of the node that the VM is running on.
- type: string
- generateSecret:
- type: boolean
- default: true
- proxyUrl:
- description: >-
- If specified, is copied to the generated
- viewer configuration files.
+ ticket:
type: string
streamingVideo:
type: string
@@ -1470,10 +1376,6 @@ spec:
type: object
default: {}
properties:
- runnerVersion:
- description: >-
- The version string of the runner.
- type: string
cpus:
description: >-
Number of CPUs currently in use.
@@ -1484,50 +1386,6 @@ spec:
Amount of memory in use.
type: string
default: "0"
- consoleClient:
- description: >-
- The hostname of the currently connected client.
- type: string
- default: ""
- consoleUser:
- description: >-
- The id of the user who has last requested a console
- connection.
- type: string
- default: ""
- loggedInUser:
- description: >-
- The name of a user that is currently logged in by the
- VM operator agent.
- type: string
- displayPasswordSerial:
- description: >-
- Counts changes of the display password. Set to -1
- by the runner if password protection is not enabled.
- type: integer
- default: 0
- osinfo:
- description: Copy of the OS info provided by the guest agent.
- type: object
- x-kubernetes-preserve-unknown-fields: true
- assignment:
- description: >-
- The assignment of this VM to a a particular user.
- type: object
- properties:
- pool:
- description: >-
- The pool this VM is taken from.
- type: string
- user:
- description: >-
- The user this VM is assigned to.
- type: string
- lastUsed:
- description: >-
- The last time this VM was used by the user.
- type: string
- default: {}
conditions:
description: >-
List of component conditions observed
@@ -1538,30 +1396,6 @@ spec:
lastTransitionTime: "1970-01-01T00:00:00Z"
reason: Creation
message: "Creation of CR"
- - type: Booted
- status: "False"
- observedGeneration: 1
- lastTransitionTime: "1970-01-01T00:00:00Z"
- reason: Creation
- message: "Creation of CR"
- - type: VmopAgentConnected
- status: "False"
- observedGeneration: 1
- lastTransitionTime: "1970-01-01T00:00:00Z"
- reason: Creation
- message: "Creation of CR"
- - type: UserLoggedIn
- status: "False"
- observedGeneration: 1
- lastTransitionTime: "1970-01-01T00:00:00Z"
- reason: Creation
- message: "Creation of CR"
- - type: ConsoleConnected
- status: "False"
- observedGeneration: 1
- lastTransitionTime: "1970-01-01T00:00:00Z"
- reason: Creation
- message: "Creation of CR"
type: array
items:
type: object
diff --git a/deploy/vmop-deployment.yaml b/deploy/vmop-deployment.yaml
index 08316f6..648cc39 100644
--- a/deploy/vmop-deployment.yaml
+++ b/deploy/vmop-deployment.yaml
@@ -21,31 +21,22 @@ spec:
- name: vm-operator
image: >-
ghcr.io/mnlipp/org.jdrupes.vmoperator.manager:latest
- imagePullPolicy: Always
- env:
- - name: JAVA_OPTS
- # The VM operator needs about 25 MB of memory, plus 1 MB for
- # each VM. The reason is that for the sake of effeciency, we
- # have to keep a parsed representation of the CRD in memory,
- # which requires about 512 KB per VM. While handling updates,
- # we temporarily have the old and the new version of the CRD
- # in memory, so we need another 512 KB per VM.
- value: "-Xmx128m"
- resources:
- requests:
- cpu: 100m
- memory: 128Mi
volumeMounts:
- name: config
mountPath: /etc/opt/vmoperator
- name: vmop-image-repository
mountPath: /var/local/vmop-image-repository
+ imagePullPolicy: Always
securityContext:
capabilities:
drop:
- ALL
readOnlyRootFilesystem: true
allowPrivilegeEscalation: false
+ resources:
+ requests:
+ cpu: 100m
+ memory: 128Mi
volumes:
- name: config
configMap:
diff --git a/deploy/vmop-role.yaml b/deploy/vmop-role.yaml
index e1ae7bc..e1ea85b 100644
--- a/deploy/vmop-role.yaml
+++ b/deploy/vmop-role.yaml
@@ -9,15 +9,8 @@ rules:
- vmoperator.jdrupes.org
resources:
- vms
- - vmpools
verbs:
- '*'
-- apiGroups:
- - vmoperator.jdrupes.org
- resources:
- - vms/status
- verbs:
- - patch
- apiGroups:
- apps
resources:
@@ -35,12 +28,8 @@ rules:
- apiGroups:
- ""
resources:
- - persistentvolumeclaims
- pods
verbs:
- - watch
- list
- - get
- - create
- delete
- patch
diff --git a/deploy/vmrunner-role.yaml b/deploy/vmrunner-role.yaml
index c6df666..54e8742 100644
--- a/deploy/vmrunner-role.yaml
+++ b/deploy/vmrunner-role.yaml
@@ -12,16 +12,9 @@ rules:
verbs:
- list
- get
- - patch
- apiGroups:
- vmoperator.jdrupes.org
resources:
- vms/status
verbs:
- patch
-- apiGroups:
- - events.k8s.io
- resources:
- - events
- verbs:
- - create
diff --git a/dev-example/.gitignore b/dev-example/.gitignore
deleted file mode 100644
index 1e31cc5..0000000
--- a/dev-example/.gitignore
+++ /dev/null
@@ -1,4 +0,0 @@
-/test-vm-ci.yaml
-/kubeconfig.yaml
-/crds/
-/.vm-operator-cmd.rc
diff --git a/dev-example/Readme.md b/dev-example/Readme.md
index d794b24..dfcd3e8 100644
--- a/dev-example/Readme.md
+++ b/dev-example/Readme.md
@@ -1,16 +1,16 @@
# Example setup for development
-The CRD must be deployed independently. Apart from that, the
-`kustomize.yaml`
-
- * creates a small cdrom image repository and
-
- * deploys the operator in namespace `vmop-dev` with a replica of 0.
+The CRD must be deployed independently. Apart from that, the
+`kustomize.yaml`
+* creates a small cdrom image repository and
+
+* deploys the operator in namespace `vmop-dev` with a replica of 0.
+
This allows you to run the manager in your IDE.
The `kustomize.yaml` also changes the container image repository for
-the operator to a private repository for development. You have to
+the operator to a private repository for development. You have to
adapt this to your own repository if you also want to test your
development version in a container.
diff --git a/dev-example/config.yaml b/dev-example/config.yaml
index 2a72bc8..4a471a8 100644
--- a/dev-example/config.yaml
+++ b/dev-example/config.yaml
@@ -1,34 +1,12 @@
# Used for running manager outside Kubernetes.
# Keep in sync with kustomize.yaml
"/Manager":
- # If provided, is shown at top left before namespace
- # clusterName: "test"
# The controller manages the VM
"/Controller":
namespace: vmop-dev
"/Reconciler":
- runnerDataPvc:
- storageClassName: rook-cephfs
- loadBalancerService:
- labels:
- label1: label1
- label2: toBeReplaced
- annotations:
- metallb.universe.tf/loadBalancerIPs: 192.168.168.1
- metallb.universe.tf/ip-allocated-from-pool: single-common
- metallb.universe.tf/allow-shared-ip: single-common
- loggingProperties: |
- # Defaults for namespace (VM domain)
- handlers=java.util.logging.ConsoleHandler
-
- #org.jgrapes.level=FINE
- #org.jgrapes.core.handlerTracking.level=FINER
-
- org.jdrupes.vmoperator.runner.qemu.level=FINEST
-
- 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
+ runnerData:
+ storageClassName: null
"/GuiSocketServer":
port: 8888
"/GuiHttpServer":
@@ -37,34 +15,17 @@
"/WebConsole":
"/LoginConlet":
users:
- - name: admin
- fullName: Administrator
- password: "$2b$05$NiBd74ZGdplLC63ePZf1f.UtjMKkbQ23cQoO2OKOFalDBHWAOy21."
- - name: operator
- fullName: Operator
- password: "$2b$05$hZaI/jToXf/d3BctZdT38Or7H7h6Pn2W3WiB49p5AyhDHFkkYCvo2"
- - name: test1
- fullName: Test Account 1
- password: "$2b$05$hZaI/jToXf/d3BctZdT38Or7H7h6Pn2W3WiB49p5AyhDHFkkYCvo2"
- - name: test2
- fullName: Test Account 2
- password: "$2b$05$hZaI/jToXf/d3BctZdT38Or7H7h6Pn2W3WiB49p5AyhDHFkkYCvo2"
- - name: test3
- fullName: Test Account 3
- password: "$2b$05$hZaI/jToXf/d3BctZdT38Or7H7h6Pn2W3WiB49p5AyhDHFkkYCvo2"
+ admin:
+ fullName: Administrator
+ password: "$2b$05$NiBd74ZGdplLC63ePZf1f.UtjMKkbQ23cQoO2OKOFalDBHWAOy21."
+ test:
+ fullName: Test Account
+ password: "$2b$05$hZaI/jToXf/d3BctZdT38Or7H7h6Pn2W3WiB49p5AyhDHFkkYCvo2"
"/RoleConfigurator":
rolesByUser:
# User admin has role admin
admin:
- admin
- operator:
- - operator
- test1:
- - user
- test2:
- - user
- test3:
- - user
# All users have role other
"*":
- other
@@ -74,17 +35,6 @@
# Admins can use all conlets
admin:
- "*"
- operator:
- - org.jdrupes.vmoperator.vmmgmt.VmMgmt
- - org.jdrupes.vmoperator.vmaccess.VmAccess
- user:
- - org.jdrupes.vmoperator.vmaccess.VmAccess
# Others cannot use any conlet (except login conlet to log out)
other:
- - org.jgrapes.webconlet.oidclogin.LoginConlet
- "/ComponentCollector":
- "/VmAccess":
- displayResource:
- preferredIpVersion: ipv4
- syncPreviewsFor:
- - role: user
+ - org.jgrapes.webconlet.locallogin.LoginConlet
diff --git a/dev-example/gen-pool-vm-crds b/dev-example/gen-pool-vm-crds
deleted file mode 100755
index f9cf692..0000000
--- a/dev-example/gen-pool-vm-crds
+++ /dev/null
@@ -1,47 +0,0 @@
-#!/bin/bash
-
-function usage() {
- cat >&2 <&2 "Unknown option: $1"; exit 1;;
- *) template="$1";;
- esac
- shift
-done
-
-if [ -z "$template" ]; then
- usage
-fi
-
-if [ "$count" = "0" ]; then
- exit 0
-fi
-for number in $(seq 1 $count); do
- if [ -z "$prefix" ]; then
- prefix=$(basename $template .tpl.yaml)
- fi
- name="$prefix$(printf %03d $number)"
- index=$(($number - 1))
- esh -o $destination/$name.yaml $template number=$number index=$index
-done
diff --git a/dev-example/kustomization.yaml b/dev-example/kustomization.yaml
index 975d95f..ae36fe1 100644
--- a/dev-example/kustomization.yaml
+++ b/dev-example/kustomization.yaml
@@ -29,20 +29,11 @@ patches:
# Keep in sync with config.yaml
config.yaml: |
"/Manager":
- # clusterName: "test"
"/Controller":
namespace: vmop-dev
"/Reconciler":
runnerData:
storageClassName: null
- loadBalancerService:
- labels:
- label1: label1
- label2: toBeReplaced
- annotations:
- metallb.universe.tf/loadBalancerIPs: 192.168.168.1
- metallb.universe.tf/ip-allocated-from-pool: single-common
- metallb.universe.tf/allow-shared-ip: single-common
"/GuiSocketServer":
port: 8888
"/GuiHttpServer":
@@ -51,29 +42,17 @@ patches:
"/WebConsole":
"/LoginConlet":
users:
- - name: admin
- fullName: Administrator
- password: "$2b$05$NiBd74ZGdplLC63ePZf1f.UtjMKkbQ23cQoO2OKOFalDBHWAOy21."
- - name: test1
- fullName: Test Account
- password: "$2b$05$hZaI/jToXf/d3BctZdT38Or7H7h6Pn2W3WiB49p5AyhDHFkkYCvo2"
- - name: test2
- fullName: Test Account
- password: "$2b$05$hZaI/jToXf/d3BctZdT38Or7H7h6Pn2W3WiB49p5AyhDHFkkYCvo2"
- - name: test3
- fullName: Test Account
- password: "$2b$05$hZaI/jToXf/d3BctZdT38Or7H7h6Pn2W3WiB49p5AyhDHFkkYCvo2"
+ admin:
+ fullName: Administrator
+ password: "$2b$05$NiBd74ZGdplLC63ePZf1f.UtjMKkbQ23cQoO2OKOFalDBHWAOy21."
+ test:
+ fullName: Test Account
+ password: "$2b$05$hZaI/jToXf/d3BctZdT38Or7H7h6Pn2W3WiB49p5AyhDHFkkYCvo2"
"/RoleConfigurator":
rolesByUser:
# User admin has role admin
admin:
- admin
- test1:
- - user
- test2:
- - user
- test3:
- - user
# All users have role other
"*":
- other
@@ -83,17 +62,10 @@ patches:
# Admins can use all conlets
admin:
- "*"
- user:
- - org.jdrupes.vmoperator.vmviewer.VmViewer
# Others cannot use any conlet (except login conlet to log out)
other:
- org.jgrapes.webconlet.locallogin.LoginConlet
- "/ComponentCollector":
- "/VmAccess":
- displayResource:
- preferredIpVersion: ipv4
- syncPreviewsFor:
- - role: user
+
- target:
group: apps
version: v1
diff --git a/dev-example/pool-action b/dev-example/pool-action
deleted file mode 100755
index bc8fbce..0000000
--- a/dev-example/pool-action
+++ /dev/null
@@ -1,66 +0,0 @@
-#!/bin/bash
-
-function usage() {
- cat >&2 <&2 "Unknown option: $1"; exit 1;;
- *) if [ ! -v pool ]; then
- pool="$1"
- elif [ ! -v action ]; then
- action="$1"
- else
- usage
- fi;;
- esac
- shift
-done
-
-if [ ! -v pool -o ! -v "action" -o ! -v context ]; then
- echo >&2 "Missing arguments or context not set."
- echo >&2
- usage
-fi
-case "$action" in
- "start"|"stop"|"delete"|"delete-disks") ;;
- *) usage;;
-esac
-
-kubectl --context="$context" -n "$namespace" get vms -o json \
- | jq -r '.items[] | select(.spec.pools | contains(["'${pool}'"])) | .metadata.name' \
-| while read vmName; do
- case "$action" in
- start) kubectl --context="$context" -n "$namespace" patch vms "$vmName" \
- --type='merge' -p '{"spec":{"vm":{"state":"Running"}}}';;
- stop) kubectl --context="$context" -n "$namespace" patch vms "$vmName" \
- --type='merge' -p '{"spec":{"vm":{"state":"Stopped"}}}';;
- delete) kubectl --context="$context" -n "$namespace" delete vm/"$vmName";;
- delete-disks) kubectl --context="$context" -n "$namespace" delete \
- pvc -l app.kubernetes.io/instance="$vmName" ;;
- esac
-done
diff --git a/dev-example/test-pool.yaml b/dev-example/test-pool.yaml
deleted file mode 100644
index 497aaf7..0000000
--- a/dev-example/test-pool.yaml
+++ /dev/null
@@ -1,17 +0,0 @@
-apiVersion: "vmoperator.jdrupes.org/v1"
-kind: VmPool
-metadata:
- namespace: vmop-dev
- name: test-vms
-spec:
- retention: "PT1m"
- loginOnAssignment: true
- permissions:
- - user: admin
- may:
- - accessConsole
- - start
- - role: user
- may:
- - accessConsole
- - start
diff --git a/dev-example/test-vm-display-secret.yaml b/dev-example/test-vm-display-secret.yaml
deleted file mode 100644
index a6f0fe6..0000000
--- a/dev-example/test-vm-display-secret.yaml
+++ /dev/null
@@ -1,13 +0,0 @@
-kind: Secret
-apiVersion: v1
-metadata:
- name: test-vm-display-secret
- namespace: vmop-dev
- labels:
- app.kubernetes.io/name: vm-runner
- app.kubernetes.io/instance: test-vm
- app.kubernetes.io/component: display-secret
-type: Opaque
-data:
- display-password: dGVzdC12bQ==
- password-expiry: KzMw
diff --git a/dev-example/test-vm-shell.yaml b/dev-example/test-vm-shell.yaml
deleted file mode 100644
index 8137694..0000000
--- a/dev-example/test-vm-shell.yaml
+++ /dev/null
@@ -1,30 +0,0 @@
-kind: Pod
-apiVersion: v1
-metadata:
- name: test-vm-shell
- namespace: vmop-dev
-spec:
- volumes:
- - name: test-vm-system-disk
- persistentVolumeClaim:
- claimName: system-disk-test-vm-0
- - name: vmop-image-repository
- persistentVolumeClaim:
- claimName: vmop-image-repository
- containers:
- - name: test-vm-shell
- image: archlinux/archlinux
- args:
- - bash
- imagePullPolicy: Always
- stdin: true
- stdinOnce: true
- tty: true
- volumeDevices:
- - name: test-vm-system-disk
- devicePath: /dev/test-vm-system-disk
- volumeMounts:
- - name: vmop-image-repository
- mountPath: /var/local/vmop-image-repository
- securityContext:
- privileged: true
diff --git a/dev-example/test-vm-snapshot.yaml b/dev-example/test-vm-snapshot.yaml
deleted file mode 100644
index fd60a25..0000000
--- a/dev-example/test-vm-snapshot.yaml
+++ /dev/null
@@ -1,10 +0,0 @@
----
-apiVersion: snapshot.storage.k8s.io/v1
-kind: VolumeSnapshot
-metadata:
- namespace: vmop-dev
- name: test-vm-system-disk-snapshot
-spec:
- volumeSnapshotClassName: csi-rbdplugin-snapclass
- source:
- persistentVolumeClaimName: test-vm-system-disk
diff --git a/dev-example/test-vm.tpl.yaml b/dev-example/test-vm.tpl.yaml
deleted file mode 100644
index 76adfba..0000000
--- a/dev-example/test-vm.tpl.yaml
+++ /dev/null
@@ -1,66 +0,0 @@
-apiVersion: "vmoperator.jdrupes.org/v1"
-kind: VirtualMachine
-metadata:
- namespace: vmop-dev
- name: test-vm<%= $(printf "%02d" ${number}) %>
- annotations:
- argocd.argoproj.io/sync-wave: "20"
-
-spec:
- image:
- source: ghcr.io/mnlipp/org.jdrupes.vmoperator.runner.qemu-arch:latest
-# source: registry.mnl.de/org/jdrupes/vm-operator/org.jdrupes.vmoperator.runner.qemu-arch:testing
-# source: docker-registry.lan.mnl.de/vmoperator/org.jdrupes.vmoperator.runner.qemu-arch:latest
- pullPolicy: Always
-
- runnerTemplate:
- update: true
-
- permissions:
- - role: admin
- may:
- - "*"
-
- guestShutdownStops: true
-
- cloudInit:
- metaData: {}
-
- pools:
- - test-vms
-
- vm:
- # state: Running
- bootMenu: true
- maximumCpus: 4
- currentCpus: 2
- maximumRam: 6Gi
- currentRam: 4Gi
-
- networks:
- # No bridge on TC1
- # - tap: {}
- - user: {}
-
- disks:
- - volumeClaimTemplate:
- metadata:
- name: system
- spec:
- storageClassName: ceph-rbd3slow
- dataSource:
- name: test-vm-system-disk-snapshot
- kind: VolumeSnapshot
- apiGroup: snapshot.storage.k8s.io
- accessModes:
- - ReadWriteOnce
- resources:
- requests:
- storage: 40Gi
- - cdrom:
- image: ""
- # image: https://download.fedoraproject.org/pub/fedora/linux/releases/38/Workstation/x86_64/iso/Fedora-Workstation-Live-x86_64-38-1.6.iso
-
- display:
- spice:
- port: <%= $((5910 + number)) %>
diff --git a/dev-example/test-vm.yaml b/dev-example/test-vm.yaml
index aa75bc3..0cd820b 100644
--- a/dev-example/test-vm.yaml
+++ b/dev-example/test-vm.yaml
@@ -5,23 +5,15 @@ metadata:
name: test-vm
spec:
image:
- source: registry.mnl.de/org/jdrupes/vm-operator/org.jdrupes.vmoperator.runner.qemu-arch:testing
+ repository: docker-registry.lan.mnl.de
+ path: vmoperator/org.jdrupes.vmoperator.runner.qemu-alpine
pullPolicy: Always
- permissions:
- - user: admin
- may:
- - "*"
-
resources:
requests:
cpu: 1
memory: 2Gi
-
- guestShutdownStops: true
-
- cloudInit: {}
-
+
vm:
# state: Running
bootMenu: yes
@@ -32,9 +24,8 @@ spec:
currentCpus: 4
networks:
- # No bridge on test cluster
- - user: {}
-
+ - tap:
+ mac: "00:16:3e:33:58:10"
disks:
- volumeClaimTemplate:
metadata:
@@ -57,6 +48,3 @@ spec:
display:
spice:
port: 5810
- generateSecret: true
-
- loadBalancerService: {}
diff --git a/dev-example/vmop-agent/99-vmop-agent.rules b/dev-example/vmop-agent/99-vmop-agent.rules
deleted file mode 100644
index 4a18472..0000000
--- a/dev-example/vmop-agent/99-vmop-agent.rules
+++ /dev/null
@@ -1,2 +0,0 @@
-SUBSYSTEM=="virtio-ports", ATTR{name}=="org.jdrupes.vmop_agent.0", \
- TAG+="systemd" ENV{SYSTEMD_WANTS}="vmop-agent.service"
diff --git a/dev-example/vmop-agent/gdm/PostLogin/Default b/dev-example/vmop-agent/gdm/PostLogin/Default
deleted file mode 100755
index 8a70890..0000000
--- a/dev-example/vmop-agent/gdm/PostLogin/Default
+++ /dev/null
@@ -1,3 +0,0 @@
-#!/bin/sh
-
-sed -i '/AutomaticLogin/d' /etc/gdm/custom.conf
diff --git a/dev-example/vmop-agent/vmop-agent b/dev-example/vmop-agent/vmop-agent
deleted file mode 100755
index 9f4d9e7..0000000
--- a/dev-example/vmop-agent/vmop-agent
+++ /dev/null
@@ -1,146 +0,0 @@
-#!/usr/bin/bash
-
-# Note that this script requires "jq" to be installed and a version
-# of loginctl that accepts the "-j" option.
-
-while [ "$#" -gt 0 ]; do
- case "$1" in
- --path) shift; ttyPath="$1";;
- --path=*) IFS='=' read -r option value <<< "$1"; ttyPath="$value";;
- esac
- shift
-done
-
-ttyPath="${ttyPath:-/dev/virtio-ports/org.jdrupes.vmop_agent.0}"
-
-if [ ! -w "$ttyPath" ]; then
- echo >&2 "Device $ttyPath not writable"
- exit 1
-fi
-
-# Create fd for the tty in variable con
-if ! exec {con}<>"$ttyPath"; then
- echo >&2 "Cannot open device $ttyPath"
- exit 1
-fi
-
-# Temporary file for logging error messages, clear tty and signal ready
-temperr=$(mktemp)
-clear >/dev/tty1
-echo >&${con} "220 Hello"
-
-# This script uses the (shared) home directory as "dictonary" for
-# synchronizing the username and the uid between hosts.
-#
-# Every user has a directory with his username. The directory is
-# owned by root to prevent changes of access rights by the user.
-# The uid and gid of the directory are equal. Thus the name of the
-# directory and the id from the group ownership also provide the
-# association between the username and the uid.
-
-# Add the user with name $1 to the host's "user database". This
-# may not be invoked concurrently.
-createUser() {
- local missing=$1
- local uid
- local userHome="/home/$missing"
- local createOpts=""
-
- # Retrieve or create the uid for the username
- if [ -d "$userHome" ]; then
- # If a home directory exists, use the id from the group ownership as uid
- uid=$(ls -ldn "$userHome" | head -n 1 | awk '{print $4}')
- createOpts="--no-create-home"
- else
- # Else get the maximum of all ids from the group ownership +1
- uid=$(ls -ln "/home" | tail -n +2 | awk '{print $4}' | sort | tail -1)
- uid=$(( $uid + 1 ))
- if [ $uid -lt 1100 ]; then
- uid=1100
- fi
- createOpts="--create-home"
- fi
- groupadd -g $uid $missing
- useradd $missing -u $uid -g $uid $createOpts
-}
-
-# Login the user, i.e. create a desktopn for the user.
-doLogin() {
- user=$1
- if [ "$user" = "root" ]; then
- echo >&${con} "504 Won't log in root"
- return
- fi
-
- # Check if this user is already logged in on tty2
- curUser=$(loginctl -j | jq -r '.[] | select(.tty=="tty2") | .user')
- if [ "$curUser" = "$user" ]; then
- echo >&${con} "201 User already logged in"
- return
- fi
-
- # Terminate a running desktop (fail safe)
- attemptLogout
-
- # Check if username is known on this host. If not, create user
- uid=$(id -u ${user} 2>/dev/null)
- if [ $? != 0 ]; then
- ( flock 200
- createUser ${user}
- ) 200>/home/.gen-uid-lock
-
- # This should now work, else something went wrong
- uid=$(id -u ${user} 2>/dev/null)
- if [ $? != 0 ]; then
- echo >&${con} "451 Cannot determine uid"
- return
- fi
- fi
-
- # Configure user as auto login user
- sed -i '/AutomaticLogin/d' /etc/gdm/custom.conf
- sed -i '/\[daemon\]/a AutomaticLoginEnable=true\nAutomaticLogin='$user \
- /etc/gdm/custom.conf
-
- # Activate user
- systemctl restart gdm
- if [ $? -eq 0 ]; then
- echo >&${con} "201 User logged in successfully"
- else
- echo >&${con} "451 $(tr '\n' ' ' <${temperr})"
- fi
-}
-
-# Attempt to log out a user currently using tty1. This is an intermediate
-# operation that can be invoked from other operations
-attemptLogout() {
- sed -i '/AutomaticLogin/d' /etc/gdm/custom.conf
- systemctl stop gdm
- echo >&${con} "102 Desktop stopped"
-}
-
-# Log out any user currently using tty1. This is invoked when executing
-# the logout command and therefore sends back a 2xx return code.
-# Also try to restart gdm, if it is not running.
-doLogout() {
- attemptLogout
- systemctl restart gdm
- echo >&${con} "202 User logged out"
-}
-
-while read line <&${con}; do
- case $line in
- "login "*) IFS=' ' read -ra args <<< "$line"; doLogin ${args[1]};;
- "logout") doLogout;;
- esac
-done
-
-onExit() {
- doLogout
- if [ -n "$temperr" ]; then
- rm -f $temperr
- fi
- echo >&${con} "240 Quit"
-}
-
-trap onExit EXIT
diff --git a/dev-example/vmop-agent/vmop-agent.service b/dev-example/vmop-agent/vmop-agent.service
deleted file mode 100644
index 11c64f2..0000000
--- a/dev-example/vmop-agent/vmop-agent.service
+++ /dev/null
@@ -1,15 +0,0 @@
-[Unit]
-Description=VM-Operator (Guest) Agent
-BindsTo=dev-virtio\x2dports-org.jdrupes.vmop_agent.0.device
-After=dev-virtio\x2dports-org.jdrupes.vmop_agent.0.device multi-user.target
-IgnoreOnIsolate=True
-
-[Service]
-UMask=0077
-#EnvironmentFile=/etc/sysconfig/vmop-agent
-ExecStart=/usr/local/libexec/vmop-agent
-Restart=always
-RestartSec=0
-
-[Install]
-WantedBy=dev-virtio\x2dports-org.jdrupes.vmop_agent.0.device
diff --git a/example/local-path/Readme.md b/example/local-path/Readme.md
index bdba8cc..7afb948 100644
--- a/example/local-path/Readme.md
+++ b/example/local-path/Readme.md
@@ -1,17 +1,17 @@
# Example setup
-The CRD must be deployed independently.
+The CRD must be deployed independently.
```sh
kubectl apply -f https://github.com/mnlipp/VM-Operator/raw/main/deploy/crds/vms-crd.yaml
```
-Apart from that, the `kustomize.yaml` defines a namespace for the manager
+Apart from that, the `kustomize.yaml` defines a namespace for the manager
(and the VMs managed by it) and patches the repository PVC to create
a small volume using local-path.
-A second patch provides a new configuration file for the manager
-that makes it use the local-path storage class when creating the
+A second patch provides a new configuration file for the manager
+that makes it use the local-path storage class when creating the
small volume for a runner's data.
The `kustomize.yaml` does not include the test VM. Before creating
diff --git a/example/rook-ceph/Readme.md b/example/rook-ceph/Readme.md
index 3756e93..1d2cfc6 100644
--- a/example/rook-ceph/Readme.md
+++ b/example/rook-ceph/Readme.md
@@ -1,12 +1,12 @@
# Example setup
-The CRD must be deployed independently.
+The CRD must be deployed independently.
```sh
kubectl apply -f https://github.com/mnlipp/VM-Operator/raw/main/deploy/crds/vms-crd.yaml
```
-Apart from that, the `kustomize.yaml` defines a namespace for the manager
+Apart from that, the `kustomize.yaml` defines a namespace for the manager
(and the VMs managed by it) and applies patches to use `rook-cephfs` as
storage class (instead of the default storage class).
diff --git a/gradle.properties b/gradle.properties
deleted file mode 100644
index f97ebb7..0000000
--- a/gradle.properties
+++ /dev/null
@@ -1 +0,0 @@
-org.gradle.parallel=true
diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar
index e644113..ccebba7 100644
Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index a441313..8707e8b 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,7 +1,6 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-8.8-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-8.1.1-all.zip
networkTimeout=10000
-validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
diff --git a/gradlew b/gradlew
index b740cf1..79a61d4 100755
--- a/gradlew
+++ b/gradlew
@@ -55,7 +55,7 @@
# Darwin, MinGW, and NonStop.
#
# (3) This script is generated from the Groovy template
-# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
+# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# within the Gradle project.
#
# You can find Gradle at https://github.com/gradle/gradle/.
@@ -83,8 +83,10 @@ done
# This is normally unused
# shellcheck disable=SC2034
APP_BASE_NAME=${0##*/}
-# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
-APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit
+APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD=maximum
@@ -131,13 +133,10 @@ location of your Java installation."
fi
else
JAVACMD=java
- if ! command -v java >/dev/null 2>&1
- then
- die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+ which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
- fi
fi
# Increase the maximum file descriptors if we can.
@@ -145,7 +144,7 @@ if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
case $MAX_FD in #(
max*)
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
- # shellcheck disable=SC2039,SC3045
+ # shellcheck disable=SC3045
MAX_FD=$( ulimit -H -n ) ||
warn "Could not query maximum file descriptor limit"
esac
@@ -153,7 +152,7 @@ if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
'' | soft) :;; #(
*)
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
- # shellcheck disable=SC2039,SC3045
+ # shellcheck disable=SC3045
ulimit -n "$MAX_FD" ||
warn "Could not set maximum file descriptor limit to $MAX_FD"
esac
@@ -198,15 +197,11 @@ if "$cygwin" || "$msys" ; then
done
fi
-
-# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
-DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
-
-# Collect all arguments for the java command:
-# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
-# and any embedded shellness will be escaped.
-# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
-# treated as '${Hostname}' itself on the command line.
+# Collect all arguments for the java command;
+# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
+# shell script including quotes and variable substitutions, so put them in
+# double quotes to make sure that they get re-expanded; and
+# * put everything else in single quotes, so that it's not re-expanded.
set -- \
"-Dorg.gradle.appname=$APP_BASE_NAME" \
diff --git a/gradlew.bat b/gradlew.bat
index 25da30d..93e3f59 100644
--- a/gradlew.bat
+++ b/gradlew.bat
@@ -43,11 +43,11 @@ set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if %ERRORLEVEL% equ 0 goto execute
-echo. 1>&2
-echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
-echo. 1>&2
-echo Please set the JAVA_HOME variable in your environment to match the 1>&2
-echo location of your Java installation. 1>&2
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
goto fail
@@ -57,11 +57,11 @@ set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto execute
-echo. 1>&2
-echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
-echo. 1>&2
-echo Please set the JAVA_HOME variable in your environment to match the 1>&2
-echo location of your Java installation. 1>&2
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
goto fail
diff --git a/misc/DejaVuSans-Bold.woff2 b/misc/DejaVuSans-Bold.woff2
new file mode 100644
index 0000000..373095f
Binary files /dev/null and b/misc/DejaVuSans-Bold.woff2 differ
diff --git a/misc/DejaVuSans.woff2 b/misc/DejaVuSans.woff2
new file mode 100644
index 0000000..8437d4e
Binary files /dev/null and b/misc/DejaVuSans.woff2 differ
diff --git a/misc/DejaVuSansMono-Bold.woff2 b/misc/DejaVuSansMono-Bold.woff2
new file mode 100644
index 0000000..f2b469a
Binary files /dev/null and b/misc/DejaVuSansMono-Bold.woff2 differ
diff --git a/misc/DejaVuSansMono.woff2 b/misc/DejaVuSansMono.woff2
new file mode 100644
index 0000000..cf200e1
Binary files /dev/null and b/misc/DejaVuSansMono.woff2 differ
diff --git a/misc/DejaVuSerif-Bold.woff2 b/misc/DejaVuSerif-Bold.woff2
new file mode 100644
index 0000000..655ac56
Binary files /dev/null and b/misc/DejaVuSerif-Bold.woff2 differ
diff --git a/misc/DejaVuSerif.woff2 b/misc/DejaVuSerif.woff2
new file mode 100644
index 0000000..238566d
Binary files /dev/null and b/misc/DejaVuSerif.woff2 differ
diff --git a/misc/javadoc-overwrites.css b/misc/javadoc-overwrites.css
deleted file mode 100644
index 7eed81f..0000000
--- a/misc/javadoc-overwrites.css
+++ /dev/null
@@ -1,2 +0,0 @@
-:root { --body-font-size: 16px;}
-:root { --code-font-size: 16px;}
diff --git a/misc/javadoc.bottom.txt b/misc/javadoc.bottom.txt
index d5589ac..bf7dd56 100644
--- a/misc/javadoc.bottom.txt
+++ b/misc/javadoc.bottom.txt
@@ -4,33 +4,26 @@
Terms
— Privacy
-
-
-
-
-
\ No newline at end of file
diff --git a/misc/stylesheet.css b/misc/stylesheet.css
new file mode 100644
index 0000000..e21b9b2
--- /dev/null
+++ b/misc/stylesheet.css
@@ -0,0 +1,912 @@
+/*
+ * Javadoc style sheet
+ */
+
+@font-face {
+ font-family: 'DejaVu Serif';
+ src: local('DejaVu Serif'), url('DejaVuSerif.woff2');
+}
+
+@font-face {
+ font-family: 'DejaVu Serif';
+ font-weight: bold;
+ src: local('DejaVu Serif Bold'), url('DejaVuSerif-Bold.woff2');
+}
+
+@font-face {
+ font-family: 'DejaVu Sans';
+ src: local('DejaVu Sans'), url('DejaVuSans.woff2');
+}
+
+@font-face {
+ font-family: 'DejaVu Sans';
+ font-weight: bold;
+ src: local('DejaVu Sans Bold'), url('DejaVuSans-Bold.woff2');
+}
+
+@font-face {
+ font-family: 'DejaVu Sans Mono';
+ src: local('DejaVu Sans Mono'), url('DejaVuSansMono.woff2');
+}
+
+@font-face {
+ font-family: 'DejaVu Sans Mono';
+ font-weight: bold;
+ src: local('DejaVu Sans Mono Bold'), url('DejaVuSansMono-Bold.woff2');
+}
+
+/*
+ * Styles for individual HTML elements.
+ *
+ * These are styles that are specific to individual HTML elements. Changing them affects the style of a particular
+ * HTML element throughout the page.
+ */
+
+body {
+ background-color:#ffffff;
+ color:#353833;
+ font: normal 16px/1.5 "DejaVu Serif", serif;
+ margin:0;
+ padding:0;
+ height:100%;
+ width:100%;
+}
+iframe {
+ margin:0;
+ padding:0;
+ height:100%;
+ width:100%;
+ overflow-y:scroll;
+ border:none;
+}
+a:link, a:visited {
+ text-decoration:none;
+ color:#4A6782;
+}
+a[href]:hover, a[href]:focus {
+ text-decoration:none;
+ color:#bb7a2a;
+}
+a[name] {
+ color:#353833;
+}
+pre {
+ font-family: "DejaVu Sans Mono", monospace;
+}
+h1 {
+ font-family: "DejaVu Sans", sans;
+ font-size:20px;
+}
+h2 {
+ font-family: "DejaVu Sans", sans;
+ font-size:18px;
+}
+h3 {
+ font-family: "DejaVu Sans", sans;
+ font-size:16px;
+}
+h4 {
+ font-family: "DejaVu Sans", sans;
+ font-size:15px;
+}
+h5 {
+ font-family: "DejaVu Sans", sans;
+ font-size:14px;
+}
+h6 {
+ font-family: "DejaVu Sans", sans;
+ font-size:13px;
+}
+ul {
+ list-style-type:disc;
+}
+code, tt {
+ font-family: "DejaVu Sans Mono", monospace;
+}
+:not(h1, h2, h3, h4, h5, h6) > code,
+:not(h1, h2, h3, h4, h5, h6) > tt {
+ /* font-size:14px; */
+ padding-top:4px;
+ margin-top:8px;
+ line-height:1.4em;
+}
+dt code {
+ font-family: "DejaVu Sans Mono", monospace;
+ font-size:14px;
+ padding-top:4px;
+}
+.summary-table dt code {
+ font-family: "DejaVu Sans Mono", monospace;
+ font-size:14px;
+ vertical-align:top;
+ padding-top:4px;
+}
+sup {
+ font-size:8px;
+}
+
+/*
+ * Styles for HTML generated by javadoc.
+ *
+ * These are style classes that are used by the standard doclet to generate HTML documentation.
+ */
+
+/*
+ * Styles for document title and copyright.
+ */
+.clear {
+ clear:both;
+ height:0;
+ overflow:hidden;
+}
+.about-language {
+ float:right;
+ padding:0 21px 8px 8px;
+ font-size:11px;
+ margin-top:-9px;
+ height:2.9em;
+}
+.legal-copy {
+ margin-left:.5em;
+}
+.tab {
+ background-color:#0066FF;
+ color:#ffffff;
+ padding:8px;
+ width:5em;
+ font-weight:bold;
+}
+/*
+ * Styles for navigation bar.
+ */
+@media screen {
+ .flex-box {
+ position:fixed;
+ display:flex;
+ flex-direction:column;
+ height: 100%;
+ width: 100%;
+ }
+ .flex-header {
+ flex: 0 0 auto;
+ }
+ .flex-content {
+ flex: 1 1 auto;
+ overflow-y: auto;
+ }
+}
+.top-nav {
+ background-color:#4D7A97;
+ color:#FFFFFF;
+ float:left;
+ padding:0;
+ width:100%;
+ clear:right;
+ min-height:2.8em;
+ padding-top:10px;
+ overflow:hidden;
+ font-family: "DejaVu Sans", sans;
+ font-size:80%;
+}
+.sub-nav {
+ background-color:#dee3e9;
+ float:left;
+ width:100%;
+ overflow:hidden;
+ font-family: "DejaVu Sans", sans;
+ font-size:80%;
+}
+.sub-nav div {
+ clear:left;
+ float:left;
+ padding:0 0 5px 6px;
+ text-transform:uppercase;
+}
+.sub-nav .nav-list {
+ padding-top:5px;
+}
+ul.nav-list {
+ display:block;
+ margin:0 25px 0 0;
+ padding:0;
+}
+ul.sub-nav-list {
+ float:left;
+ margin:0 25px 0 0;
+ padding:0;
+}
+ul.nav-list li {
+ list-style:none;
+ float:left;
+ padding: 5px 6px;
+ text-transform:uppercase;
+}
+.sub-nav .nav-list-search {
+ float:right;
+ margin:0 0 0 0;
+ padding:5px 6px;
+ clear:none;
+}
+.nav-list-search label {
+ position:relative;
+ right:-16px;
+}
+ul.sub-nav-list li {
+ list-style:none;
+ float:left;
+ padding-top:10px;
+}
+.top-nav a:link, .top-nav a:active, .top-nav a:visited {
+ color:#FFFFFF;
+ text-decoration:none;
+ text-transform:uppercase;
+}
+.top-nav a:hover {
+ text-decoration:none;
+ color:#bb7a2a;
+ text-transform:uppercase;
+}
+.nav-bar-cell1-rev {
+ background-color:#F8981D;
+ color:#253441;
+ margin: auto 5px;
+}
+.skip-nav {
+ position:absolute;
+ top:auto;
+ left:-9999px;
+ overflow:hidden;
+}
+/*
+ * Hide navigation links and search box in print layout
+ */
+@media print {
+ ul.nav-list, div.sub-nav {
+ display:none;
+ }
+}
+/*
+ * Styles for page header and footer.
+ */
+.title {
+ color:#2c4557;
+ margin:10px 0;
+}
+.sub-title {
+ margin:5px 0 0 0;
+}
+.header ul {
+ margin:0 0 15px 0;
+ padding:0;
+}
+.header ul li, .footer ul li {
+ list-style:none;
+ font-size:80%;
+}
+/*
+ * Styles for headings.
+ */
+body.class-declaration-page .summary h2,
+body.class-declaration-page .details h2,
+body.class-use-page h2,
+body.module-declaration-page .block-list h2 {
+ font-style: italic;
+ padding:0;
+ margin:15px 0;
+}
+body.class-declaration-page .summary h3,
+body.class-declaration-page .details h3,
+body.class-declaration-page .summary .inherited-list h2 {
+ background-color:#dee3e9;
+ border:1px solid #d0d9e0;
+ margin:0 0 6px -8px;
+ padding:7px 5px;
+}
+/*
+ * Styles for page layout containers.
+ */
+main {
+ clear:both;
+ padding:10px 20px;
+ position:relative;
+}
+dl.notes > dt {
+ font-family: "DejaVu Sans", sans;
+ font-weight:bold;
+ margin:10px 0 0 0;
+ color:#4E4E4E;
+}
+dl.notes > dd {
+ margin:5px 10px 10px 0;
+}
+dl.name-value > dt {
+ margin-left:1px;
+ /* font-size:1.1em; */
+ display:inline;
+ font-weight:bold;
+}
+dl.name-value > dd {
+ margin:0 0 0 1px;
+ /* font-size:1.1em; */
+ display:inline;
+}
+/*
+ * Styles for lists.
+ */
+li.circle {
+ list-style:circle;
+}
+ul.horizontal li {
+ display:inline;
+ /* font-size:0.9em; */
+}
+div.inheritance {
+ margin:0;
+ padding:0;
+}
+div.inheritance div.inheritance {
+ margin-left:2em;
+}
+ul.block-list,
+ul.details-list,
+ul.member-list,
+ul.summary-list {
+ margin:10px 0 10px 0;
+ padding:0;
+}
+ul.block-list > li,
+ul.details-list > li,
+ul.member-list > li,
+ul.summary-list > li {
+ list-style:none;
+ margin-bottom:15px;
+ line-height:1.4;
+}
+.summary-table dl, .summary-table dl dt, .summary-table dl dd {
+ margin-top:0;
+ margin-bottom:1px;
+}
+ul.see-list, ul.see-list-long {
+ padding-left: 0;
+ list-style: none;
+}
+ul.see-list li {
+ display: inline;
+}
+ul.see-list li:not(:last-child):after,
+ul.see-list-long li:not(:last-child):after {
+ content: ", ";
+ white-space: pre-wrap;
+}
+/*
+ * Styles for tables.
+ */
+.summary-table, .details-table {
+ width:100%;
+ border-spacing:0;
+ border-left:1px solid #EEE;
+ border-right:1px solid #EEE;
+ border-bottom:1px solid #EEE;
+ padding:0;
+}
+.caption {
+ position:relative;
+ text-align:left;
+ background-repeat:no-repeat;
+ color:#253441;
+ font-weight:bold;
+ clear:none;
+ overflow:hidden;
+ padding:0;
+ padding-top:10px;
+ padding-left:1px;
+ margin:0;
+ white-space:pre;
+ font-family: 'DejaVu Sans';
+}
+.caption a:link, .caption a:visited {
+ color:#1f389c;
+}
+.caption a:hover,
+.caption a:active {
+ color:#FFFFFF;
+}
+.caption span {
+ white-space:nowrap;
+ padding-top:5px;
+ padding-left:12px;
+ padding-right:12px;
+ padding-bottom:7px;
+ display:inline-block;
+ float:left;
+ background-color:#F8981D;
+ border: none;
+ height:16px;
+}
+div.table-tabs {
+ padding:10px 0 0 1px;
+ margin:0;
+}
+div.table-tabs > button {
+ border: none;
+ cursor: pointer;
+ padding: 5px 12px 7px 12px;
+ font-weight: bold;
+ margin-right: 3px;
+}
+div.table-tabs > button.active-table-tab {
+ background: #F8981D;
+ color: #253441;
+}
+div.table-tabs > button.table-tab {
+ background: #4D7A97;
+ color: #FFFFFF;
+}
+.two-column-summary {
+ display: grid;
+ grid-template-columns: minmax(15%, max-content) minmax(15%, auto);
+}
+.three-column-summary {
+ display: grid;
+ grid-template-columns: minmax(10%, max-content) minmax(15%, max-content) minmax(15%, auto);
+}
+#method-summary-table .three-column-summary {
+ grid-template-columns: minmax(10%, 20%) minmax(15%, max-content) minmax(15%, auto);
+}
+.four-column-summary {
+ display: grid;
+ grid-template-columns: minmax(10%, max-content) minmax(10%, max-content) minmax(10%, max-content) minmax(10%, auto);
+}
+@media screen and (max-width: 600px) {
+ .two-column-summary {
+ display: grid;
+ grid-template-columns: 1fr;
+ }
+}
+@media screen and (max-width: 800px) {
+ .three-column-summary {
+ display: grid;
+ grid-template-columns: minmax(10%, max-content) minmax(25%, auto);
+ }
+ .three-column-summary .col-last {
+ grid-column-end: span 2;
+ }
+}
+@media screen and (max-width: 1000px) {
+ .four-column-summary {
+ display: grid;
+ grid-template-columns: minmax(15%, max-content) minmax(15%, auto);
+ }
+}
+.summary-table > div, .details-table > div {
+ text-align:left;
+ padding: 8px 3px 3px 7px;
+}
+.col-first, .col-second, .col-last, .col-constructor-name, .col-summary-item-name {
+ vertical-align:top;
+ padding-right:0;
+ padding-top:8px;
+ padding-bottom:3px;
+}
+.table-header {
+ background:#dee3e9;
+ font-family: 'DejaVu Sans';
+ font-weight: bold;
+}
+/*
+.col-first, .col-first {
+ font-size:13px;
+}
+.col-second, .col-second, .col-last, .col-constructor-name, .col-summary-item-name, .col-last {
+ font-size:13px;
+}
+*/
+.col-first, .col-second, .col-constructor-name {
+ vertical-align:top;
+ overflow: auto;
+}
+.col-last {
+ white-space:normal;
+}
+/*
+.col-first a:link, .col-first a:visited,
+.col-second a:link, .col-second a:visited,
+.col-first a:link, .col-first a:visited,
+.col-second a:link, .col-second a:visited,
+.col-constructor-name a:link, .col-constructor-name a:visited,
+.col-summary-item-name a:link, .col-summary-item-name a:visited,
+.constant-values-container a:link, .constant-values-container a:visited,
+.all-classes-container a:link, .all-classes-container a:visited,
+.all-packages-container a:link, .all-packages-container a:visited {
+ font-weight:bold;
+}
+*/
+.table-sub-heading-color {
+ background-color:#EEEEFF;
+}
+.even-row-color, .even-row-color .table-header {
+ background-color:#FFFFFF;
+}
+.odd-row-color, .odd-row-color .table-header {
+ background-color:#EEEEEF;
+}
+/*
+ * Styles for contents.
+ */
+.deprecated-content {
+ margin:0;
+ padding:10px 0;
+}
+/*
+div.block {
+ font-size:14px;
+ font-family:'DejaVu Serif', Georgia, "Times New Roman", Times, serif;
+}
+*/
+.col-last div {
+ padding-top:0;
+}
+.col-last a {
+ padding-bottom:3px;
+}
+.module-signature,
+.package-signature,
+.type-signature,
+.member-signature {
+ font-family: "DejaVu Sans Mono", monospace;
+ /* font-size:14px; */
+ margin:14px 0;
+ white-space: pre-wrap;
+}
+.module-signature,
+.package-signature,
+.type-signature {
+ margin-top: 0;
+}
+.member-signature .type-parameters-long,
+.member-signature .parameters,
+.member-signature .exceptions {
+ display: inline-block;
+ vertical-align: top;
+ white-space: pre;
+}
+.member-signature .type-parameters {
+ white-space: normal;
+}
+/*
+ * Styles for formatting effect.
+ */
+.source-line-no {
+ color:green;
+ padding:0 30px 0 0;
+}
+h1.hidden {
+ visibility:hidden;
+ overflow:hidden;
+ /* font-size:10px; */
+}
+.block {
+ display:block;
+ margin:0 10px 5px 0;
+ color:#474747;
+}
+.deprecated-label, .descfrm-type-label, .implementation-label, .member-name-label, .member-name-link,
+.module-label-in-package, .module-label-in-type, .override-specify-label, .package-label-in-type,
+.package-hierarchy-label, .type-name-label, .type-name-link, .search-tag-link, .preview-label {
+ font-family: "DejaVu Sans", sans;
+ font-weight:bold;
+}
+.sub-title, .inheritance, .all-packages-table-tab1.col-first,
+ .summary-table .col-first {
+ font-family: "DejaVu Sans", sans;
+}
+.deprecation-comment, .help-footnote, .preview-comment {
+ font-style:italic;
+}
+.deprecation-block {
+ /* font-size:14px; */
+ font-family:'DejaVu Serif', Georgia, "Times New Roman", Times, serif;
+ border-style:solid;
+ border-width:thin;
+ border-radius:10px;
+ padding:10px;
+ margin-bottom:10px;
+ margin-right:10px;
+ display:inline-block;
+}
+.preview-block {
+ /* font-size:14px; */
+ font-family:'DejaVu Serif', Georgia, "Times New Roman", Times, serif;
+ border-style:solid;
+ border-width:thin;
+ border-radius:10px;
+ padding:10px;
+ margin-bottom:10px;
+ margin-right:10px;
+ display:inline-block;
+}
+div.block div.deprecation-comment {
+ font-style:normal;
+}
+/*
+ * Styles specific to HTML5 elements.
+ */
+main, nav, header, footer, section {
+ display:block;
+}
+/*
+ * Styles for javadoc search.
+ */
+.ui-autocomplete-category {
+ font-weight:bold;
+ /* font-size:15px; */
+ padding:7px 0 7px 3px;
+ background-color:#4D7A97;
+ color:#FFFFFF;
+}
+.result-item {
+ /* font-size:13px; */
+}
+.ui-autocomplete {
+ max-height:85%;
+ max-width:65%;
+ overflow-y:scroll;
+ overflow-x:scroll;
+ white-space:nowrap;
+ box-shadow: 0 3px 6px rgba(0,0,0,0.16), 0 3px 6px rgba(0,0,0,0.23);
+}
+ul.ui-autocomplete {
+ position:fixed;
+ z-index:999999;
+}
+ul.ui-autocomplete li {
+ float:left;
+ clear:both;
+ width:100%;
+}
+.result-highlight {
+ font-weight:bold;
+}
+#search-input {
+ background-image:url('resources/glass.png');
+ background-size:13px;
+ background-repeat:no-repeat;
+ background-position:2px 3px;
+ padding-left:20px;
+ position:relative;
+ right:-18px;
+ width:400px;
+}
+#reset-button {
+ background-color: rgb(255,255,255);
+ background-image:url('resources/x.png');
+ background-position:center;
+ background-repeat:no-repeat;
+ background-size:12px;
+ border:0 none;
+ width:16px;
+ height:16px;
+ position:relative;
+ left:-4px;
+ top:-4px;
+ font-size:0px;
+}
+.watermark {
+ color:#545454;
+}
+.search-tag-desc-result {
+ font-style:italic;
+ /* font-size:11px; */
+}
+.search-tag-holder-result {
+ font-style:italic;
+ /* font-size:12px; */
+}
+.search-tag-result:target {
+ background-color:yellow;
+}
+.module-graph span {
+ display:none;
+ position:absolute;
+}
+.module-graph:hover span {
+ display:block;
+ margin: -100px 0 0 100px;
+ z-index: 1;
+}
+.inherited-list {
+ margin: 10px 0 10px 0;
+}
+section.class-description {
+ line-height: 1.4;
+}
+.summary section[class$="-summary"], .details section[class$="-details"],
+.class-uses .detail, .serialized-class-details {
+ padding: 0px 20px 5px 10px;
+ border: 1px solid #ededed;
+ background-color: #f8f8f8;
+}
+.inherited-list, section[class$="-details"] .detail {
+ padding:0 0 5px 8px;
+ background-color:#ffffff;
+ border:none;
+}
+.vertical-separator {
+ padding: 0 5px;
+}
+ul.help-section-list {
+ margin: 0;
+}
+ul.help-subtoc > li {
+ display: inline-block;
+ padding-right: 5px;
+ /* font-size: smaller; */
+}
+ul.help-subtoc > li::before {
+ content: "\2022" ;
+ padding-right:2px;
+}
+span.help-note {
+ font-style: italic;
+}
+/*
+ * Indicator icon for external links.
+ */
+main a[href*="://"]::after {
+ content:"";
+ display:inline-block;
+ background-image:url('data:image/svg+xml; utf8, \
+ ');
+ background-size:100% 100%;
+ width:7px;
+ height:7px;
+ margin-left:2px;
+ margin-bottom:4px;
+}
+main a[href*="://"]:hover::after,
+main a[href*="://"]:focus::after {
+ background-image:url('data:image/svg+xml; utf8, \
+ ');
+}
+
+/*
+ * Styles for user-provided tables.
+ *
+ * borderless:
+ * No borders, vertical margins, styled caption.
+ * This style is provided for use with existing doc comments.
+ * In general, borderless tables should not be used for layout purposes.
+ *
+ * plain:
+ * Plain borders around table and cells, vertical margins, styled caption.
+ * Best for small tables or for complex tables for tables with cells that span
+ * rows and columns, when the "striped" style does not work well.
+ *
+ * striped:
+ * Borders around the table and vertical borders between cells, striped rows,
+ * vertical margins, styled caption.
+ * Best for tables that have a header row, and a body containing a series of simple rows.
+ */
+
+table.borderless,
+table.plain,
+table.striped {
+ margin-top: 10px;
+ margin-bottom: 10px;
+}
+table.borderless > caption,
+table.plain > caption,
+table.striped > caption {
+ font-weight: bold;
+ /* font-size: smaller; */
+}
+table.borderless th, table.borderless td,
+table.plain th, table.plain td,
+table.striped th, table.striped td {
+ padding: 2px 5px;
+}
+table.borderless,
+table.borderless > thead > tr > th, table.borderless > tbody > tr > th, table.borderless > tr > th,
+table.borderless > thead > tr > td, table.borderless > tbody > tr > td, table.borderless > tr > td {
+ border: none;
+}
+table.borderless > thead > tr, table.borderless > tbody > tr, table.borderless > tr {
+ background-color: transparent;
+}
+table.plain {
+ border-collapse: collapse;
+ border: 1px solid black;
+}
+table.plain > thead > tr, table.plain > tbody tr, table.plain > tr {
+ background-color: transparent;
+}
+table.plain > thead > tr > th, table.plain > tbody > tr > th, table.plain > tr > th,
+table.plain > thead > tr > td, table.plain > tbody > tr > td, table.plain > tr > td {
+ border: 1px solid black;
+}
+table.striped {
+ border-collapse: collapse;
+ border: 1px solid black;
+}
+table.striped > thead {
+ background-color: #E3E3E3;
+}
+table.striped > thead > tr > th, table.striped > thead > tr > td {
+ border: 1px solid black;
+}
+table.striped > tbody > tr:nth-child(even) {
+ background-color: #EEE
+}
+table.striped > tbody > tr:nth-child(odd) {
+ background-color: #FFF
+}
+table.striped > tbody > tr > th, table.striped > tbody > tr > td {
+ border-left: 1px solid black;
+ border-right: 1px solid black;
+}
+table.striped > tbody > tr > th {
+ font-weight: normal;
+}
+/**
+ * Tweak font sizes and paddings for small screens.
+ */
+@media screen and (max-width: 1050px) {
+ #search-input {
+ width: 300px;
+ }
+}
+@media screen and (max-width: 800px) {
+ #search-input {
+ width: 200px;
+ }
+ .top-nav,
+ .bottom-nav {
+ font-size: 80%;
+ padding-top: 6px;
+ }
+ .sub-nav {
+ font-size: 80%;
+ }
+ .about-language {
+ padding-right: 16px;
+ }
+ ul.nav-list li,
+ .sub-nav .nav-list-search {
+ padding: 6px;
+ }
+ ul.sub-nav-list li {
+ padding-top: 5px;
+ }
+ main {
+ padding: 10px;
+ }
+ .summary section[class$="-summary"], .details section[class$="-details"],
+ .class-uses .detail, .serialized-class-details {
+ padding: 0 8px 5px 8px;
+ }
+ body {
+ -webkit-text-size-adjust: none;
+ }
+}
+@media screen and (max-width: 500px) {
+ #search-input {
+ width: 150px;
+ }
+ .top-nav,
+ .bottom-nav {
+ font-size: 80%;
+ }
+ .sub-nav {
+ font-size: 80%;
+ }
+ .about-language {
+ font-size: 80%;
+ padding-right: 12px;
+ }
+}
diff --git a/org.jdrupes.vmoperator.common/.eclipse-pmd b/org.jdrupes.vmoperator.common/.eclipse-pmd
index 5d69caa..8b394f8 100644
--- a/org.jdrupes.vmoperator.common/.eclipse-pmd
+++ b/org.jdrupes.vmoperator.common/.eclipse-pmd
@@ -2,6 +2,6 @@
-
+
diff --git a/org.jdrupes.vmoperator.common/.settings/net.sf.jautodoc.prefs b/org.jdrupes.vmoperator.common/.settings/net.sf.jautodoc.prefs
deleted file mode 100644
index 8b8b906..0000000
--- a/org.jdrupes.vmoperator.common/.settings/net.sf.jautodoc.prefs
+++ /dev/null
@@ -1,7 +0,0 @@
-add_header=true
-eclipse.preferences.version=1
-header_text=/*\n * VM-Operator\n * Copyright (C) 2024 Michael N. Lipp\n * \n * This program is free software\: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as\n * published by the Free Software Foundation, either version 3 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see .\n */
-project_specific_settings=true
-visibility_package=false
-visibility_private=false
-visibility_protected=false
diff --git a/org.jdrupes.vmoperator.common/build.gradle b/org.jdrupes.vmoperator.common/build.gradle
index e72cb14..ed082a1 100644
--- a/org.jdrupes.vmoperator.common/build.gradle
+++ b/org.jdrupes.vmoperator.common/build.gradle
@@ -10,8 +10,5 @@ plugins {
dependencies {
api project(':org.jdrupes.vmoperator.util')
- api 'org.jgrapes:org.jgrapes.core:[1.22.1,2)'
- api 'io.kubernetes:client-java:[19.0.0,20.0.0)'
- api 'org.yaml:snakeyaml'
- api 'com.fasterxml.jackson.datatype:jackson-datatype-jsr310:[2.16.1,3]'
+ api 'io.kubernetes:client-java:[18.0.0,19)'
}
diff --git a/org.jdrupes.vmoperator.common/src/org/jdrupes/vmoperator/common/Constants.java b/org.jdrupes.vmoperator.common/src/org/jdrupes/vmoperator/common/Constants.java
index b9de69f..3ebe29d 100644
--- a/org.jdrupes.vmoperator.common/src/org/jdrupes/vmoperator/common/Constants.java
+++ b/org.jdrupes.vmoperator.common/src/org/jdrupes/vmoperator/common/Constants.java
@@ -21,7 +21,6 @@ package org.jdrupes.vmoperator.common;
/**
* Some constants.
*/
-@SuppressWarnings("PMD.DataClass")
public class Constants {
/** The Constant APP_NAME. */
@@ -30,98 +29,9 @@ public class Constants {
/** The Constant VM_OP_NAME. */
public static final String VM_OP_NAME = "vm-operator";
- /**
- * Constants related to the CRD.
- */
- @SuppressWarnings("PMD.ShortClassName")
- public static class Crd {
- /** The Constant GROUP. */
- public static final String GROUP = "vmoperator.jdrupes.org";
+ /** The Constant VM_OP_GROUP. */
+ public static final String VM_OP_GROUP = "vmoperator.jdrupes.org";
- /** The Constant KIND_VM. */
- public static final String KIND_VM = "VirtualMachine";
-
- /** The Constant KIND_VM_POOL. */
- public static final String KIND_VM_POOL = "VmPool";
- }
-
- /**
- * Status related constants.
- */
- public static class Status {
- /** The Constant RUNNER_VERSION. */
- public static final String RUNNER_VERSION = "runnerVersion";
-
- /** The Constant CPUS. */
- public static final String CPUS = "cpus";
-
- /** The Constant RAM. */
- public static final String RAM = "ram";
-
- /** The Constant OSINFO. */
- public static final String OSINFO = "osinfo";
-
- /** The Constant DISPLAY_PASSWORD_SERIAL. */
- public static final String DISPLAY_PASSWORD_SERIAL
- = "displayPasswordSerial";
-
- /** The Constant LOGGED_IN_USER. */
- public static final String LOGGED_IN_USER = "loggedInUser";
-
- /** The Constant CONSOLE_CLIENT. */
- public static final String CONSOLE_CLIENT = "consoleClient";
-
- /** The Constant CONSOLE_USER. */
- public static final String CONSOLE_USER = "consoleUser";
-
- /** The Constant ASSIGNMENT. */
- public static final String ASSIGNMENT = "assignment";
-
- /**
- * Conditions used in Status.
- */
- public static class Condition {
- /** The Constant COND_RUNNING. */
- public static final String RUNNING = "Running";
-
- /** The Constant COND_BOOTED. */
- public static final String BOOTED = "Booted";
-
- /** The Constant COND_VMOP_AGENT. */
- public static final String VMOP_AGENT = "VmopAgentConnected";
-
- /** The Constant COND_USER_LOGGED_IN. */
- public static final String USER_LOGGED_IN = "UserLoggedIn";
-
- /** The Constant COND_CONSOLE. */
- public static final String CONSOLE_CONNECTED = "ConsoleConnected";
-
- /**
- * Reasons used in conditions.
- */
- public static class Reason {
- /** The Constant NOT_REQUESTED. */
- public static final String NOT_REQUESTED = "NotRequested";
-
- /** The Constant USER_LOGGED_IN. */
- public static final String LOGGED_IN = "LoggedIn";
- }
- }
- }
-
- /**
- * DisplaySecret related constants.
- */
- public static class DisplaySecret {
-
- /** The Constant NAME. */
- public static final String NAME = "display-secret";
-
- /** The Constant PASSWORD. */
- public static final String PASSWORD = "display-password";
-
- /** The Constant EXPIRY. */
- public static final String EXPIRY = "password-expiry";
-
- }
+ /** The Constant VM_OP_KIND_VM. */
+ public static final String VM_OP_KIND_VM = "VirtualMachine";
}
diff --git a/org.jdrupes.vmoperator.common/src/org/jdrupes/vmoperator/common/Convertions.java b/org.jdrupes.vmoperator.common/src/org/jdrupes/vmoperator/common/Convertions.java
index 68f52eb..47b7208 100644
--- a/org.jdrupes.vmoperator.common/src/org/jdrupes/vmoperator/common/Convertions.java
+++ b/org.jdrupes.vmoperator.common/src/org/jdrupes/vmoperator/common/Convertions.java
@@ -32,11 +32,13 @@ import java.util.regex.Pattern;
public class Convertions {
@SuppressWarnings({ "PMD.UseConcurrentHashMap",
- "PMD.FieldNamingConventions" })
+ "PMD.FieldNamingConventions", "PMD.VariableNamingConventions" })
private static final Map unitMap = new HashMap<>();
- @SuppressWarnings({ "PMD.FieldNamingConventions" })
+ @SuppressWarnings({ "PMD.FieldNamingConventions",
+ "PMD.VariableNamingConventions" })
private static final List> unitMappings;
- @SuppressWarnings({ "PMD.FieldNamingConventions" })
+ @SuppressWarnings({ "PMD.FieldNamingConventions",
+ "PMD.VariableNamingConventions" })
private static final Pattern memorySize
= Pattern.compile("^\\s*(\\d+(\\.\\d+)?)\\s*([A-Za-z]*)\\s*");
@@ -67,6 +69,7 @@ public class Convertions {
* @param amount the amount
* @return the big integer
*/
+ @SuppressWarnings("PMD.DataflowAnomalyAnalysis")
public static BigInteger parseMemory(Object amount) {
if (amount == null) {
return (BigInteger) amount;
diff --git a/org.jdrupes.vmoperator.common/src/org/jdrupes/vmoperator/common/DynamicTypeAdapterFactory.java b/org.jdrupes.vmoperator.common/src/org/jdrupes/vmoperator/common/DynamicTypeAdapterFactory.java
deleted file mode 100644
index d21eed4..0000000
--- a/org.jdrupes.vmoperator.common/src/org/jdrupes/vmoperator/common/DynamicTypeAdapterFactory.java
+++ /dev/null
@@ -1,197 +0,0 @@
-/*
- * VM-Operator
- * Copyright (C) 2024 Michael N. Lipp
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see .
- */
-
-package org.jdrupes.vmoperator.common;
-
-import com.google.gson.Gson;
-import com.google.gson.InstanceCreator;
-import com.google.gson.JsonObject;
-import com.google.gson.TypeAdapter;
-import com.google.gson.TypeAdapterFactory;
-import com.google.gson.reflect.TypeToken;
-import com.google.gson.stream.JsonReader;
-import com.google.gson.stream.JsonWriter;
-import io.kubernetes.client.openapi.ApiClient;
-import java.io.IOException;
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Type;
-
-/**
- * A factory for creating objects.
- *
- * @param the generic type
- * @param the generic type
- */
-public class DynamicTypeAdapterFactory> implements TypeAdapterFactory {
-
- private final Class objectClass;
- private final Class objectListClass;
-
- /**
- * Make sure that this adapter is registered.
- *
- * @param client the client
- */
- public void register(ApiClient client) {
- if (!ModelCreator.class
- .equals(client.getJSON().getGson().getAdapter(objectClass)
- .getClass())
- || !ModelsCreator.class.equals(client.getJSON().getGson()
- .getAdapter(objectListClass).getClass())) {
- Gson gson = client.getJSON().getGson();
- client.getJSON().setGson(gson.newBuilder()
- .registerTypeAdapterFactory(this).create());
- }
- }
-
- /**
- * Instantiates a new generic type adapter factory.
- *
- * @param objectClass the object class
- * @param objectListClass the object list class
- */
- public DynamicTypeAdapterFactory(Class objectClass,
- Class objectListClass) {
- this.objectClass = objectClass;
- this.objectListClass = objectListClass;
- }
-
- /**
- * Creates a type adapter for the given type.
- *
- * @param the generic type
- * @param gson the gson
- * @param typeToken the type token
- * @return the type adapter or null if the type is not handles by
- * this factory
- */
- @SuppressWarnings("unchecked")
- @Override
- public TypeAdapter create(Gson gson, TypeToken typeToken) {
- if (TypeToken.get(objectClass).equals(typeToken)) {
- return (TypeAdapter) new ModelCreator(gson);
- }
- if (TypeToken.get(objectListClass).equals(typeToken)) {
- return (TypeAdapter) new ModelsCreator(gson);
- }
- return null;
- }
-
- /**
- * The Class ModelCreator.
- */
- private class ModelCreator extends TypeAdapter
- implements InstanceCreator {
- private final Gson delegate;
-
- /**
- * Instantiates a new object state creator.
- *
- * @param delegate the delegate
- */
- public ModelCreator(Gson delegate) {
- this.delegate = delegate;
- }
-
- @Override
- public O createInstance(Type type) {
- try {
- return objectClass.getConstructor(Gson.class, JsonObject.class)
- .newInstance(delegate, null);
- } catch (InstantiationException | IllegalAccessException
- | IllegalArgumentException | InvocationTargetException
- | NoSuchMethodException | SecurityException e) {
- return null;
- }
- }
-
- @Override
- public void write(JsonWriter jsonWriter, O state)
- throws IOException {
- jsonWriter.jsonValue(delegate.toJson(state.data()));
- }
-
- @Override
- public O read(JsonReader jsonReader)
- throws IOException {
- try {
- return objectClass.getConstructor(Gson.class, JsonObject.class)
- .newInstance(delegate,
- delegate.fromJson(jsonReader, JsonObject.class));
- } catch (InstantiationException | IllegalAccessException
- | IllegalArgumentException | InvocationTargetException
- | NoSuchMethodException | SecurityException e) {
- return null;
- }
- }
- }
-
- /**
- * The Class ModelsCreator.
- */
- private class ModelsCreator extends TypeAdapter
- implements InstanceCreator {
-
- private final Gson delegate;
-
- /**
- * Instantiates a new object states creator.
- *
- * @param delegate the delegate
- */
- public ModelsCreator(Gson delegate) {
- this.delegate = delegate;
- }
-
- @Override
- public L createInstance(Type type) {
- try {
- return objectListClass
- .getConstructor(Gson.class, JsonObject.class)
- .newInstance(delegate, null);
- } catch (InstantiationException | IllegalAccessException
- | IllegalArgumentException | InvocationTargetException
- | NoSuchMethodException | SecurityException e) {
- return null;
- }
- }
-
- @Override
- public void write(JsonWriter jsonWriter, L states)
- throws IOException {
- jsonWriter.jsonValue(delegate.toJson(states.data()));
- }
-
- @Override
- public L read(JsonReader jsonReader)
- throws IOException {
- try {
- return objectListClass
- .getConstructor(Gson.class, JsonObject.class)
- .newInstance(delegate,
- delegate.fromJson(jsonReader, JsonObject.class));
- } catch (InstantiationException | IllegalAccessException
- | IllegalArgumentException | InvocationTargetException
- | NoSuchMethodException | SecurityException e) {
- return null;
- }
- }
- }
-
-}
diff --git a/org.jdrupes.vmoperator.common/src/org/jdrupes/vmoperator/common/K8s.java b/org.jdrupes.vmoperator.common/src/org/jdrupes/vmoperator/common/K8s.java
index 3870337..5a87ecd 100644
--- a/org.jdrupes.vmoperator.common/src/org/jdrupes/vmoperator/common/K8s.java
+++ b/org.jdrupes.vmoperator.common/src/org/jdrupes/vmoperator/common/K8s.java
@@ -1,6 +1,6 @@
/*
* VM-Operator
- * Copyright (C) 2023,2024 Michael N. Lipp
+ * 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
@@ -18,141 +18,168 @@
package org.jdrupes.vmoperator.common;
-import com.google.gson.JsonObject;
-import io.kubernetes.client.Discovery;
-import io.kubernetes.client.Discovery.APIResource;
import io.kubernetes.client.common.KubernetesListObject;
import io.kubernetes.client.common.KubernetesObject;
-import io.kubernetes.client.common.KubernetesType;
import io.kubernetes.client.custom.V1Patch;
import io.kubernetes.client.openapi.ApiClient;
import io.kubernetes.client.openapi.ApiException;
-import io.kubernetes.client.openapi.apis.EventsV1Api;
-import io.kubernetes.client.openapi.models.EventsV1Event;
+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.V1ObjectReference;
-import io.kubernetes.client.util.Strings;
+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.KubernetesApiResponse;
+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.io.Reader;
-import java.net.HttpURLConnection;
-import java.time.OffsetDateTime;
-import java.util.Map;
import java.util.Optional;
-import org.yaml.snakeyaml.LoaderOptions;
-import org.yaml.snakeyaml.Yaml;
-import org.yaml.snakeyaml.constructor.SafeConstructor;
/**
* Helpers for K8s API.
*/
-@SuppressWarnings({ "PMD.ShortClassName", "PMD.UseUtilityClass" })
+@SuppressWarnings({ "PMD.ShortClassName", "PMD.UseUtilityClass",
+ "PMD.DataflowAnomalyAnalysis" })
public class K8s {
/**
- * Returns the result from an API call as {@link Optional} if the
- * call was successful. Returns an empty `Optional` if the status
- * code is 404 (not found). Else throws an exception.
+ * Given a groupVersion, returns only the version.
*
- * @param the generic type
- * @param response the response
- * @return the optional
- * @throws ApiException the API exception
+ * @param groupVersion the group version
+ * @return the string
*/
- public static Optional
- optional(KubernetesApiResponse response) throws ApiException {
- if (response.isSuccess()) {
- return Optional.of(response.getObject());
+ public static String version(String groupVersion) {
+ return groupVersion.substring(groupVersion.lastIndexOf('/') + 1);
+ }
+
+ /**
+ * Get PVC API.
+ *
+ * @param client the client
+ * @return the generic kubernetes api
+ */
+ public static GenericKubernetesApi pvcApi(ApiClient client) {
+ return new GenericKubernetesApi<>(V1PersistentVolumeClaim.class,
+ V1PersistentVolumeClaimList.class, "", "v1",
+ "persistentvolumeclaims", client);
+ }
+
+ /**
+ * Get config map API.
+ *
+ * @param client the client
+ * @return the generic kubernetes api
+ */
+ public static GenericKubernetesApi cmApi(ApiClient client) {
+ return new GenericKubernetesApi<>(V1ConfigMap.class,
+ V1ConfigMapList.class, "", "v1", "configmaps", client);
+ }
+
+ /**
+ * Get pod API.
+ *
+ * @param client the client
+ * @return the generic kubernetes api
+ */
+ public static GenericKubernetesApi
+ podApi(ApiClient client) {
+ return new GenericKubernetesApi<>(V1Pod.class, V1PodList.class, "",
+ "v1", "pods", client);
+ }
+
+ /**
+ * Get the API for a custom resource.
+ *
+ * @param client the client
+ * @param group the group
+ * @param kind the kind
+ * @param namespace the namespace
+ * @param name the name
+ * @return the dynamic kubernetes api
+ * @throws ApiException the api exception
+ */
+ @SuppressWarnings("PMD.UseObjectForClearerAPI")
+ public static Optional 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);
+ }
}
- if (response.getHttpStatusCode() == HttpURLConnection.HTTP_NOT_FOUND) {
- return Optional.empty();
- }
- response.throwsApiException();
- // Never reached
return Optional.empty();
}
/**
- * Returns a new context with the given version as preferred version.
+ * Get an object from its metadata.
*
- * @param context the context
- * @param version the version
- * @return the API resource
+ * @param the generic type
+ * @param the generic type
+ * @param api the api
+ * @param meta the meta
+ * @return the object
*/
- public static APIResource preferred(APIResource context, String version) {
- assert context.getVersions().contains(version);
- return new APIResource(context.getGroup(),
- context.getVersions(), version, context.getKind(),
- context.getNamespaced(), context.getResourcePlural(),
- context.getResourceSingular());
- }
-
- /**
- * Return a string representation of the context (API resource).
- *
- * @param context the context
- * @return the string
- */
- @SuppressWarnings("PMD.UseLocaleWithCaseConversions")
- public static String toString(APIResource context) {
- return (Strings.isNullOrEmpty(context.getGroup()) ? ""
- : context.getGroup() + "/")
- + context.getPreferredVersion().toUpperCase()
- + context.getKind();
- }
-
- /**
- * Convert Yaml to Json.
- *
- * @param client the client
- * @param yaml the yaml
- * @return the json element
- */
- public static JsonObject yamlToJson(ApiClient client, Reader yaml) {
- // Avoid Yaml.load due to
- // https://github.com/kubernetes-client/java/issues/2741
- Map yamlData
- = new Yaml(new SafeConstructor(new LoaderOptions())).load(yaml);
-
- // There's no short-cut from Java (collections) to Gson
- var gson = client.getJSON().getGson();
- var jsonText = gson.toJson(yamlData);
- return gson.fromJson(jsonText, JsonObject.class);
- }
-
- /**
- * Lookup the specified API resource. If the version is `null` or
- * empty, the preferred version in the result is the default
- * returned from the server.
- *
- * @param client the client
- * @param group the group
- * @param version the version
- * @param kind the kind
- * @return the optional
- * @throws ApiException the api exception
- */
- public static Optional context(ApiClient client,
- String group, String version, String kind) throws ApiException {
- var apiMatch = new Discovery(client).findAll().stream()
- .filter(r -> r.getGroup().equals(group) && r.getKind().equals(kind)
- && (Strings.isNullOrEmpty(version)
- || r.getVersions().contains(version)))
- .findFirst();
- if (apiMatch.isEmpty()) {
- return Optional.empty();
+ public static
+ Optional
+ get(GenericKubernetesApi api, V1ObjectMeta meta) {
+ var response = api.get(meta.getNamespace(), meta.getName());
+ if (response.isSuccess()) {
+ return Optional.of(response.getObject());
}
- var apiRes = apiMatch.get();
- if (!Strings.isNullOrEmpty(version)) {
- if (!apiRes.getVersions().contains(version)) {
- return Optional.empty();
- }
- apiRes = new APIResource(apiRes.getGroup(), apiRes.getVersions(),
- version, apiRes.getKind(), apiRes.getNamespaced(),
- apiRes.getResourcePlural(), apiRes.getResourceSingular());
- }
- return Optional.of(apiRes);
+ return Optional.empty();
+ }
+
+ /**
+ * Delete an object.
+ *
+ * @param the generic type
+ * @param the generic type
+ * @param api the api
+ * @param object the object
+ */
+ public static
+ void delete(GenericKubernetesApi api, T object)
+ throws ApiException {
+ api.delete(object.getMetadata().getNamespace(),
+ object.getMetadata().getName()).throwsApiException();
+ }
+
+ /**
+ * Delete an object.
+ *
+ * @param the generic type
+ * @param the generic type
+ * @param api the api
+ * @param object the object
+ */
+ public static
+ void delete(GenericKubernetesApi api, T object,
+ DeleteOptions options) throws ApiException {
+ api.delete(object.getMetadata().getNamespace(),
+ object.getMetadata().getName(), options).throwsApiException();
}
/**
@@ -163,10 +190,8 @@ public class K8s {
* @param api the api
* @param existing the existing
* @param update the update
- * @return the t
* @throws ApiException the api exception
*/
- @SuppressWarnings("PMD.GenericsNaming")
public static
T apply(GenericKubernetesApi api, T existing, String update)
throws ApiException {
@@ -179,71 +204,4 @@ public class K8s {
return response.getObject();
}
- /**
- * Create an object reference.
- *
- * @param object the object
- * @return the v 1 object reference
- */
- public static V1ObjectReference
- objectReference(KubernetesObject object) {
- return new V1ObjectReference().apiVersion(object.getApiVersion())
- .kind(object.getKind())
- .namespace(object.getMetadata().getNamespace())
- .name(object.getMetadata().getName())
- .resourceVersion(object.getMetadata().getResourceVersion())
- .uid(object.getMetadata().getUid());
- }
-
- /**
- * Creates an event related to the object, adding reasonable defaults.
- *
- * * If `kind` is not set, it is set to "Event".
- * * If `metadata.namespace` is not set, it is set
- * to the object's namespace.
- * * If neither `metadata.name` nor `matadata.generateName` are set,
- * set `generateName` to the object's name with a dash appended.
- * * If `reportingInstance` is not set, set it to the object's name.
- * * If `eventTime` is not set, set it to now.
- * * If `type` is not set, set it to "Normal"
- * * If `regarding` is not set, set it to the given object.
- *
- * @param client the client
- * @param object the object
- * @param event the event
- * @throws ApiException the api exception
- */
- @SuppressWarnings("PMD.NPathComplexity")
- public static void createEvent(ApiClient client,
- KubernetesObject object, EventsV1Event event)
- throws ApiException {
- if (Strings.isNullOrEmpty(event.getKind())) {
- event.kind("Event");
- }
- if (event.getMetadata() == null) {
- event.metadata(new V1ObjectMeta());
- }
- if (Strings.isNullOrEmpty(event.getMetadata().getNamespace())) {
- event.getMetadata().namespace(object.getMetadata().getNamespace());
- }
- if (Strings.isNullOrEmpty(event.getMetadata().getName())
- && Strings.isNullOrEmpty(event.getMetadata().getGenerateName())) {
- event.getMetadata()
- .generateName(object.getMetadata().getName() + "-");
- }
- if (Strings.isNullOrEmpty(event.getReportingInstance())) {
- event.reportingInstance(object.getMetadata().getName());
- }
- if (event.getEventTime() == null) {
- event.eventTime(OffsetDateTime.now());
- }
- if (Strings.isNullOrEmpty(event.getType())) {
- event.type("Normal");
- }
- if (event.getRegarding() == null) {
- event.regarding(objectReference(object));
- }
- new EventsV1Api(client).createNamespacedEvent(
- object.getMetadata().getNamespace(), event, null, null, null, null);
- }
}
diff --git a/org.jdrupes.vmoperator.common/src/org/jdrupes/vmoperator/common/K8sClient.java b/org.jdrupes.vmoperator.common/src/org/jdrupes/vmoperator/common/K8sClient.java
deleted file mode 100644
index 272da2b..0000000
--- a/org.jdrupes.vmoperator.common/src/org/jdrupes/vmoperator/common/K8sClient.java
+++ /dev/null
@@ -1,954 +0,0 @@
-/*
- * VM-Operator
- * Copyright (C) 2024 Michael N. Lipp
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see .
- */
-
-package org.jdrupes.vmoperator.common;
-
-import io.kubernetes.client.openapi.ApiCallback;
-import io.kubernetes.client.openapi.ApiClient;
-import io.kubernetes.client.openapi.ApiException;
-import io.kubernetes.client.openapi.ApiResponse;
-import io.kubernetes.client.openapi.JSON;
-import io.kubernetes.client.openapi.Pair;
-import io.kubernetes.client.openapi.auth.Authentication;
-import io.kubernetes.client.util.ClientBuilder;
-import io.kubernetes.client.util.generic.options.PatchOptions;
-import java.io.File;
-import java.io.IOException;
-import java.io.InputStream;
-import java.lang.reflect.Type;
-import java.text.DateFormat;
-import java.time.format.DateTimeFormatter;
-import java.util.Collection;
-import java.util.List;
-import java.util.Map;
-import javax.net.ssl.KeyManager;
-import okhttp3.Call;
-import okhttp3.OkHttpClient;
-import okhttp3.Request;
-import okhttp3.Request.Builder;
-import okhttp3.RequestBody;
-import okhttp3.Response;
-
-/**
- * A client with some additional properties.
- */
-@SuppressWarnings({ "PMD.ExcessivePublicCount", "PMD.TooManyMethods",
- "checkstyle:LineLength", "PMD.CouplingBetweenObjects", "PMD.GodClass" })
-public class K8sClient extends ApiClient {
-
- private ApiClient apiClient;
- private PatchOptions defaultPatchOptions;
-
- /**
- * Instantiates a new client.
- *
- * @throws IOException Signals that an I/O exception has occurred.
- */
- public K8sClient() throws IOException {
- defaultPatchOptions = new PatchOptions();
- defaultPatchOptions.setFieldManager("kubernetes-java-kubectl-apply");
- }
-
- private ApiClient apiClient() {
- if (apiClient == null) {
- try {
- apiClient = ClientBuilder.standard().build();
- } catch (IOException e) {
- throw new IllegalStateException(e);
- }
- }
- return apiClient;
- }
-
- /**
- * Gets the default patch options.
- *
- * @return the defaultPatchOptions
- */
- public PatchOptions defaultPatchOptions() {
- return defaultPatchOptions;
- }
-
- /**
- * Changes the default patch options.
- *
- * @param patchOptions the patch options
- * @return the client
- */
- public K8sClient with(PatchOptions patchOptions) {
- defaultPatchOptions = patchOptions;
- return this;
- }
-
- /**
- * Gets the base path.
- *
- * @return the base path
- * @see ApiClient#getBasePath()
- */
- @Override
- public String getBasePath() {
- return apiClient().getBasePath();
- }
-
- /**
- * Sets the base path.
- *
- * @param basePath the base path
- * @return the api client
- * @see ApiClient#setBasePath(java.lang.String)
- */
- @Override
- public ApiClient setBasePath(String basePath) {
- return apiClient().setBasePath(basePath);
- }
-
- /**
- * Gets the http client.
- *
- * @return the http client
- * @see ApiClient#getHttpClient()
- */
- @Override
- public OkHttpClient getHttpClient() {
- return apiClient().getHttpClient();
- }
-
- /**
- * Sets the http client.
- *
- * @param newHttpClient the new http client
- * @return the api client
- * @see ApiClient#setHttpClient(okhttp3.OkHttpClient)
- */
- @Override
- public ApiClient setHttpClient(OkHttpClient newHttpClient) {
- return apiClient().setHttpClient(newHttpClient);
- }
-
- /**
- * Gets the json.
- *
- * @return the json
- * @see ApiClient#getJSON()
- */
- @SuppressWarnings("abbreviationAsWordInName")
- @Override
- public JSON getJSON() {
- return apiClient().getJSON();
- }
-
- /**
- * Sets the JSON.
- *
- * @param json the json
- * @return the api client
- * @see ApiClient#setJSON(io.kubernetes.client.openapi.JSON)
- */
- @SuppressWarnings("abbreviationAsWordInName")
- @Override
- public ApiClient setJSON(JSON json) {
- return apiClient().setJSON(json);
- }
-
- /**
- * Checks if is verifying ssl.
- *
- * @return true, if is verifying ssl
- * @see ApiClient#isVerifyingSsl()
- */
- @Override
- public boolean isVerifyingSsl() {
- return apiClient().isVerifyingSsl();
- }
-
- /**
- * Sets the verifying ssl.
- *
- * @param verifyingSsl the verifying ssl
- * @return the api client
- * @see ApiClient#setVerifyingSsl(boolean)
- */
- @Override
- public ApiClient setVerifyingSsl(boolean verifyingSsl) {
- return apiClient().setVerifyingSsl(verifyingSsl);
- }
-
- /**
- * Gets the ssl ca cert.
- *
- * @return the ssl ca cert
- * @see ApiClient#getSslCaCert()
- */
- @Override
- public InputStream getSslCaCert() {
- return apiClient().getSslCaCert();
- }
-
- /**
- * Sets the ssl ca cert.
- *
- * @param sslCaCert the ssl ca cert
- * @return the api client
- * @see ApiClient#setSslCaCert(java.io.InputStream)
- */
- @Override
- public ApiClient setSslCaCert(InputStream sslCaCert) {
- return apiClient().setSslCaCert(sslCaCert);
- }
-
- /**
- * Gets the key managers.
- *
- * @return the key managers
- * @see ApiClient#getKeyManagers()
- */
- @Override
- public KeyManager[] getKeyManagers() {
- return apiClient().getKeyManagers();
- }
-
- /**
- * Sets the key managers.
- *
- * @param managers the managers
- * @return the api client
- * @see ApiClient#setKeyManagers(javax.net.ssl.KeyManager[])
- */
- @Override
- public ApiClient setKeyManagers(KeyManager[] managers) {
- return apiClient().setKeyManagers(managers);
- }
-
- /**
- * Gets the date format.
- *
- * @return the date format
- * @see ApiClient#getDateFormat()
- */
- @Override
- public DateFormat getDateFormat() {
- return apiClient().getDateFormat();
- }
-
- /**
- * Sets the date format.
- *
- * @param dateFormat the date format
- * @return the api client
- * @see ApiClient#setDateFormat(java.text.DateFormat)
- */
- @Override
- public ApiClient setDateFormat(DateFormat dateFormat) {
- return apiClient().setDateFormat(dateFormat);
- }
-
- /**
- * Sets the sql date format.
- *
- * @param dateFormat the date format
- * @return the api client
- * @see ApiClient#setSqlDateFormat(java.text.DateFormat)
- */
- @Override
- public ApiClient setSqlDateFormat(DateFormat dateFormat) {
- return apiClient().setSqlDateFormat(dateFormat);
- }
-
- /**
- * Sets the offset date time format.
- *
- * @param dateFormat the date format
- * @return the api client
- * @see ApiClient#setOffsetDateTimeFormat(java.time.format.DateTimeFormatter)
- */
- @Override
- public ApiClient setOffsetDateTimeFormat(DateTimeFormatter dateFormat) {
- return apiClient().setOffsetDateTimeFormat(dateFormat);
- }
-
- /**
- * Sets the local date format.
- *
- * @param dateFormat the date format
- * @return the api client
- * @see ApiClient#setLocalDateFormat(java.time.format.DateTimeFormatter)
- */
- @Override
- public ApiClient setLocalDateFormat(DateTimeFormatter dateFormat) {
- return apiClient().setLocalDateFormat(dateFormat);
- }
-
- /**
- * Sets the lenient on json.
- *
- * @param lenientOnJson the lenient on json
- * @return the api client
- * @see ApiClient#setLenientOnJson(boolean)
- */
- @Override
- public ApiClient setLenientOnJson(boolean lenientOnJson) {
- return apiClient().setLenientOnJson(lenientOnJson);
- }
-
- /**
- * Gets the authentications.
- *
- * @return the authentications
- * @see ApiClient#getAuthentications()
- */
- @Override
- public Map getAuthentications() {
- return apiClient().getAuthentications();
- }
-
- /**
- * Gets the authentication.
- *
- * @param authName the auth name
- * @return the authentication
- * @see ApiClient#getAuthentication(java.lang.String)
- */
- @Override
- public Authentication getAuthentication(String authName) {
- return apiClient().getAuthentication(authName);
- }
-
- /**
- * Sets the username.
- *
- * @param username the new username
- * @see ApiClient#setUsername(java.lang.String)
- */
- @Override
- public void setUsername(String username) {
- apiClient().setUsername(username);
- }
-
- /**
- * Sets the password.
- *
- * @param password the new password
- * @see ApiClient#setPassword(java.lang.String)
- */
- @Override
- public void setPassword(String password) {
- apiClient().setPassword(password);
- }
-
- /**
- * Sets the api key.
- *
- * @param apiKey the new api key
- * @see ApiClient#setApiKey(java.lang.String)
- */
- @Override
- public void setApiKey(String apiKey) {
- apiClient().setApiKey(apiKey);
- }
-
- /**
- * Sets the api key prefix.
- *
- * @param apiKeyPrefix the new api key prefix
- * @see ApiClient#setApiKeyPrefix(java.lang.String)
- */
- @Override
- public void setApiKeyPrefix(String apiKeyPrefix) {
- apiClient().setApiKeyPrefix(apiKeyPrefix);
- }
-
- /**
- * Sets the access token.
- *
- * @param accessToken the new access token
- * @see ApiClient#setAccessToken(java.lang.String)
- */
- @Override
- public void setAccessToken(String accessToken) {
- apiClient().setAccessToken(accessToken);
- }
-
- /**
- * Sets the user agent.
- *
- * @param userAgent the user agent
- * @return the api client
- * @see ApiClient#setUserAgent(java.lang.String)
- */
- @Override
- public ApiClient setUserAgent(String userAgent) {
- return apiClient().setUserAgent(userAgent);
- }
-
- /**
- * To string.
- *
- * @return the string
- * @see java.lang.Object#toString()
- */
- @Override
- public String toString() {
- return apiClient().toString();
- }
-
- /**
- * Adds the default header.
- *
- * @param key the key
- * @param value the value
- * @return the api client
- * @see ApiClient#addDefaultHeader(java.lang.String, java.lang.String)
- */
- @Override
- public ApiClient addDefaultHeader(String key, String value) {
- return apiClient().addDefaultHeader(key, value);
- }
-
- /**
- * Adds the default cookie.
- *
- * @param key the key
- * @param value the value
- * @return the api client
- * @see ApiClient#addDefaultCookie(java.lang.String, java.lang.String)
- */
- @Override
- public ApiClient addDefaultCookie(String key, String value) {
- return apiClient().addDefaultCookie(key, value);
- }
-
- /**
- * Checks if is debugging.
- *
- * @return true, if is debugging
- * @see ApiClient#isDebugging()
- */
- @Override
- public boolean isDebugging() {
- return apiClient().isDebugging();
- }
-
- /**
- * Sets the debugging.
- *
- * @param debugging the debugging
- * @return the api client
- * @see ApiClient#setDebugging(boolean)
- */
- @Override
- public ApiClient setDebugging(boolean debugging) {
- return apiClient().setDebugging(debugging);
- }
-
- /**
- * Gets the temp folder path.
- *
- * @return the temp folder path
- * @see ApiClient#getTempFolderPath()
- */
- @Override
- public String getTempFolderPath() {
- return apiClient().getTempFolderPath();
- }
-
- /**
- * Sets the temp folder path.
- *
- * @param tempFolderPath the temp folder path
- * @return the api client
- * @see ApiClient#setTempFolderPath(java.lang.String)
- */
- @Override
- public ApiClient setTempFolderPath(String tempFolderPath) {
- return apiClient().setTempFolderPath(tempFolderPath);
- }
-
- /**
- * Gets the connect timeout.
- *
- * @return the connect timeout
- * @see ApiClient#getConnectTimeout()
- */
- @Override
- public int getConnectTimeout() {
- return apiClient().getConnectTimeout();
- }
-
- /**
- * Sets the connect timeout.
- *
- * @param connectionTimeout the connection timeout
- * @return the api client
- * @see ApiClient#setConnectTimeout(int)
- */
- @Override
- public ApiClient setConnectTimeout(int connectionTimeout) {
- return apiClient().setConnectTimeout(connectionTimeout);
- }
-
- /**
- * Gets the read timeout.
- *
- * @return the read timeout
- * @see ApiClient#getReadTimeout()
- */
- @Override
- public int getReadTimeout() {
- return apiClient().getReadTimeout();
- }
-
- /**
- * Sets the read timeout.
- *
- * @param readTimeout the read timeout
- * @return the api client
- * @see ApiClient#setReadTimeout(int)
- */
- @Override
- public ApiClient setReadTimeout(int readTimeout) {
- return apiClient().setReadTimeout(readTimeout);
- }
-
- /**
- * Gets the write timeout.
- *
- * @return the write timeout
- * @see ApiClient#getWriteTimeout()
- */
- @Override
- public int getWriteTimeout() {
- return apiClient().getWriteTimeout();
- }
-
- /**
- * Sets the write timeout.
- *
- * @param writeTimeout the write timeout
- * @return the api client
- * @see ApiClient#setWriteTimeout(int)
- */
- @Override
- public ApiClient setWriteTimeout(int writeTimeout) {
- return apiClient().setWriteTimeout(writeTimeout);
- }
-
- /**
- * Parameter to string.
- *
- * @param param the param
- * @return the string
- * @see ApiClient#parameterToString(java.lang.Object)
- */
- @Override
- public String parameterToString(Object param) {
- return apiClient().parameterToString(param);
- }
-
- /**
- * Parameter to pair.
- *
- * @param name the name
- * @param value the value
- * @return the list
- * @see ApiClient#parameterToPair(java.lang.String, java.lang.Object)
- */
- @Override
- public List parameterToPair(String name, Object value) {
- return apiClient().parameterToPair(name, value);
- }
-
- /**
- * Parameter to pairs.
- *
- * @param collectionFormat the collection format
- * @param name the name
- * @param value the value
- * @return the list
- * @see ApiClient#parameterToPairs(java.lang.String, java.lang.String, java.util.Collection)
- */
- @SuppressWarnings({ "rawtypes", "PMD.AvoidDuplicateLiterals" })
- @Override
- public List parameterToPairs(String collectionFormat, String name,
- Collection value) {
- return apiClient().parameterToPairs(collectionFormat, name, value);
- }
-
- /**
- * Collection path parameter to string.
- *
- * @param collectionFormat the collection format
- * @param value the value
- * @return the string
- * @see ApiClient#collectionPathParameterToString(java.lang.String, java.util.Collection)
- */
- @SuppressWarnings("rawtypes")
- @Override
- public String collectionPathParameterToString(String collectionFormat,
- Collection value) {
- return apiClient().collectionPathParameterToString(collectionFormat,
- value);
- }
-
- /**
- * Sanitize filename.
- *
- * @param filename the filename
- * @return the string
- * @see ApiClient#sanitizeFilename(java.lang.String)
- */
- @Override
- public String sanitizeFilename(String filename) {
- return apiClient().sanitizeFilename(filename);
- }
-
- /**
- * Checks if is json mime.
- *
- * @param mime the mime
- * @return true, if is json mime
- * @see ApiClient#isJsonMime(java.lang.String)
- */
- @Override
- public boolean isJsonMime(String mime) {
- return apiClient().isJsonMime(mime);
- }
-
- /**
- * Select header accept.
- *
- * @param accepts the accepts
- * @return the string
- * @see ApiClient#selectHeaderAccept(java.lang.String[])
- */
- @Override
- public String selectHeaderAccept(String[] accepts) {
- return apiClient().selectHeaderAccept(accepts);
- }
-
- /**
- * Select header content type.
- *
- * @param contentTypes the content types
- * @return the string
- * @see ApiClient#selectHeaderContentType(java.lang.String[])
- */
- @Override
- public String selectHeaderContentType(String[] contentTypes) {
- return apiClient().selectHeaderContentType(contentTypes);
- }
-
- /**
- * Escape string.
- *
- * @param str the str
- * @return the string
- * @see ApiClient#escapeString(java.lang.String)
- */
- @Override
- public String escapeString(String str) {
- return apiClient().escapeString(str);
- }
-
- /**
- * Deserialize.
- *
- * @param the generic type
- * @param response the response
- * @param returnType the return type
- * @return the t
- * @throws ApiException the api exception
- * @see ApiClient#deserialize(okhttp3.Response, java.lang.reflect.Type)
- */
- @Override
- public T deserialize(Response response, Type returnType)
- throws ApiException {
- return apiClient().deserialize(response, returnType);
- }
-
- /**
- * Serialize.
- *
- * @param obj the obj
- * @param contentType the content type
- * @return the request body
- * @throws ApiException the api exception
- * @see ApiClient#serialize(java.lang.Object, java.lang.String)
- */
- @Override
- public RequestBody serialize(Object obj, String contentType)
- throws ApiException {
- return apiClient().serialize(obj, contentType);
- }
-
- /**
- * Download file from response.
- *
- * @param response the response
- * @return the file
- * @throws ApiException the api exception
- * @see ApiClient#downloadFileFromResponse(okhttp3.Response)
- */
- @Override
- public File downloadFileFromResponse(Response response)
- throws ApiException {
- return apiClient().downloadFileFromResponse(response);
- }
-
- /**
- * Prepare download file.
- *
- * @param response the response
- * @return the file
- * @throws IOException Signals that an I/O exception has occurred.
- * @see ApiClient#prepareDownloadFile(okhttp3.Response)
- */
- @Override
- public File prepareDownloadFile(Response response) throws IOException {
- return apiClient().prepareDownloadFile(response);
- }
-
- /**
- * Execute.
- *
- * @param the generic type
- * @param call the call
- * @return the api response
- * @throws ApiException the api exception
- * @see ApiClient#execute(okhttp3.Call)
- */
- @Override
- public ApiResponse execute(Call call) throws ApiException {
- return apiClient().execute(call);
- }
-
- /**
- * Execute.
- *
- * @param the generic type
- * @param call the call
- * @param returnType the return type
- * @return the api response
- * @throws ApiException the api exception
- * @see ApiClient#execute(okhttp3.Call, java.lang.reflect.Type)
- */
- @Override
- public ApiResponse execute(Call call, Type returnType)
- throws ApiException {
- return apiClient().execute(call, returnType);
- }
-
- /**
- * Execute async.
- *
- * @param the generic type
- * @param call the call
- * @param callback the callback
- * @see ApiClient#executeAsync(okhttp3.Call, io.kubernetes.client.openapi.ApiCallback)
- */
- @Override
- public void executeAsync(Call call, ApiCallback callback) {
- apiClient().executeAsync(call, callback);
- }
-
- /**
- * Execute async.
- *
- * @param the generic type
- * @param call the call
- * @param returnType the return type
- * @param callback the callback
- * @see ApiClient#executeAsync(okhttp3.Call, java.lang.reflect.Type, io.kubernetes.client.openapi.ApiCallback)
- */
- @Override
- public void executeAsync(Call call, Type returnType,
- ApiCallback callback) {
- apiClient().executeAsync(call, returnType, callback);
- }
-
- /**
- * Handle response.
- *
- * @param the generic type
- * @param response the response
- * @param returnType the return type
- * @return the t
- * @throws ApiException the api exception
- * @see ApiClient#handleResponse(okhttp3.Response, java.lang.reflect.Type)
- */
- @Override
- public T handleResponse(Response response, Type returnType)
- throws ApiException {
- return apiClient().handleResponse(response, returnType);
- }
-
- /**
- * Builds the call.
- *
- * @param path the path
- * @param method the method
- * @param queryParams the query params
- * @param collectionQueryParams the collection query params
- * @param body the body
- * @param headerParams the header params
- * @param cookieParams the cookie params
- * @param formParams the form params
- * @param authNames the auth names
- * @param callback the callback
- * @return the call
- * @throws ApiException the api exception
- * @see ApiClient#buildCall(java.lang.String, java.lang.String, java.util.List, java.util.List, java.lang.Object, java.util.Map, java.util.Map, java.util.Map, java.lang.String[], io.kubernetes.client.openapi.ApiCallback)
- */
- @SuppressWarnings({ "rawtypes" })
- @Override
- public Call buildCall(String path, String method, List queryParams,
- List collectionQueryParams, Object body,
- Map headerParams, Map cookieParams,
- Map formParams, String[] authNames,
- ApiCallback callback) throws ApiException {
- return apiClient().buildCall(path, method, queryParams,
- collectionQueryParams, body, headerParams, cookieParams, formParams,
- authNames, callback);
- }
-
- /**
- * Builds the request.
- *
- * @param path the path
- * @param method the method
- * @param queryParams the query params
- * @param collectionQueryParams the collection query params
- * @param body the body
- * @param headerParams the header params
- * @param cookieParams the cookie params
- * @param formParams the form params
- * @param authNames the auth names
- * @param callback the callback
- * @return the request
- * @throws ApiException the api exception
- * @see ApiClient#buildRequest(java.lang.String, java.lang.String, java.util.List, java.util.List, java.lang.Object, java.util.Map, java.util.Map, java.util.Map, java.lang.String[], io.kubernetes.client.openapi.ApiCallback)
- */
- @SuppressWarnings({ "rawtypes" })
- @Override
- public Request buildRequest(String path, String method,
- List queryParams, List collectionQueryParams,
- Object body, Map headerParams,
- Map cookieParams, Map formParams,
- String[] authNames, ApiCallback callback) throws ApiException {
- return apiClient().buildRequest(path, method, queryParams,
- collectionQueryParams, body, headerParams, cookieParams, formParams,
- authNames, callback);
- }
-
- /**
- * Builds the url.
- *
- * @param path the path
- * @param queryParams the query params
- * @param collectionQueryParams the collection query params
- * @return the string
- * @see ApiClient#buildUrl(java.lang.String, java.util.List, java.util.List)
- */
- @Override
- public String buildUrl(String path, List queryParams,
- List collectionQueryParams) {
- return apiClient().buildUrl(path, queryParams, collectionQueryParams);
- }
-
- /**
- * Process header params.
- *
- * @param headerParams the header params
- * @param reqBuilder the req builder
- * @see ApiClient#processHeaderParams(java.util.Map, okhttp3.Request.Builder)
- */
- @Override
- public void processHeaderParams(Map headerParams,
- Builder reqBuilder) {
- apiClient().processHeaderParams(headerParams, reqBuilder);
- }
-
- /**
- * Process cookie params.
- *
- * @param cookieParams the cookie params
- * @param reqBuilder the req builder
- * @see ApiClient#processCookieParams(java.util.Map, okhttp3.Request.Builder)
- */
- @Override
- public void processCookieParams(Map cookieParams,
- Builder reqBuilder) {
- apiClient().processCookieParams(cookieParams, reqBuilder);
- }
-
- /**
- * Update params for auth.
- *
- * @param authNames the auth names
- * @param queryParams the query params
- * @param headerParams the header params
- * @param cookieParams the cookie params
- * @see ApiClient#updateParamsForAuth(java.lang.String[], java.util.List, java.util.Map, java.util.Map)
- */
- @Override
- public void updateParamsForAuth(String[] authNames, List queryParams,
- Map headerParams,
- Map cookieParams) {
- apiClient().updateParamsForAuth(authNames, queryParams, headerParams,
- cookieParams);
- }
-
- /**
- * Builds the request body form encoding.
- *
- * @param formParams the form params
- * @return the request body
- * @see ApiClient#buildRequestBodyFormEncoding(java.util.Map)
- */
- @Override
- public RequestBody
- buildRequestBodyFormEncoding(Map formParams) {
- return apiClient().buildRequestBodyFormEncoding(formParams);
- }
-
- /**
- * Builds the request body multipart.
- *
- * @param formParams the form params
- * @return the request body
- * @see ApiClient#buildRequestBodyMultipart(java.util.Map)
- */
- @Override
- public RequestBody
- buildRequestBodyMultipart(Map formParams) {
- return apiClient().buildRequestBodyMultipart(formParams);
- }
-
- /**
- * Guess content type from file.
- *
- * @param file the file
- * @return the string
- * @see ApiClient#guessContentTypeFromFile(java.io.File)
- */
- @Override
- public String guessContentTypeFromFile(File file) {
- return apiClient().guessContentTypeFromFile(file);
- }
-
-}
\ No newline at end of file
diff --git a/org.jdrupes.vmoperator.common/src/org/jdrupes/vmoperator/common/K8sClusterGenericStub.java b/org.jdrupes.vmoperator.common/src/org/jdrupes/vmoperator/common/K8sClusterGenericStub.java
deleted file mode 100644
index 59b4d12..0000000
--- a/org.jdrupes.vmoperator.common/src/org/jdrupes/vmoperator/common/K8sClusterGenericStub.java
+++ /dev/null
@@ -1,396 +0,0 @@
-/*
- * VM-Operator
- * Copyright (C) 2024 Michael N. Lipp
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see .
- */
-
-package org.jdrupes.vmoperator.common;
-
-import io.kubernetes.client.Discovery.APIResource;
-import io.kubernetes.client.apimachinery.GroupVersionKind;
-import io.kubernetes.client.common.KubernetesListObject;
-import io.kubernetes.client.common.KubernetesObject;
-import io.kubernetes.client.custom.V1Patch;
-import io.kubernetes.client.openapi.ApiException;
-import io.kubernetes.client.util.Strings;
-import io.kubernetes.client.util.generic.GenericKubernetesApi;
-import io.kubernetes.client.util.generic.options.GetOptions;
-import io.kubernetes.client.util.generic.options.ListOptions;
-import io.kubernetes.client.util.generic.options.PatchOptions;
-import java.net.HttpURLConnection;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Optional;
-import java.util.function.Function;
-
-/**
- * A stub for cluster scoped objects. This stub provides the
- * functions common to all Kubernetes objects, but uses variables
- * for all types. This class should be used as base class only.
- *
- * @param the generic type
- * @param the generic type
- */
-@SuppressWarnings({ "PMD.CouplingBetweenObjects" })
-public class K8sClusterGenericStub {
- protected final K8sClient client;
- private final GenericKubernetesApi api;
- protected final APIResource context;
- protected final String name;
-
- /**
- * Instantiates a new stub for the object specified. If the object
- * exists in the context specified, the version (see
- * {@link #version()} is bound to the existing object's version.
- * Else the stub is dangling with the version set to the context's
- * preferred version.
- *
- * @param objectClass the object class
- * @param objectListClass the object list class
- * @param client the client
- * @param context the context
- * @param name the name
- */
- @SuppressWarnings("PMD.AvoidInstantiatingObjectsInLoops")
- protected K8sClusterGenericStub(Class objectClass,
- Class objectListClass, K8sClient client, APIResource context,
- String name) {
- this.client = client;
- this.name = name;
-
- // Bind version
- var foundVersion = context.getPreferredVersion();
- GenericKubernetesApi testApi = null;
- GetOptions mdOpts
- = new GetOptions().isPartialObjectMetadataRequest(true);
- for (var version : candidateVersions(context)) {
- testApi = new GenericKubernetesApi<>(objectClass, objectListClass,
- context.getGroup(), version, context.getResourcePlural(),
- client);
- if (testApi.get(name, mdOpts).isSuccess()) {
- foundVersion = version;
- break;
- }
- }
- if (foundVersion.equals(context.getPreferredVersion())) {
- this.context = context;
- } else {
- this.context = K8s.preferred(context, foundVersion);
- }
-
- api = Optional.ofNullable(testApi)
- .orElseGet(() -> new GenericKubernetesApi<>(objectClass,
- objectListClass, group(), version(), plural(), client));
- }
-
- /**
- * Gets the context.
- *
- * @return the context
- */
- public APIResource context() {
- return context;
- }
-
- /**
- * Gets the group.
- *
- * @return the group
- */
- public String group() {
- return context.getGroup();
- }
-
- /**
- * Gets the version.
- *
- * @return the version
- */
- public String version() {
- return context.getPreferredVersion();
- }
-
- /**
- * Gets the kind.
- *
- * @return the kind
- */
- public String kind() {
- return context.getKind();
- }
-
- /**
- * Gets the plural.
- *
- * @return the plural
- */
- public String plural() {
- return context.getResourcePlural();
- }
-
- /**
- * Gets the name.
- *
- * @return the name
- */
- public String name() {
- return name;
- }
-
- /**
- * Delete the Kubernetes object.
- *
- * @throws ApiException the API exception
- */
- public void delete() throws ApiException {
- var result = api.delete(name);
- if (result.isSuccess()
- || result.getHttpStatusCode() == HttpURLConnection.HTTP_NOT_FOUND) {
- return;
- }
- result.throwsApiException();
- }
-
- /**
- * Retrieves and returns the current state of the object.
- *
- * @return the object's state
- * @throws ApiException the api exception
- */
- public Optional model() throws ApiException {
- return K8s.optional(api.get(name));
- }
-
- /**
- * Updates the object's status.
- *
- * @param object the current state of the object (passed to `status`)
- * @param status function that returns the new status
- * @return the updated model or empty if not successful
- * @throws ApiException the api exception
- */
- public Optional updateStatus(O object,
- Function status) throws ApiException {
- return K8s.optional(api.updateStatus(object, status));
- }
-
- /**
- * Updates the status.
- *
- * @param status the status
- * @return the kubernetes api response
- * the updated model or empty if not successful
- * @throws ApiException the api exception
- */
- public Optional updateStatus(Function status)
- throws ApiException {
- return updateStatus(api.get(name).throwsApiException().getObject(),
- status);
- }
-
- /**
- * Patch the object.
- *
- * @param patchType the patch type
- * @param patch the patch
- * @param options the options
- * @return the kubernetes api response
- * @throws ApiException the api exception
- */
- public Optional patch(String patchType, V1Patch patch,
- PatchOptions options) throws ApiException {
- return K8s
- .optional(api.patch(name, patchType, patch, options));
- }
-
- /**
- * Patch the object using default options.
- *
- * @param patchType the patch type
- * @param patch the patch
- * @return the kubernetes api response
- * @throws ApiException the api exception
- */
- public Optional
- patch(String patchType, V1Patch patch) throws ApiException {
- PatchOptions opts = new PatchOptions();
- return patch(patchType, patch, opts);
- }
-
- /**
- * A supplier for generic stubs.
- *
- * @param the object type
- * @param the object list type
- * @param the result type
- */
- @FunctionalInterface
- public interface GenericSupplier> {
-
- /**
- * Gets a new stub.
- *
- * @param objectClass the object class
- * @param objectListClass the object list class
- * @param client the client
- * @param context the API resource
- * @param name the name
- * @return the result
- */
- R get(Class objectClass, Class objectListClass, K8sClient client,
- APIResource context, String name);
- }
-
- @Override
- @SuppressWarnings("PMD.UseLocaleWithCaseConversions")
- public String toString() {
- return (Strings.isNullOrEmpty(group()) ? "" : group() + "/")
- + version().toUpperCase() + kind() + " " + name;
- }
-
- /**
- * Get an object stub. If the version in parameter
- * `gvk` is an empty string, the stub refers to the first object
- * found with matching group and kind.
- *
- * @param the object type
- * @param the object list type
- * @param the stub type
- * @param objectClass the object class
- * @param objectListClass the object list class
- * @param client the client
- * @param gvk the group, version and kind
- * @param name the name
- * @param provider the provider
- * @return the stub if the object exists
- * @throws ApiException the api exception
- */
- public static >
- R get(Class objectClass, Class objectListClass,
- K8sClient client, GroupVersionKind gvk, String name,
- GenericSupplier provider) throws ApiException {
- var context = K8s.context(client, gvk.getGroup(), gvk.getVersion(),
- gvk.getKind());
- if (context.isEmpty()) {
- throw new ApiException("No known API for " + gvk.getGroup()
- + "/" + gvk.getVersion() + " " + gvk.getKind());
- }
- return provider.get(objectClass, objectListClass, client, context.get(),
- name);
- }
-
- /**
- * Get an object stub.
- *
- * @param the object type
- * @param the object list type
- * @param the stub type
- * @param objectClass the object class
- * @param objectListClass the object list class
- * @param client the client
- * @param context the context
- * @param name the name
- * @param provider the provider
- * @return the stub if the object exists
- * @throws ApiException the api exception
- */
- public static >
- R get(Class objectClass, Class objectListClass,
- K8sClient client, APIResource context, String name,
- GenericSupplier provider) {
- return provider.get(objectClass, objectListClass, client, context,
- name);
- }
-
- /**
- * Get an object stub for a newly created object.
- *
- * @param the object type
- * @param the object list type
- * @param the stub type
- * @param objectClass the object class
- * @param objectListClass the object list class
- * @param client the client
- * @param context the context
- * @param model the model
- * @param provider the provider
- * @return the stub if the object exists
- * @throws ApiException the api exception
- */
- public static >
- R create(Class objectClass, Class objectListClass,
- K8sClient client, APIResource context, O model,
- GenericSupplier provider) throws ApiException {
- var api = new GenericKubernetesApi<>(objectClass, objectListClass,
- context.getGroup(), context.getPreferredVersion(),
- context.getResourcePlural(), client);
- api.create(model).throwsApiException();
- return provider.get(objectClass, objectListClass, client,
- context, model.getMetadata().getName());
- }
-
- /**
- * Get the stubs for the objects that match
- * the criteria from the given options.
- *
- * @param the object type
- * @param the object list type
- * @param the stub type
- * @param objectClass the object class
- * @param objectListClass the object list class
- * @param client the client
- * @param context the context
- * @param options the options
- * @param provider the provider
- * @return the collection
- * @throws ApiException the api exception
- */
- public static >
- Collection list(Class objectClass, Class objectListClass,
- K8sClient client, APIResource context,
- ListOptions options, GenericSupplier provider)
- throws ApiException {
- var result = new ArrayList();
- for (var version : candidateVersions(context)) {
- @SuppressWarnings("PMD.AvoidInstantiatingObjectsInLoops")
- var api = new GenericKubernetesApi<>(objectClass, objectListClass,
- context.getGroup(), version, context.getResourcePlural(),
- client);
- var objs = api.list(options).throwsApiException();
- for (var item : objs.getObject().getItems()) {
- result.add(provider.get(objectClass, objectListClass, client,
- context, item.getMetadata().getName()));
- }
- }
- return result;
- }
-
- private static List candidateVersions(APIResource context) {
- var result = new LinkedList<>(context.getVersions());
- result.remove(context.getPreferredVersion());
- result.add(0, context.getPreferredVersion());
- return result;
- }
-
-}
diff --git a/org.jdrupes.vmoperator.common/src/org/jdrupes/vmoperator/common/K8sDynamicModel.java b/org.jdrupes.vmoperator.common/src/org/jdrupes/vmoperator/common/K8sDynamicModel.java
deleted file mode 100644
index 2392d3e..0000000
--- a/org.jdrupes.vmoperator.common/src/org/jdrupes/vmoperator/common/K8sDynamicModel.java
+++ /dev/null
@@ -1,113 +0,0 @@
-/*
- * VM-Operator
- * Copyright (C) 2024 Michael N. Lipp
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see .
- */
-
-package org.jdrupes.vmoperator.common;
-
-import com.google.gson.Gson;
-import com.google.gson.JsonObject;
-import io.kubernetes.client.common.KubernetesObject;
-import io.kubernetes.client.openapi.models.V1ObjectMeta;
-
-/**
- * Represents a Kubernetes object using a JSON data structure.
- * Some information that is common to all Kubernetes objects,
- * notably the metadata, is made available through the methods
- * defined by {@link KubernetesObject}.
- */
-public class K8sDynamicModel implements KubernetesObject {
-
- private final V1ObjectMeta metadata;
- private final JsonObject data;
-
- /**
- * Instantiates a new model from the JSON representation.
- *
- * @param delegate the gson instance to use for extracting structured data
- * @param json the JSON
- */
- public K8sDynamicModel(Gson delegate, JsonObject json) {
- this.data = json;
- metadata = delegate.fromJson(data.get("metadata"), V1ObjectMeta.class);
- }
-
- @Override
- public String getApiVersion() {
- return apiVersion();
- }
-
- /**
- * Gets the API version. (Abbreviated method name for convenience.)
- *
- * @return the API version
- */
- public String apiVersion() {
- return data.get("apiVersion").getAsString();
- }
-
- @Override
- public String getKind() {
- return kind();
- }
-
- /**
- * Gets the kind. (Abbreviated method name for convenience.)
- *
- * @return the kind
- */
- public String kind() {
- return data.get("kind").getAsString();
- }
-
- @Override
- public V1ObjectMeta getMetadata() {
- return metadata;
- }
-
- /**
- * Gets the metadata. (Abbreviated method name for convenience.)
- *
- * @return the metadata
- */
- public V1ObjectMeta metadata() {
- return metadata;
- }
-
- /**
- * Gets the data.
- *
- * @return the data
- */
- public JsonObject data() {
- return data;
- }
-
- /**
- * Convenience method for getting the status.
- *
- * @return the JSON object describing the status
- */
- public JsonObject statusJson() {
- return data.getAsJsonObject("status");
- }
-
- @Override
- public String toString() {
- return data.toString();
- }
-
-}
diff --git a/org.jdrupes.vmoperator.common/src/org/jdrupes/vmoperator/common/K8sDynamicModels.java b/org.jdrupes.vmoperator.common/src/org/jdrupes/vmoperator/common/K8sDynamicModels.java
deleted file mode 100644
index d165c10..0000000
--- a/org.jdrupes.vmoperator.common/src/org/jdrupes/vmoperator/common/K8sDynamicModels.java
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * VM-Operator
- * Copyright (C) 2024 Michael N. Lipp
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see .
- */
-
-package org.jdrupes.vmoperator.common;
-
-import com.google.gson.Gson;
-import com.google.gson.JsonObject;
-import io.kubernetes.client.common.KubernetesListObject;
-
-/**
- * Represents a list of Kubernetes objects each of which is
- * represented using a JSON data structure.
- * Some information that is common to all Kubernetes objects,
- * notably the metadata, is made available through the methods
- * defined by {@link KubernetesListObject}.
- */
-public class K8sDynamicModels extends K8sDynamicModelsBase {
-
- /**
- * Initialize the object list using the given JSON data.
- *
- * @param delegate the gson instance to use for extracting structured data
- * @param data the data
- */
- public K8sDynamicModels(Gson delegate, JsonObject data) {
- super(K8sDynamicModel.class, delegate, data);
- }
-
-}
diff --git a/org.jdrupes.vmoperator.common/src/org/jdrupes/vmoperator/common/K8sDynamicModelsBase.java b/org.jdrupes.vmoperator.common/src/org/jdrupes/vmoperator/common/K8sDynamicModelsBase.java
deleted file mode 100644
index 4e21c0e..0000000
--- a/org.jdrupes.vmoperator.common/src/org/jdrupes/vmoperator/common/K8sDynamicModelsBase.java
+++ /dev/null
@@ -1,174 +0,0 @@
-/*
- * VM-Operator
- * Copyright (C) 2024 Michael N. Lipp
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see .
- */
-
-package org.jdrupes.vmoperator.common;
-
-import com.google.gson.Gson;
-import com.google.gson.JsonElement;
-import com.google.gson.JsonObject;
-import io.kubernetes.client.common.KubernetesListObject;
-import io.kubernetes.client.openapi.Configuration;
-import io.kubernetes.client.openapi.models.V1ListMeta;
-import java.lang.reflect.InvocationTargetException;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Objects;
-
-/**
- * Represents a list of Kubernetes objects each of which is
- * represented using a JSON data structure.
- * Some information that is common to all Kubernetes objects,
- * notably the metadata, is made available through the methods
- * defined by {@link KubernetesListObject}.
- */
-public class K8sDynamicModelsBase
- implements KubernetesListObject {
-
- private final JsonObject data;
- private final V1ListMeta metadata;
- private final List items;
-
- /**
- * Initialize the object list using the given JSON data.
- *
- * @param itemClass the item class
- * @param delegate the gson instance to use for extracting structured data
- * @param data the data
- */
- public K8sDynamicModelsBase(Class itemClass, Gson delegate,
- JsonObject data) {
- this.data = data;
- metadata = delegate.fromJson(data.get("metadata"), V1ListMeta.class);
- items = new ArrayList<>();
- for (JsonElement e : data.get("items").getAsJsonArray()) {
- try {
- items.add(itemClass.getConstructor(Gson.class, JsonObject.class)
- .newInstance(delegate, e.getAsJsonObject()));
- } catch (InstantiationException | IllegalAccessException
- | IllegalArgumentException | InvocationTargetException
- | NoSuchMethodException | SecurityException exc) {
- throw new IllegalArgumentException(exc);
- }
- }
- }
-
- @Override
- public String getApiVersion() {
- return apiVersion();
- }
-
- /**
- * Gets the API version. (Abbreviated method name for convenience.)
- *
- * @return the API version
- */
- public String apiVersion() {
- return data.get("apiVersion").getAsString();
- }
-
- @Override
- public String getKind() {
- return kind();
- }
-
- /**
- * Gets the kind. (Abbreviated method name for convenience.)
- *
- * @return the kind
- */
- public String kind() {
- return data.get("kind").getAsString();
- }
-
- @Override
- public V1ListMeta getMetadata() {
- return metadata;
- }
-
- /**
- * Gets the metadata. (Abbreviated method name for convenience.)
- *
- * @return the metadata
- */
- public V1ListMeta metadata() {
- return metadata;
- }
-
- /**
- * Returns the JSON representation of this object.
- *
- * @return the JOSN representation
- */
- public JsonObject data() {
- return data;
- }
-
- @Override
- public List getItems() {
- return items;
- }
-
- /**
- * Sets the api version.
- *
- * @param apiVersion the new api version
- */
- public void setApiVersion(String apiVersion) {
- data.addProperty("apiVersion", apiVersion);
- }
-
- /**
- * Sets the kind.
- *
- * @param kind the new kind
- */
- public void setKind(String kind) {
- data.addProperty("kind", kind);
- }
-
- /**
- * Sets the metadata.
- *
- * @param objectMeta the new metadata
- */
- public void setMetadata(V1ListMeta objectMeta) {
- data.add("metadata",
- Configuration.getDefaultApiClient().getJSON().getGson()
- .toJsonTree(objectMeta));
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(data);
- }
-
- @Override
- public boolean equals(Object obj) {
- if (this == obj) {
- return true;
- }
- if (obj == null) {
- return false;
- }
- if (getClass() != obj.getClass()) {
- return false;
- }
- K8sDynamicModelsBase> other = (K8sDynamicModelsBase>) obj;
- return Objects.equals(data, other.data);
- }
-}
diff --git a/org.jdrupes.vmoperator.common/src/org/jdrupes/vmoperator/common/K8sDynamicStub.java b/org.jdrupes.vmoperator.common/src/org/jdrupes/vmoperator/common/K8sDynamicStub.java
deleted file mode 100644
index c0303c2..0000000
--- a/org.jdrupes.vmoperator.common/src/org/jdrupes/vmoperator/common/K8sDynamicStub.java
+++ /dev/null
@@ -1,152 +0,0 @@
-/*
- * VM-Operator
- * Copyright (C) 2024 Michael N. Lipp
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see .
- */
-
-package org.jdrupes.vmoperator.common;
-
-import io.kubernetes.client.Discovery.APIResource;
-import io.kubernetes.client.apimachinery.GroupVersionKind;
-import io.kubernetes.client.openapi.ApiException;
-import io.kubernetes.client.util.generic.options.ListOptions;
-import java.io.Reader;
-import java.util.Collection;
-
-/**
- * A stub for namespaced custom objects. It uses a dynamic model
- * (see {@link K8sDynamicModel}) for representing the object's
- * state and can therefore be used for any kind of object, especially
- * custom objects.
- */
-public class K8sDynamicStub
- extends K8sDynamicStubBase {
-
- private static DynamicTypeAdapterFactory taf = new K8sDynamicModelTypeAdapterFactory();
-
- /**
- * Instantiates a new dynamic stub.
- *
- * @param client the client
- * @param context the context
- * @param namespace the namespace
- * @param name the name
- */
- public K8sDynamicStub(K8sClient client,
- APIResource context, String namespace, String name) {
- super(K8sDynamicModel.class, K8sDynamicModels.class, taf, client,
- context, namespace, name);
- }
-
- /**
- * Get a dynamic object stub. If the version in parameter
- * `gvk` is an empty string, the stub refers to the first object with
- * matching group and kind.
- *
- * @param client the client
- * @param gvk the group, version and kind
- * @param namespace the namespace
- * @param name the name
- * @return the stub if the object exists
- * @throws ApiException the api exception
- */
- public static K8sDynamicStub get(K8sClient client,
- GroupVersionKind gvk, String namespace, String name)
- throws ApiException {
- return new K8sDynamicStub(client, apiResource(client, gvk), namespace,
- name);
- }
-
- /**
- * Get a dynamic object stub.
- *
- * @param client the client
- * @param context the context
- * @param namespace the namespace
- * @param name the name
- * @return the stub if the object exists
- * @throws ApiException the api exception
- */
- public static K8sDynamicStub get(K8sClient client,
- APIResource context, String namespace, String name) {
- return new K8sDynamicStub(client, context, namespace, name);
- }
-
- /**
- * Creates a stub from yaml.
- *
- * @param client the client
- * @param context the context
- * @param yaml the yaml
- * @return the k 8 s dynamic stub
- * @throws ApiException the api exception
- */
- public static K8sDynamicStub createFromYaml(K8sClient client,
- APIResource context, Reader yaml) throws ApiException {
- var model = new K8sDynamicModel(client.getJSON().getGson(),
- K8s.yamlToJson(client, yaml));
- return K8sGenericStub.create(K8sDynamicModel.class,
- K8sDynamicModels.class, client, context, model,
- (c, ns, n) -> new K8sDynamicStub(c, context, ns, n));
- }
-
- /**
- * Get the stubs for the objects in the given namespace that match
- * the criteria from the given options.
- *
- * @param client the client
- * @param namespace the namespace
- * @param options the options
- * @return the collection
- * @throws ApiException the api exception
- */
- public static Collection list(K8sClient client,
- APIResource context, String namespace, ListOptions options)
- throws ApiException {
- return K8sGenericStub.list(K8sDynamicModel.class,
- K8sDynamicModels.class, client, context, namespace, options,
- (c, ns, n) -> new K8sDynamicStub(c, context, ns, n));
- }
-
- /**
- * Get the stubs for the objects in the given namespace.
- *
- * @param client the client
- * @param namespace the namespace
- * @return the collection
- * @throws ApiException the api exception
- */
- public static Collection list(K8sClient client,
- APIResource context, String namespace)
- throws ApiException {
- return list(client, context, namespace, new ListOptions());
- }
-
- /**
- * A factory for creating K8sDynamicModel(s) objects.
- */
- public static class K8sDynamicModelTypeAdapterFactory extends
- DynamicTypeAdapterFactory {
-
- /**
- * Instantiates a new dynamic model type adapter factory.
- */
- public K8sDynamicModelTypeAdapterFactory() {
- super(K8sDynamicModel.class, K8sDynamicModels.class);
- }
- }
-
-}
\ No newline at end of file
diff --git a/org.jdrupes.vmoperator.common/src/org/jdrupes/vmoperator/common/K8sDynamicStubBase.java b/org.jdrupes.vmoperator.common/src/org/jdrupes/vmoperator/common/K8sDynamicStubBase.java
deleted file mode 100644
index ae3f012..0000000
--- a/org.jdrupes.vmoperator.common/src/org/jdrupes/vmoperator/common/K8sDynamicStubBase.java
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * VM-Operator
- * Copyright (C) 2024 Michael N. Lipp
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see .
- */
-
-package org.jdrupes.vmoperator.common;
-
-import io.kubernetes.client.Discovery.APIResource;
-
-/**
- * A stub for namespaced custom objects. It uses a dynamic model
- * (see {@link K8sDynamicModel}) for representing the object's
- * state and can therefore be used for any kind of object, especially
- * custom objects.
- */
-public abstract class K8sDynamicStubBase> extends K8sGenericStub {
-
- /**
- * Instantiates a new dynamic stub.
- *
- * @param objectClass the object class
- * @param objectListClass the object list class
- * @param client the client
- * @param context the context
- * @param namespace the namespace
- * @param name the name
- */
- public K8sDynamicStubBase(Class objectClass,
- Class objectListClass, DynamicTypeAdapterFactory taf,
- K8sClient client, APIResource context, String namespace,
- String name) {
- super(objectClass, objectListClass, client, context, namespace, name);
- taf.register(client);
- }
-}
\ No newline at end of file
diff --git a/org.jdrupes.vmoperator.common/src/org/jdrupes/vmoperator/common/K8sGenericStub.java b/org.jdrupes.vmoperator.common/src/org/jdrupes/vmoperator/common/K8sGenericStub.java
deleted file mode 100644
index 9ba376f..0000000
--- a/org.jdrupes.vmoperator.common/src/org/jdrupes/vmoperator/common/K8sGenericStub.java
+++ /dev/null
@@ -1,474 +0,0 @@
-/*
- * VM-Operator
- * Copyright (C) 2024 Michael N. Lipp
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see .
- */
-
-package org.jdrupes.vmoperator.common;
-
-import io.kubernetes.client.Discovery.APIResource;
-import io.kubernetes.client.apimachinery.GroupVersionKind;
-import io.kubernetes.client.common.KubernetesListObject;
-import io.kubernetes.client.common.KubernetesObject;
-import io.kubernetes.client.custom.V1Patch;
-import io.kubernetes.client.openapi.ApiException;
-import io.kubernetes.client.util.Strings;
-import io.kubernetes.client.util.generic.GenericKubernetesApi;
-import io.kubernetes.client.util.generic.KubernetesApiResponse;
-import io.kubernetes.client.util.generic.dynamic.DynamicKubernetesObject;
-import io.kubernetes.client.util.generic.options.GetOptions;
-import io.kubernetes.client.util.generic.options.ListOptions;
-import io.kubernetes.client.util.generic.options.PatchOptions;
-import io.kubernetes.client.util.generic.options.UpdateOptions;
-import java.net.HttpURLConnection;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Optional;
-import java.util.function.Function;
-
-/**
- * A stub for namespaced custom objects. This stub provides the
- * functions common to all Kubernetes objects, but uses variables
- * for all types. This class should be used as base class only.
- *
- * @param the generic type
- * @param the generic type
- */
-@SuppressWarnings({ "PMD.TooManyMethods" })
-public class K8sGenericStub {
- protected final K8sClient client;
- private final GenericKubernetesApi api;
- protected final APIResource context;
- protected final String namespace;
- protected final String name;
-
- /**
- * Instantiates a new stub for the object specified. If the object
- * exists in the context specified, the version (see
- * {@link #version()} is bound to the existing object's version.
- * Else the stub is dangling with the version set to the context's
- * preferred version.
- *
- * @param objectClass the object class
- * @param objectListClass the object list class
- * @param client the client
- * @param context the context
- * @param namespace the namespace
- * @param name the name
- */
- @SuppressWarnings("PMD.AvoidInstantiatingObjectsInLoops")
- protected K8sGenericStub(Class objectClass, Class objectListClass,
- K8sClient client, APIResource context, String namespace,
- String name) {
- this.client = client;
- this.namespace = namespace;
- this.name = name;
-
- // Bind version
- var foundVersion = context.getPreferredVersion();
- GenericKubernetesApi testApi = null;
- GetOptions mdOpts
- = new GetOptions().isPartialObjectMetadataRequest(true);
- for (var version : candidateVersions(context)) {
- testApi = new GenericKubernetesApi<>(objectClass, objectListClass,
- context.getGroup(), version, context.getResourcePlural(),
- client);
- if (testApi.get(namespace, name, mdOpts)
- .isSuccess()) {
- foundVersion = version;
- break;
- }
- }
- if (foundVersion.equals(context.getPreferredVersion())) {
- this.context = context;
- } else {
- this.context = K8s.preferred(context, foundVersion);
- }
-
- api = Optional.ofNullable(testApi)
- .orElseGet(() -> new GenericKubernetesApi<>(objectClass,
- objectListClass, group(), version(), plural(), client));
- }
-
- /**
- * Gets the context.
- *
- * @return the context
- */
- public APIResource context() {
- return context;
- }
-
- /**
- * Gets the group.
- *
- * @return the group
- */
- public String group() {
- return context.getGroup();
- }
-
- /**
- * Gets the version.
- *
- * @return the version
- */
- public String version() {
- return context.getPreferredVersion();
- }
-
- /**
- * Gets the kind.
- *
- * @return the kind
- */
- public String kind() {
- return context.getKind();
- }
-
- /**
- * Gets the plural.
- *
- * @return the plural
- */
- public String plural() {
- return context.getResourcePlural();
- }
-
- /**
- * Gets the namespace.
- *
- * @return the namespace
- */
- public String namespace() {
- return namespace;
- }
-
- /**
- * Gets the name.
- *
- * @return the name
- */
- public String name() {
- return name;
- }
-
- /**
- * Delete the Kubernetes object.
- *
- * @throws ApiException the API exception
- */
- public void delete() throws ApiException {
- var result = api.delete(namespace, name);
- if (result.isSuccess()
- || result.getHttpStatusCode() == HttpURLConnection.HTTP_NOT_FOUND) {
- return;
- }
- result.throwsApiException();
- }
-
- /**
- * Retrieves and returns the current state of the object.
- *
- * @return the object's state
- * @throws ApiException the api exception
- */
- public Optional model() throws ApiException {
- return K8s.optional(api.get(namespace, name));
- }
-
- /**
- * Updates the object's status. Does not retry in case of conflict.
- *
- * @param object the current state of the object (passed to `status`)
- * @param updater function that returns the new status
- * @return the updated model or empty if the object was not found
- * @throws ApiException the api exception
- */
- public Optional updateStatus(O object, Function updater)
- throws ApiException {
- return K8s.optional(api.updateStatus(object, updater));
- }
-
- /**
- * Updates the status of the given object. In case of conflict,
- * get the current version of the object and tries again. Retries
- * up to `retries` times.
- *
- * @param updater the function updating the status
- * @param current the current state of the object, used for the first
- * attempt to update
- * @param retries the retries in case of conflict
- * @return the updated model or empty if the object was not found
- * @throws ApiException the api exception
- */
- @SuppressWarnings({ "PMD.AssignmentInOperand" })
- public Optional updateStatus(Function updater, O current,
- int retries) throws ApiException {
- while (true) {
- try {
- if (current == null) {
- current = api.get(namespace, name)
- .throwsApiException().getObject();
- }
- return updateStatus(current, updater);
- } catch (ApiException e) {
- if (HttpURLConnection.HTTP_CONFLICT != e.getCode()
- || retries-- <= 0) {
- throw e;
- }
- // Get current version for new attempt
- current = null;
- }
- }
- }
-
- /**
- * Gets the object and updates the status. In case of conflict, retries
- * up to `retries` times.
- *
- * @param updater the function updating the status
- * @param retries the retries in case of conflict
- * @return the updated model or empty if the object was not found
- * @throws ApiException the api exception
- */
- public Optional updateStatus(Function updater, int retries)
- throws ApiException {
- return updateStatus(updater, null, retries);
- }
-
- /**
- * Updates the status of the given object. In case of conflict,
- * get the current version of the object and tries again. Retries
- * up to `retries` times.
- *
- * @param updater the function updating the status
- * @param current the current
- * @return the kubernetes api response
- * the updated model or empty if not successful
- * @throws ApiException the api exception
- */
- public Optional updateStatus(Function updater, O current)
- throws ApiException {
- return updateStatus(updater, current, 16);
- }
-
- /**
- * Updates the status. In case of conflict, retries up to 16 times.
- *
- * @param updater the function updating the status
- * @return the kubernetes api response
- * the updated model or empty if not successful
- * @throws ApiException the api exception
- */
- public Optional updateStatus(Function updater)
- throws ApiException {
- return updateStatus(updater, null);
- }
-
- /**
- * Patch the object.
- *
- * @param patchType the patch type
- * @param patch the patch
- * @param options the options
- * @return the kubernetes api response if successful
- * @throws ApiException the api exception
- */
- public Optional patch(String patchType, V1Patch patch,
- PatchOptions options) throws ApiException {
- return K8s
- .optional(api.patch(namespace, name, patchType, patch, options)
- .throwsApiException());
- }
-
- /**
- * Patch the object using default options.
- *
- * @param patchType the patch type
- * @param patch the patch
- * @return the kubernetes api response if successful
- * @throws ApiException the api exception
- */
- public Optional
- patch(String patchType, V1Patch patch) throws ApiException {
- PatchOptions opts = new PatchOptions();
- return patch(patchType, patch, opts);
- }
-
- /**
- * Apply the given definition.
- *
- * @param def the def
- * @return the kubernetes api response if successful
- * @throws ApiException the api exception
- */
- public Optional apply(DynamicKubernetesObject def) throws ApiException {
- PatchOptions opts = new PatchOptions();
- opts.setForce(true);
- opts.setFieldManager("kubernetes-java-kubectl-apply");
- return patch(V1Patch.PATCH_FORMAT_APPLY_YAML,
- new V1Patch(client.getJSON().serialize(def)), opts);
- }
-
- /**
- * Update the object.
- *
- * @param object the object
- * @return the kubernetes api response
- * @throws ApiException the api exception
- */
- public KubernetesApiResponse update(O object) throws ApiException {
- return api.update(object).throwsApiException();
- }
-
- /**
- * Update the object.
- *
- * @param object the object
- * @param options the options
- * @return the kubernetes api response
- * @throws ApiException the api exception
- */
- public KubernetesApiResponse update(O object, UpdateOptions options)
- throws ApiException {
- return api.update(object, options).throwsApiException();
- }
-
- /**
- * A supplier for generic stubs.
- *
- * @param the object type
- * @param the object list type
- * @param the result type
- */
- @FunctionalInterface
- public interface GenericSupplier> {
-
- /**
- * Gets a new stub.
- *
- * @param client the client
- * @param namespace the namespace
- * @param name the name
- * @return the result
- */
- R get(K8sClient client, String namespace, String name);
- }
-
- @Override
- @SuppressWarnings("PMD.UseLocaleWithCaseConversions")
- public String toString() {
- return (Strings.isNullOrEmpty(group()) ? "" : group() + "/")
- + version().toUpperCase() + kind() + " " + namespace + ":" + name;
- }
-
- /**
- * Get a namespaced object stub for a newly created object.
- *
- * @param the object type
- * @param the object list type
- * @param the stub type
- * @param objectClass the object class
- * @param objectListClass the object list class
- * @param client the client
- * @param context the context
- * @param model the model
- * @param provider the provider
- * @return the stub if the object exists
- * @throws ApiException the api exception
- */
- public static >
- R create(Class objectClass, Class objectListClass,
- K8sClient client, APIResource context, O model,
- GenericSupplier provider) throws ApiException {
- var api = new GenericKubernetesApi<>(objectClass, objectListClass,
- context.getGroup(), context.getPreferredVersion(),
- context.getResourcePlural(), client);
- api.create(model).throwsApiException();
- return provider.get(client, model.getMetadata().getNamespace(),
- model.getMetadata().getName());
- }
-
- /**
- * Get the stubs for the objects in the given namespace that match
- * the criteria from the given options.
- *
- * @param the object type
- * @param the object list type
- * @param the stub type
- * @param objectClass the object class
- * @param objectListClass the object list class
- * @param client the client
- * @param context the context
- * @param namespace the namespace
- * @param options the options
- * @param provider the provider
- * @return the collection
- * @throws ApiException the api exception
- */
- public static >
- Collection list(Class objectClass, Class objectListClass,
- K8sClient client, APIResource context, String namespace,
- ListOptions options, GenericSupplier provider)
- throws ApiException {
- var result = new ArrayList();
- for (var version : candidateVersions(context)) {
- @SuppressWarnings("PMD.AvoidInstantiatingObjectsInLoops")
- var api = new GenericKubernetesApi<>(objectClass, objectListClass,
- context.getGroup(), version, context.getResourcePlural(),
- client);
- var objs = api.list(namespace, options).throwsApiException();
- for (var item : objs.getObject().getItems()) {
- result.add(provider.get(client, namespace,
- item.getMetadata().getName()));
- }
- }
- return result;
- }
-
- private static List candidateVersions(APIResource context) {
- var result = new LinkedList<>(context.getVersions());
- result.remove(context.getPreferredVersion());
- result.add(0, context.getPreferredVersion());
- return result;
- }
-
- /**
- * Api resource.
- *
- * @param client the client
- * @param gvk the gvk
- * @return the API resource
- * @throws ApiException the api exception
- */
- public static APIResource apiResource(K8sClient client,
- GroupVersionKind gvk) throws ApiException {
- var context = K8s.context(client, gvk.getGroup(), gvk.getVersion(),
- gvk.getKind());
- if (context.isEmpty()) {
- throw new ApiException("No known API for " + gvk.getGroup()
- + "/" + gvk.getVersion() + " " + gvk.getKind());
- }
- return context.get();
- }
-
-}
diff --git a/org.jdrupes.vmoperator.common/src/org/jdrupes/vmoperator/common/K8sObserver.java b/org.jdrupes.vmoperator.common/src/org/jdrupes/vmoperator/common/K8sObserver.java
deleted file mode 100644
index 9e22382..0000000
--- a/org.jdrupes.vmoperator.common/src/org/jdrupes/vmoperator/common/K8sObserver.java
+++ /dev/null
@@ -1,237 +0,0 @@
-/*
- * VM-Operator
- * Copyright (C) 2024 Michael N. Lipp
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see .
- */
-
-package org.jdrupes.vmoperator.common;
-
-import io.kubernetes.client.Discovery.APIResource;
-import io.kubernetes.client.common.KubernetesListObject;
-import io.kubernetes.client.common.KubernetesObject;
-import io.kubernetes.client.openapi.ApiException;
-import io.kubernetes.client.util.Watch.Response;
-import io.kubernetes.client.util.generic.GenericKubernetesApi;
-import io.kubernetes.client.util.generic.options.ListOptions;
-import java.time.Duration;
-import java.time.Instant;
-import java.util.Optional;
-import java.util.function.BiConsumer;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-import org.jgrapes.core.Components;
-
-/**
- * An observer that watches namespaced resources in a given context and
- * invokes a handler on changes.
- *
- * @param the object type for the context
- * @param the object list type for the context
- */
-public class K8sObserver {
-
- /**
- * The type of change reported by {@link Response} as enum.
- */
- public enum ResponseType {
- ADDED, MODIFIED, DELETED
- }
-
- protected final Logger logger = Logger.getLogger(getClass().getName());
-
- protected final K8sClient client;
- protected final GenericKubernetesApi api;
- protected final APIResource context;
- protected final String namespace;
- protected final ListOptions options;
- protected final Thread thread;
- protected BiConsumer> handler;
- protected BiConsumer, Throwable> onTerminated;
-
- /**
- * Create and start a new observer for objects in the given context
- * (using preferred version) and namespace with the given options.
- *
- * @param objectClass the object class
- * @param objectListClass the object list class
- * @param client the client
- * @param context the context
- * @param namespace the namespace
- * @param options the options
- */
- @SuppressWarnings({ "PMD.AvoidCatchingThrowable",
- "PMD.CognitiveComplexity", "PMD.AvoidCatchingGenericException" })
- public K8sObserver(Class objectClass, Class objectListClass,
- K8sClient client, APIResource context, String namespace,
- ListOptions options) {
- this.client = client;
- this.context = context;
- this.namespace = namespace;
- this.options = options;
-
- api = new GenericKubernetesApi<>(objectClass, objectListClass,
- context.getGroup(), context.getPreferredVersion(),
- context.getResourcePlural(), client);
- thread = (Components.useVirtualThreads() ? Thread.ofVirtual()
- : Thread.ofPlatform()).unstarted(() -> {
- try {
- logger.fine(() -> "Observing " + context.getResourcePlural()
- + " (" + context.getPreferredVersion() + ")"
- + Optional.ofNullable(options.getLabelSelector())
- .map(ls -> " with labels " + ls).orElse("")
- + " in " + namespace);
-
- // Watch sometimes terminates without apparent reason.
- while (!Thread.currentThread().isInterrupted()) {
- Instant startedAt = Instant.now();
- try {
- var changed
- = api.watch(namespace, options).iterator();
- while (changed.hasNext()) {
- var response = changed.next();
- logger.fine(() -> "Resource "
- + context.getKind() + "/"
- + response.object.getMetadata().getName()
- + " " + response.type);
- handler.accept(client, response);
- }
- } catch (ApiException | RuntimeException e) {
- logger.log(Level.FINE, e, () -> "Problem watching"
- + " resource " + context.getKind()
- + " (will retry): " + e.getMessage());
- delayRestart(startedAt);
- }
- }
- if (onTerminated != null) {
- onTerminated.accept(this, null);
- }
- } catch (Throwable e) {
- logger.log(Level.SEVERE, e, () -> "Probem watching: "
- + e.getMessage());
- if (onTerminated != null) {
- onTerminated.accept(this, e);
- }
- }
- });
- }
-
- @SuppressWarnings("PMD.AvoidLiteralsInIfCondition")
- private void delayRestart(Instant started) {
- var runningFor = Duration
- .between(started, Instant.now()).toMillis();
- if (runningFor < 5000) {
- logger.log(Level.FINE, () -> "Waiting... ");
- try {
- Thread.sleep(5000 - runningFor);
- } catch (InterruptedException e1) { // NOPMD
- // Retry
- }
- logger.log(Level.FINE, () -> "Retrying");
- }
- }
-
- /**
- * Sets the handler.
- *
- * @param handler the handler
- * @return the observer
- */
- public K8sObserver
- handler(BiConsumer> handler) {
- this.handler = handler;
- return this;
- }
-
- /**
- * Sets a function to invoke if the observer terminates. First argument
- * is this observer, the second is the throwable that caused the
- * abnormal termination or `null` if the observer was terminated
- * by {@link #stop()}.
- *
- * @param onTerminated the on terminated
- * @return the observer
- */
- public K8sObserver onTerminated(
- BiConsumer, Throwable> onTerminated) {
- this.onTerminated = onTerminated;
- return this;
- }
-
- /**
- * Start the observer.
- *
- * @return the observer
- */
- public K8sObserver start() {
- if (handler == null) {
- throw new IllegalStateException("No handler defined");
- }
- thread.start();
- return this;
- }
-
- /**
- * Stops the observer.
- *
- * @return the observer
- */
- public K8sObserver stop() {
- thread.interrupt();
- return this;
- }
-
- /**
- * Returns the client.
- *
- * @return the client
- */
- public K8sClient client() {
- return client;
- }
-
- /**
- * Returns the context.
- *
- * @return the context
- */
- public APIResource context() {
- return context;
- }
-
- /**
- * Returns the observed namespace.
- *
- * @return the namespace
- */
- public String getNamespace() {
- return namespace;
- }
-
- /**
- * Returns the options for object selection.
- *
- * @return the list options
- */
- public ListOptions options() {
- return options;
- }
-
- @Override
- public String toString() {
- return "Observer for " + K8s.toString(context) + " " + namespace;
- }
-
-}
diff --git a/org.jdrupes.vmoperator.common/src/org/jdrupes/vmoperator/common/K8sV1ConfigMapStub.java b/org.jdrupes.vmoperator.common/src/org/jdrupes/vmoperator/common/K8sV1ConfigMapStub.java
deleted file mode 100644
index 07c59b2..0000000
--- a/org.jdrupes.vmoperator.common/src/org/jdrupes/vmoperator/common/K8sV1ConfigMapStub.java
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * VM-Operator
- * Copyright (C) 2024 Michael N. Lipp
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see .
- */
-
-package org.jdrupes.vmoperator.common;
-
-import io.kubernetes.client.Discovery.APIResource;
-import io.kubernetes.client.openapi.models.V1ConfigMap;
-import io.kubernetes.client.openapi.models.V1ConfigMapList;
-import java.util.List;
-
-/**
- * A stub for config maps (v1).
- */
-public class K8sV1ConfigMapStub
- extends K8sGenericStub {
-
- public static final APIResource CONTEXT = new APIResource("", List.of("v1"),
- "v1", "ConfigMap", true, "configmaps", "configmap");
-
- /**
- * Instantiates a new stub.
- *
- * @param client the client
- * @param namespace the namespace
- * @param name the name
- */
- protected K8sV1ConfigMapStub(K8sClient client, String namespace,
- String name) {
- super(V1ConfigMap.class, V1ConfigMapList.class, client,
- CONTEXT, namespace, name);
- }
-
- /**
- * Gets the stub for the given namespace and name.
- *
- * @param client the client
- * @param namespace the namespace
- * @param name the name
- * @return the config map stub
- */
- public static K8sV1ConfigMapStub get(K8sClient client, String namespace,
- String name) {
- return new K8sV1ConfigMapStub(client, namespace, name);
- }
-}
\ No newline at end of file
diff --git a/org.jdrupes.vmoperator.common/src/org/jdrupes/vmoperator/common/K8sV1DeploymentStub.java b/org.jdrupes.vmoperator.common/src/org/jdrupes/vmoperator/common/K8sV1DeploymentStub.java
deleted file mode 100644
index 9075a84..0000000
--- a/org.jdrupes.vmoperator.common/src/org/jdrupes/vmoperator/common/K8sV1DeploymentStub.java
+++ /dev/null
@@ -1,78 +0,0 @@
-/*
- * VM-Operator
- * Copyright (C) 2024 Michael N. Lipp
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see .
- */
-
-package org.jdrupes.vmoperator.common;
-
-import io.kubernetes.client.Discovery.APIResource;
-import io.kubernetes.client.custom.V1Patch;
-import io.kubernetes.client.openapi.ApiException;
-import io.kubernetes.client.openapi.models.V1Deployment;
-import io.kubernetes.client.openapi.models.V1DeploymentList;
-import java.util.List;
-import java.util.Optional;
-
-/**
- * A stub for pods (v1).
- */
-public class K8sV1DeploymentStub
- extends K8sGenericStub {
-
- /** The deployment's context. */
- public static final APIResource CONTEXT = new APIResource("apps",
- List.of("v1"), "v1", "Pod", true, "deployments", "deployment");
-
- /**
- * Instantiates a new stub.
- *
- * @param client the client
- * @param namespace the namespace
- * @param name the name
- */
- protected K8sV1DeploymentStub(K8sClient client, String namespace,
- String name) {
- super(V1Deployment.class, V1DeploymentList.class, client,
- CONTEXT, namespace, name);
- }
-
- /**
- * Scales the deployment.
- *
- * @param replicas the replicas
- * @return the new model or empty if not successful
- * @throws ApiException the API exception
- */
- public Optional scale(int replicas) throws ApiException {
- return patch(V1Patch.PATCH_FORMAT_JSON_PATCH,
- new V1Patch("[{\"op\": \"replace\", \"path\": \"/spec/replicas"
- + "\", \"value\": " + replicas + "}]"),
- client.defaultPatchOptions());
- }
-
- /**
- * Gets the stub for the given namespace and name.
- *
- * @param client the client
- * @param namespace the namespace
- * @param name the name
- * @return the deployment stub
- */
- public static K8sV1DeploymentStub get(K8sClient client, String namespace,
- String name) {
- return new K8sV1DeploymentStub(client, namespace, name);
- }
-}
\ No newline at end of file
diff --git a/org.jdrupes.vmoperator.common/src/org/jdrupes/vmoperator/common/K8sV1NodeStub.java b/org.jdrupes.vmoperator.common/src/org/jdrupes/vmoperator/common/K8sV1NodeStub.java
deleted file mode 100644
index ea1237d..0000000
--- a/org.jdrupes.vmoperator.common/src/org/jdrupes/vmoperator/common/K8sV1NodeStub.java
+++ /dev/null
@@ -1,83 +0,0 @@
-/*
- * VM-Operator
- * Copyright (C) 2024 Michael N. Lipp
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see .
- */
-
-package org.jdrupes.vmoperator.common;
-
-import io.kubernetes.client.Discovery.APIResource;
-import io.kubernetes.client.openapi.ApiException;
-import io.kubernetes.client.openapi.models.V1Node;
-import io.kubernetes.client.openapi.models.V1NodeList;
-import io.kubernetes.client.util.generic.options.ListOptions;
-import java.util.Collection;
-import java.util.List;
-
-/**
- * A stub for nodes (v1).
- */
-public class K8sV1NodeStub extends K8sClusterGenericStub {
-
- public static final APIResource CONTEXT = new APIResource("", List.of("v1"),
- "v1", "Node", true, "nodes", "node");
-
- /**
- * Instantiates a new stub.
- *
- * @param client the client
- * @param name the name
- */
- protected K8sV1NodeStub(K8sClient client, String name) {
- super(V1Node.class, V1NodeList.class, client, CONTEXT, name);
- }
-
- /**
- * Gets the stub for the given name.
- *
- * @param client the client
- * @param name the name
- * @return the config map stub
- */
- public static K8sV1NodeStub get(K8sClient client, String name) {
- return new K8sV1NodeStub(client, name);
- }
-
- /**
- * Get the stubs for the objects that match
- * the criteria from the given options.
- *
- * @param client the client
- * @param options the options
- * @return the collection
- * @throws ApiException the api exception
- */
- public static Collection list(K8sClient client,
- ListOptions options) throws ApiException {
- return K8sClusterGenericStub.list(V1Node.class, V1NodeList.class,
- client, CONTEXT, options, K8sV1NodeStub::getGeneric);
- }
-
- /**
- * Provide {@link GenericSupplier}.
- */
- @SuppressWarnings({ "PMD.UnusedFormalParameter" })
- private static K8sV1NodeStub getGeneric(Class objectClass,
- Class objectListClass, K8sClient client,
- APIResource context, String name) {
- return new K8sV1NodeStub(client, name);
- }
-
-}
\ No newline at end of file
diff --git a/org.jdrupes.vmoperator.common/src/org/jdrupes/vmoperator/common/K8sV1PodStub.java b/org.jdrupes.vmoperator.common/src/org/jdrupes/vmoperator/common/K8sV1PodStub.java
deleted file mode 100644
index f21bb47..0000000
--- a/org.jdrupes.vmoperator.common/src/org/jdrupes/vmoperator/common/K8sV1PodStub.java
+++ /dev/null
@@ -1,78 +0,0 @@
-/*
- * VM-Operator
- * Copyright (C) 2024 Michael N. Lipp
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see .
- */
-
-package org.jdrupes.vmoperator.common;
-
-import io.kubernetes.client.Discovery.APIResource;
-import io.kubernetes.client.openapi.ApiException;
-import io.kubernetes.client.openapi.models.V1Pod;
-import io.kubernetes.client.openapi.models.V1PodList;
-import io.kubernetes.client.util.generic.options.ListOptions;
-import java.util.Collection;
-import java.util.List;
-
-/**
- * A stub for pods (v1).
- */
-public class K8sV1PodStub extends K8sGenericStub {
-
- /** The pods' context. */
- public static final APIResource CONTEXT
- = new APIResource("", List.of("v1"), "v1", "Pod", true, "pods", "pod");
-
- /**
- * Instantiates a new stub.
- *
- * @param client the client
- * @param namespace the namespace
- * @param name the name
- */
- protected K8sV1PodStub(K8sClient client, String namespace, String name) {
- super(V1Pod.class, V1PodList.class, client, CONTEXT, namespace, name);
- }
-
- /**
- * Gets the stub for the given namespace and name.
- *
- * @param client the client
- * @param namespace the namespace
- * @param name the name
- * @return the kpod stub
- */
- public static K8sV1PodStub get(K8sClient client, String namespace,
- String name) {
- return new K8sV1PodStub(client, namespace, name);
- }
-
- /**
- * Get the stubs for the objects in the given namespace that match
- * the criteria from the given options.
- *
- * @param client the client
- * @param namespace the namespace
- * @param options the options
- * @return the collection
- * @throws ApiException the api exception
- */
- public static Collection list(K8sClient client,
- String namespace, ListOptions options) throws ApiException {
- return K8sGenericStub.list(V1Pod.class, V1PodList.class, client,
- CONTEXT, namespace, options, (clnt, nscp,
- name) -> new K8sV1PodStub(clnt, nscp, name));
- }
-}
\ No newline at end of file
diff --git a/org.jdrupes.vmoperator.common/src/org/jdrupes/vmoperator/common/K8sV1PvcStub.java b/org.jdrupes.vmoperator.common/src/org/jdrupes/vmoperator/common/K8sV1PvcStub.java
deleted file mode 100644
index c46a60f..0000000
--- a/org.jdrupes.vmoperator.common/src/org/jdrupes/vmoperator/common/K8sV1PvcStub.java
+++ /dev/null
@@ -1,81 +0,0 @@
-/*
- * VM-Operator
- * Copyright (C) 2024 Michael N. Lipp
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see .
- */
-
-package org.jdrupes.vmoperator.common;
-
-import io.kubernetes.client.Discovery.APIResource;
-import io.kubernetes.client.openapi.ApiException;
-import io.kubernetes.client.openapi.models.V1PersistentVolumeClaim;
-import io.kubernetes.client.openapi.models.V1PersistentVolumeClaimList;
-import io.kubernetes.client.util.generic.options.ListOptions;
-import java.util.Collection;
-import java.util.List;
-
-/**
- * A stub for pods (v1).
- */
-public class K8sV1PvcStub extends
- K8sGenericStub {
-
- /** The pods' context. */
- public static final APIResource CONTEXT
- = new APIResource("", List.of("v1"), "v1", "PersistentVolumeClaim",
- true, "persistentvolumeclaims", "persistentvolumeclaim");
-
- /**
- * Instantiates a new stub.
- *
- * @param client the client
- * @param namespace the namespace
- * @param name the name
- */
- protected K8sV1PvcStub(K8sClient client, String namespace, String name) {
- super(V1PersistentVolumeClaim.class, V1PersistentVolumeClaimList.class,
- client, CONTEXT, namespace, name);
- }
-
- /**
- * Gets the stub for the given namespace and name.
- *
- * @param client the client
- * @param namespace the namespace
- * @param name the name
- * @return the kpod stub
- */
- public static K8sV1PvcStub get(K8sClient client, String namespace,
- String name) {
- return new K8sV1PvcStub(client, namespace, name);
- }
-
- /**
- * Get the stubs for the objects in the given namespace that match
- * the criteria from the given options.
- *
- * @param client the client
- * @param namespace the namespace
- * @param options the options
- * @return the collection
- * @throws ApiException the api exception
- */
- public static Collection list(K8sClient client,
- String namespace, ListOptions options) throws ApiException {
- return K8sGenericStub.list(V1PersistentVolumeClaim.class,
- V1PersistentVolumeClaimList.class, client, CONTEXT, namespace,
- options, (clnt, nscp, name) -> new K8sV1PvcStub(clnt, nscp, name));
- }
-}
\ No newline at end of file
diff --git a/org.jdrupes.vmoperator.common/src/org/jdrupes/vmoperator/common/K8sV1SecretStub.java b/org.jdrupes.vmoperator.common/src/org/jdrupes/vmoperator/common/K8sV1SecretStub.java
deleted file mode 100644
index 9c1c086..0000000
--- a/org.jdrupes.vmoperator.common/src/org/jdrupes/vmoperator/common/K8sV1SecretStub.java
+++ /dev/null
@@ -1,92 +0,0 @@
-/*
- * VM-Operator
- * Copyright (C) 2024 Michael N. Lipp
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see .
- */
-
-package org.jdrupes.vmoperator.common;
-
-import io.kubernetes.client.Discovery.APIResource;
-import io.kubernetes.client.openapi.ApiException;
-import io.kubernetes.client.openapi.models.V1Secret;
-import io.kubernetes.client.openapi.models.V1SecretList;
-import io.kubernetes.client.util.generic.options.ListOptions;
-import java.util.Collection;
-import java.util.List;
-
-/**
- * A stub for secrets (v1).
- */
-public class K8sV1SecretStub extends K8sGenericStub {
-
- public static final APIResource CONTEXT = new APIResource("", List.of("v1"),
- "v1", "Secret", true, "secrets", "secret");
-
- /**
- * Instantiates a new stub.
- *
- * @param client the client
- * @param namespace the namespace
- * @param name the name
- */
- protected K8sV1SecretStub(K8sClient client, String namespace,
- String name) {
- super(V1Secret.class, V1SecretList.class, client,
- CONTEXT, namespace, name);
- }
-
- /**
- * Gets the stub for the given namespace and name.
- *
- * @param client the client
- * @param namespace the namespace
- * @param name the name
- * @return the config map stub
- */
- public static K8sV1SecretStub get(K8sClient client, String namespace,
- String name) {
- return new K8sV1SecretStub(client, namespace, name);
- }
-
- /**
- * Creates an object stub from a model.
- *
- * @param client the client
- * @param model the model
- * @return the k 8 s dynamic stub
- * @throws ApiException the api exception
- */
- public static K8sV1SecretStub create(K8sClient client, V1Secret model)
- throws ApiException {
- return K8sGenericStub.create(V1Secret.class,
- V1SecretList.class, client, CONTEXT, model, K8sV1SecretStub::new);
- }
-
- /**
- * Get the stubs for the objects in the given namespace that match
- * the criteria from the given options.
- *
- * @param client the client
- * @param namespace the namespace
- * @param options the options
- * @return the collection
- * @throws ApiException the api exception
- */
- public static Collection list(K8sClient client,
- String namespace, ListOptions options) throws ApiException {
- return K8sGenericStub.list(V1Secret.class, V1SecretList.class, client,
- CONTEXT, namespace, options, K8sV1SecretStub::new);
- }
-}
\ No newline at end of file
diff --git a/org.jdrupes.vmoperator.common/src/org/jdrupes/vmoperator/common/K8sV1ServiceStub.java b/org.jdrupes.vmoperator.common/src/org/jdrupes/vmoperator/common/K8sV1ServiceStub.java
deleted file mode 100644
index 863f86f..0000000
--- a/org.jdrupes.vmoperator.common/src/org/jdrupes/vmoperator/common/K8sV1ServiceStub.java
+++ /dev/null
@@ -1,79 +0,0 @@
-/*
- * VM-Operator
- * Copyright (C) 2024 Michael N. Lipp
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see .
- */
-
-package org.jdrupes.vmoperator.common;
-
-import io.kubernetes.client.Discovery.APIResource;
-import io.kubernetes.client.openapi.ApiException;
-import io.kubernetes.client.openapi.models.V1Service;
-import io.kubernetes.client.openapi.models.V1ServiceList;
-import io.kubernetes.client.util.generic.options.ListOptions;
-import java.util.Collection;
-import java.util.List;
-
-/**
- * A stub for secrets (v1).
- */
-public class K8sV1ServiceStub extends K8sGenericStub {
-
- public static final APIResource CONTEXT = new APIResource("", List.of("v1"),
- "v1", "Service", true, "services", "service");
-
- /**
- * Instantiates a new stub.
- *
- * @param client the client
- * @param namespace the namespace
- * @param name the name
- */
- protected K8sV1ServiceStub(K8sClient client, String namespace,
- String name) {
- super(V1Service.class, V1ServiceList.class, client, CONTEXT, namespace,
- name);
- }
-
- /**
- * Gets the stub for the given namespace and name.
- *
- * @param client the client
- * @param namespace the namespace
- * @param name the name
- * @return the config map stub
- */
- public static K8sV1ServiceStub get(K8sClient client, String namespace,
- String name) {
- return new K8sV1ServiceStub(client, namespace, name);
- }
-
- /**
- * Get the stubs for the objects in the given namespace that match
- * the criteria from the given options.
- *
- * @param client the client
- * @param namespace the namespace
- * @param options the options
- * @return the collection
- * @throws ApiException the api exception
- */
- public static Collection list(K8sClient client,
- String namespace, ListOptions options) throws ApiException {
- return K8sGenericStub.list(V1Service.class, V1ServiceList.class, client,
- CONTEXT, namespace, options,
- (clnt, nscp, name) -> new K8sV1ServiceStub(clnt, nscp, name));
- }
-}
\ No newline at end of file
diff --git a/org.jdrupes.vmoperator.common/src/org/jdrupes/vmoperator/common/K8sV1StatefulSetStub.java b/org.jdrupes.vmoperator.common/src/org/jdrupes/vmoperator/common/K8sV1StatefulSetStub.java
deleted file mode 100644
index be30b00..0000000
--- a/org.jdrupes.vmoperator.common/src/org/jdrupes/vmoperator/common/K8sV1StatefulSetStub.java
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * VM-Operator
- * Copyright (C) 2024 Michael N. Lipp
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see .
- */
-
-package org.jdrupes.vmoperator.common;
-
-import io.kubernetes.client.Discovery.APIResource;
-import io.kubernetes.client.openapi.models.V1StatefulSet;
-import io.kubernetes.client.openapi.models.V1StatefulSetList;
-import java.util.List;
-
-/**
- * A stub for stateful sets (v1).
- */
-public class K8sV1StatefulSetStub
- extends K8sGenericStub {
-
- /** The stateful sets' context */
- public static final APIResource CONTEXT
- = new APIResource("apps", List.of("v1"), "v1", "StatefulSet", true,
- "statefulsets", "statefulset");
-
- /**
- * Instantiates a new stub.
- *
- * @param client the client
- * @param namespace the namespace
- * @param name the name
- */
- protected K8sV1StatefulSetStub(K8sClient client, String namespace,
- String name) {
- super(V1StatefulSet.class, V1StatefulSetList.class, client, CONTEXT,
- namespace, name);
- }
-
- /**
- * Gets the stub for the given namespace and name.
- *
- * @param client the client
- * @param namespace the namespace
- * @param name the name
- * @return the stateful set stub
- */
- public static K8sV1StatefulSetStub get(K8sClient client, String namespace,
- String name) {
- return new K8sV1StatefulSetStub(client, namespace, name);
- }
-}
\ No newline at end of file
diff --git a/org.jdrupes.vmoperator.common/src/org/jdrupes/vmoperator/common/VmDefinition.java b/org.jdrupes.vmoperator.common/src/org/jdrupes/vmoperator/common/VmDefinition.java
deleted file mode 100644
index a0b66bf..0000000
--- a/org.jdrupes.vmoperator.common/src/org/jdrupes/vmoperator/common/VmDefinition.java
+++ /dev/null
@@ -1,499 +0,0 @@
-/*
- * VM-Operator
- * Copyright (C) 2025 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 .
- */
-
-package org.jdrupes.vmoperator.common;
-
-import com.fasterxml.jackson.databind.ObjectMapper;
-import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
-import com.google.gson.Gson;
-import com.google.gson.JsonObject;
-import io.kubernetes.client.openapi.JSON;
-import io.kubernetes.client.openapi.models.V1Condition;
-import java.time.Instant;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.EnumSet;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Objects;
-import java.util.Optional;
-import java.util.Set;
-import java.util.function.Function;
-import java.util.logging.Logger;
-import java.util.stream.Collectors;
-import org.jdrupes.vmoperator.common.Constants.Status;
-import org.jdrupes.vmoperator.common.Constants.Status.Condition;
-import org.jdrupes.vmoperator.common.Constants.Status.Condition.Reason;
-import org.jdrupes.vmoperator.util.DataPath;
-
-/**
- * Represents a VM definition.
- */
-@SuppressWarnings({ "PMD.DataClass", "PMD.TooManyMethods" })
-public class VmDefinition extends K8sDynamicModel {
-
- @SuppressWarnings({ "unused" })
- private static final Logger logger
- = Logger.getLogger(VmDefinition.class.getName());
- @SuppressWarnings("PMD.FieldNamingConventions")
- private static final Gson gson = new JSON().getGson();
- @SuppressWarnings("PMD.FieldNamingConventions")
- private static final ObjectMapper objectMapper
- = new ObjectMapper().registerModule(new JavaTimeModule());
-
- private final Model model;
- private VmExtraData extraData;
-
- /**
- * The VM state from the VM definition.
- */
- public enum RequestedVmState {
- STOPPED, RUNNING
- }
-
- /**
- * Permissions for accessing and manipulating the VM.
- */
- public enum Permission {
- START("start"), STOP("stop"), RESET("reset"),
- ACCESS_CONSOLE("accessConsole"), TAKE_CONSOLE("takeConsole");
-
- @SuppressWarnings("PMD.UseConcurrentHashMap")
- private static Map reprs = new HashMap<>();
-
- static {
- for (var value : EnumSet.allOf(Permission.class)) {
- reprs.put(value.repr, value);
- }
- }
-
- private final String repr;
-
- Permission(String repr) {
- this.repr = repr;
- }
-
- /**
- * Create permission from representation in CRD.
- *
- * @param value the value
- * @return the permission
- */
- @SuppressWarnings("PMD.AvoidLiteralsInIfCondition")
- public static Set parse(String value) {
- if ("*".equals(value)) {
- return EnumSet.allOf(Permission.class);
- }
- return Set.of(reprs.get(value));
- }
-
- /**
- * To string.
- *
- * @return the string
- */
- @Override
- public String toString() {
- return repr;
- }
- }
-
- /**
- * Permissions granted to a user or role.
- *
- * @param user the user
- * @param role the role
- * @param may the may
- */
- public record Grant(String user, String role, Set may) {
-
- /**
- * To string.
- *
- * @return the string
- */
- @Override
- public String toString() {
- StringBuilder builder = new StringBuilder();
- if (user != null) {
- builder.append("User ").append(user);
- } else {
- builder.append("Role ").append(role);
- }
- builder.append(" may=").append(may).append(']');
- return builder.toString();
- }
- }
-
- /**
- * The assignment information.
- *
- * @param pool the pool
- * @param user the user
- * @param lastUsed the last used
- */
- public record Assignment(String pool, String user, Instant lastUsed) {
- }
-
- /**
- * Instantiates a new vm definition.
- *
- * @param delegate the delegate
- * @param json the json
- */
- public VmDefinition(Gson delegate, JsonObject json) {
- super(delegate, json);
- model = gson.fromJson(json, Model.class);
- }
-
- /**
- * Gets the spec.
- *
- * @return the spec
- */
- public Map spec() {
- return model.getSpec();
- }
-
- /**
- * Get a value from the spec using {@link DataPath#get}.
- *
- * @param the generic type
- * @param selectors the selectors
- * @return the value, if found
- */
- public Optional fromSpec(Object... selectors) {
- return DataPath.get(spec(), selectors);
- }
-
- /**
- * The pools that this VM belongs to.
- *
- * @return the list
- */
- public List pools() {
- return this.> fromSpec("pools")
- .orElse(Collections.emptyList());
- }
-
- /**
- * Get a value from the `spec().get("vm")` using {@link DataPath#get}.
- *
- * @param the generic type
- * @param selectors the selectors
- * @return the value, if found
- */
- public Optional fromVm(Object... selectors) {
- return DataPath.get(spec(), "vm")
- .flatMap(vm -> DataPath.get(vm, selectors));
- }
-
- /**
- * Gets the status.
- *
- * @return the status
- */
- public Map status() {
- return model.getStatus();
- }
-
- /**
- * Get a value from the status using {@link DataPath#get}.
- *
- * @param the generic type
- * @param selectors the selectors
- * @return the value, if found
- */
- public Optional fromStatus(Object... selectors) {
- return DataPath.get(status(), selectors);
- }
-
- /**
- * The assignment information.
- *
- * @return the optional
- */
- public Optional assignment() {
- return this.