Compare commits
No commits in common. "main" and "org-jdrupes-vmoperator-manager-0.8.0" have entirely different histories.
main
...
org-jdrupe
375 changed files with 3242 additions and 45001 deletions
|
|
@ -1,14 +0,0 @@
|
||||||
root = true
|
|
||||||
|
|
||||||
[*]
|
|
||||||
charset = utf-8
|
|
||||||
insert_final_newline = true
|
|
||||||
trim_trailing_whitespace = true
|
|
||||||
|
|
||||||
[*.{html,md,yml,yaml}]
|
|
||||||
indent_size = 2
|
|
||||||
indent_style = space
|
|
||||||
|
|
||||||
[*.{gradle,js,ts}]
|
|
||||||
indent_size = 4
|
|
||||||
indent_style = space
|
|
||||||
|
|
@ -1,16 +0,0 @@
|
||||||
{
|
|
||||||
"root": true,
|
|
||||||
"rules": {
|
|
||||||
"no-unused-vars": "off",
|
|
||||||
"@typescript-eslint/no-unused-vars": [
|
|
||||||
"warn",
|
|
||||||
{
|
|
||||||
"argsIgnorePattern": "^_",
|
|
||||||
"varsIgnorePattern": "^_",
|
|
||||||
"caughtErrorsIgnorePattern": "^_"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"ignorePatterns": ["src/**/*.test.ts", "build/**/*"]
|
|
||||||
}
|
|
||||||
|
|
||||||
2
.github/workflows/codeql.yml
vendored
2
.github/workflows/codeql.yml
vendored
|
|
@ -2,7 +2,7 @@ name: "CodeQL"
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches: [ "*" ]
|
branches: [ "main" ]
|
||||||
pull_request:
|
pull_request:
|
||||||
# The branches below must be a subset of the branches above
|
# The branches below must be a subset of the branches above
|
||||||
branches: [ "main" ]
|
branches: [ "main" ]
|
||||||
|
|
|
||||||
12
.github/workflows/gradle.yml
vendored
12
.github/workflows/gradle.yml
vendored
|
|
@ -1,8 +1,10 @@
|
||||||
name: Java CI with Gradle
|
name: Java CI with Gradle
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push: {}
|
push:
|
||||||
pull_request: {}
|
branches: [ "main" ]
|
||||||
|
pull_request:
|
||||||
|
branches: [ "main" ]
|
||||||
|
|
||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
|
|
@ -22,10 +24,10 @@ jobs:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
- name: Install graphviz
|
- name: Install graphviz
|
||||||
run: sudo apt-get install graphviz
|
run: sudo apt-get install graphviz
|
||||||
- name: Set up JDK 21
|
- name: Set up JDK 17
|
||||||
uses: actions/setup-java@v3
|
uses: actions/setup-java@v3
|
||||||
with:
|
with:
|
||||||
java-version: '21'
|
java-version: '17'
|
||||||
distribution: 'temurin'
|
distribution: 'temurin'
|
||||||
- name: Build with Gradle
|
- name: Build with Gradle
|
||||||
run: ./gradlew -Pwebsite.push.token=${{ secrets.WEBSITE_PUSH_TOKEN }} stage
|
run: ./gradlew -Prepo.access.token=${{ secrets.REPO_ACCESS_TOKEN }} stage
|
||||||
|
|
|
||||||
89
.github/workflows/jekyll.yml
vendored
89
.github/workflows/jekyll.yml
vendored
|
|
@ -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
|
|
||||||
12
.github/workflows/release.yml
vendored
12
.github/workflows/release.yml
vendored
|
|
@ -8,17 +8,13 @@ permissions:
|
||||||
contents: read
|
contents: read
|
||||||
packages: write
|
packages: write
|
||||||
|
|
||||||
concurrency:
|
|
||||||
group: doc_generation
|
|
||||||
cancel-in-progress: false
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
|
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v3
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
- name: Install graphviz
|
- name: Install graphviz
|
||||||
|
|
@ -31,10 +27,10 @@ jobs:
|
||||||
registry: ghcr.io
|
registry: ghcr.io
|
||||||
username: ${{ github.actor }}
|
username: ${{ github.actor }}
|
||||||
password: ${{ secrets.GITHUB_TOKEN }}
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
- name: Set up JDK 21
|
- name: Set up JDK 17
|
||||||
uses: actions/setup-java@v3
|
uses: actions/setup-java@v3
|
||||||
with:
|
with:
|
||||||
java-version: '21'
|
java-version: '17'
|
||||||
distribution: 'temurin'
|
distribution: 'temurin'
|
||||||
- name: Push with Gradle
|
- name: Push with Gradle
|
||||||
run: ./gradlew -Pwebsite.push.token=${{ secrets.WEBSITE_PUSH_TOKEN }} -Pdocker.registry=ghcr.io/${{ github.actor }} stage publishImage
|
run: ./gradlew -Pdocker.registry=ghcr.io/${{ github.actor }} pushImages
|
||||||
|
|
|
||||||
4
.gitignore
vendored
4
.gitignore
vendored
|
|
@ -9,7 +9,3 @@ bin
|
||||||
.classpath
|
.classpath
|
||||||
.project
|
.project
|
||||||
org.eclipse.jdt.core.prefs
|
org.eclipse.jdt.core.prefs
|
||||||
**/.externalToolBuilders/
|
|
||||||
|
|
||||||
# Node
|
|
||||||
**/node_modules/
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
|
||||||
42
.project
Normal file
42
.project
Normal file
|
|
@ -0,0 +1,42 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<projectDescription>
|
||||||
|
<name>VM-Operator</name>
|
||||||
|
<comment></comment>
|
||||||
|
<projects/>
|
||||||
|
<natures>
|
||||||
|
<nature>org.eclipse.jdt.core.javanature</nature>
|
||||||
|
<nature>org.eclipse.buildship.core.gradleprojectnature</nature>
|
||||||
|
<nature>net.sf.eclipsecs.core.CheckstyleNature</nature>
|
||||||
|
<nature>ch.acanda.eclipse.pmd.builder.PMDNature</nature>
|
||||||
|
</natures>
|
||||||
|
<buildSpec>
|
||||||
|
<buildCommand>
|
||||||
|
<name>org.eclipse.jdt.core.javabuilder</name>
|
||||||
|
<arguments/>
|
||||||
|
</buildCommand>
|
||||||
|
<buildCommand>
|
||||||
|
<name>org.eclipse.buildship.core.gradleprojectbuilder</name>
|
||||||
|
<arguments/>
|
||||||
|
</buildCommand>
|
||||||
|
<buildCommand>
|
||||||
|
<name>net.sf.eclipsecs.core.CheckstyleBuilder</name>
|
||||||
|
<arguments/>
|
||||||
|
</buildCommand>
|
||||||
|
<buildCommand>
|
||||||
|
<name>ch.acanda.eclipse.pmd.builder.PMDBuilder</name>
|
||||||
|
<arguments/>
|
||||||
|
</buildCommand>
|
||||||
|
</buildSpec>
|
||||||
|
<linkedResources/>
|
||||||
|
<filteredResources>
|
||||||
|
<filter>
|
||||||
|
<id>1</id>
|
||||||
|
<type>30</type>
|
||||||
|
<name/>
|
||||||
|
<matcher>
|
||||||
|
<id>org.eclipse.core.resources.regexFilterMatcher</id>
|
||||||
|
<arguments>node_modules|\.git|__CREATED_BY_JAVA_LANGUAGE_SERVER__</arguments>
|
||||||
|
</matcher>
|
||||||
|
</filter>
|
||||||
|
</filteredResources>
|
||||||
|
</projectDescription>
|
||||||
|
|
@ -1,11 +1,11 @@
|
||||||
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
|
auto.sync=false
|
||||||
build.scans.enabled=false
|
build.scans.enabled=false
|
||||||
connection.gradle.distribution=GRADLE_DISTRIBUTION(WRAPPER)
|
connection.gradle.distribution=GRADLE_DISTRIBUTION(WRAPPER)
|
||||||
connection.project.dir=
|
connection.project.dir=
|
||||||
eclipse.preferences.version=1
|
eclipse.preferences.version=1
|
||||||
gradle.user.home=
|
gradle.user.home=
|
||||||
java.home=
|
java.home=/usr/lib/jvm/java-1.8.0-openjdk-1.8.0.362.b09-2.fc37.x86_64
|
||||||
jvm.arguments=
|
jvm.arguments=
|
||||||
offline.mode=false
|
offline.mode=false
|
||||||
override.workspace.settings=true
|
override.workspace.settings=true
|
||||||
|
|
|
||||||
|
|
@ -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
|
|
||||||
24
README.md
24
README.md
|
|
@ -1,25 +1,11 @@
|
||||||
[](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)
|
[](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
|
See the [project's home page](https://mnlipp.github.io/VM-Operator/)
|
||||||
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/)
|
|
||||||
for details.
|
for details.
|
||||||
|
|
||||||
|
|
|
||||||
BIN
VM-Operator.png
BIN
VM-Operator.png
Binary file not shown.
|
Before Width: | Height: | Size: 25 KiB |
17
build.gradle
17
build.gradle
|
|
@ -6,20 +6,16 @@ buildscript {
|
||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
id 'org.ajoberstar.grgit' version '5.2.0'
|
id 'org.ajoberstar.grgit' version '5.2.0'
|
||||||
id 'org.ajoberstar.git-publish' version '4.2.0' apply false
|
id 'org.ajoberstar.git-publish' version '4.2.0'
|
||||||
id 'pl.allegro.tech.build.axion-release' version '1.17.2' apply false
|
id 'pl.allegro.tech.build.axion-release' version '1.15.0' apply false
|
||||||
id 'org.jdrupes.vmoperator.versioning-conventions'
|
|
||||||
id 'org.jdrupes.vmoperator.java-doc-conventions'
|
id 'org.jdrupes.vmoperator.java-doc-conventions'
|
||||||
id 'eclipse'
|
id 'eclipse'
|
||||||
id "com.github.node-gradle.node" version "7.0.1"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
allprojects {
|
project.group = 'org.jdrupes.vmoperator'
|
||||||
project.group = 'org.jdrupes.vmoperator'
|
|
||||||
}
|
|
||||||
|
|
||||||
task stage {
|
task stage {
|
||||||
description = 'To be executed by CI.'
|
description = 'To be executed by CI, build and update JavaDoc.'
|
||||||
group = 'build'
|
group = 'build'
|
||||||
|
|
||||||
// Build everything first
|
// Build everything first
|
||||||
|
|
@ -27,6 +23,11 @@ task stage {
|
||||||
dependsOn subprojects.tasks.collect {
|
dependsOn subprojects.tasks.collect {
|
||||||
tc -> tc.findByName("build") }.flatten()
|
tc -> tc.findByName("build") }.flatten()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (JavaVersion.current() == JavaVersion.VERSION_17) {
|
||||||
|
// Publish JavaDoc
|
||||||
|
dependsOn gitPublishPush
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
eclipse {
|
eclipse {
|
||||||
|
|
|
||||||
11
buildSrc/.project
Normal file
11
buildSrc/.project
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<projectDescription>
|
||||||
|
<name>buildSrc</name>
|
||||||
|
<comment></comment>
|
||||||
|
<projects>
|
||||||
|
</projects>
|
||||||
|
<buildSpec>
|
||||||
|
</buildSpec>
|
||||||
|
<natures>
|
||||||
|
</natures>
|
||||||
|
</projectDescription>
|
||||||
|
|
@ -1,2 +0,0 @@
|
||||||
eclipse.preferences.version=1
|
|
||||||
encoding/<project>=UTF-8
|
|
||||||
|
|
@ -1,2 +0,0 @@
|
||||||
eclipse.preferences.version=1
|
|
||||||
line.separator=\n
|
|
||||||
|
|
@ -1,13 +1,24 @@
|
||||||
#
|
#
|
||||||
#Wed Oct 02 14:48:43 CEST 2024
|
#Sun May 07 20:03:15 CEST 2023
|
||||||
eclipse.preferences.version=1
|
org.eclipse.jdt.core.compiler.codegen.methodParameters=do not generate
|
||||||
org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
|
|
||||||
org.eclipse.jdt.core.compiler.codegen.targetPlatform=21
|
|
||||||
org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
|
|
||||||
org.eclipse.jdt.core.compiler.compliance=21
|
|
||||||
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.assertIdentifier=error
|
||||||
|
org.eclipse.jdt.core.compiler.problem.nullReference=warning
|
||||||
|
org.eclipse.jdt.core.compiler.debug.localVariable=generate
|
||||||
|
org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
|
||||||
|
eclipse.preferences.version=1
|
||||||
|
org.eclipse.jdt.core.compiler.problem.enablePreviewFeatures=disabled
|
||||||
org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
|
org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
|
||||||
org.eclipse.jdt.core.compiler.source=21
|
org.eclipse.jdt.core.compiler.annotation.nullable=org.eclipse.jdt.annotation.Nullable
|
||||||
|
org.eclipse.jdt.core.compiler.debug.sourceFile=generate
|
||||||
|
org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
|
||||||
|
org.eclipse.jdt.core.compiler.problem.nullAnnotationInferenceConflict=error
|
||||||
|
org.eclipse.jdt.core.compiler.codegen.targetPlatform=17
|
||||||
|
org.eclipse.jdt.core.compiler.problem.nullSpecViolation=error
|
||||||
|
org.eclipse.jdt.core.compiler.problem.potentialNullReference=ignore
|
||||||
|
org.eclipse.jdt.core.compiler.source=17
|
||||||
|
org.eclipse.jdt.core.compiler.annotation.nonnull=org.eclipse.jdt.annotation.NonNull
|
||||||
|
org.eclipse.jdt.core.compiler.release=disabled
|
||||||
|
org.eclipse.jdt.core.compiler.problem.reportPreviewFeatures=warning
|
||||||
|
org.eclipse.jdt.core.compiler.annotation.nullanalysis=disabled
|
||||||
|
org.eclipse.jdt.core.compiler.debug.lineNumber=generate
|
||||||
|
org.eclipse.jdt.core.compiler.compliance=17
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
eclipse.preferences.version=1
|
eclipse.preferences.version=1
|
||||||
groovy.compiler.level=-1
|
groovy.compiler.level=40
|
||||||
groovy.script.filters=**/*.dsld,y,**/*.gradle,n
|
groovy.script.filters=**/*.dsld,y,**/*.gradle,n
|
||||||
|
|
|
||||||
|
|
@ -1,34 +1,39 @@
|
||||||
|
/*
|
||||||
|
* This file was generated by the Gradle 'init' task.
|
||||||
|
*
|
||||||
|
* This project uses @Incubating APIs which are subject to change.
|
||||||
|
*/
|
||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
// Support convention plugins written in Groovy. Convention plugins
|
// Support convention plugins written in Groovy. Convention plugins
|
||||||
// are build scripts in 'src/main' that automatically become available
|
// are build scripts in 'src/main' that automatically become available
|
||||||
// as plugins in the main build.
|
// as plugins in the main build.
|
||||||
id 'groovy-gradle-plugin'
|
id 'groovy-gradle-plugin'
|
||||||
|
}
|
||||||
|
|
||||||
// Apply eclipse plugin
|
repositories {
|
||||||
id 'eclipse'
|
// Use the plugin portal to apply community plugins in convention plugins.
|
||||||
|
gradlePluginPortal()
|
||||||
}
|
}
|
||||||
|
|
||||||
sourceSets {
|
sourceSets {
|
||||||
main {
|
main {
|
||||||
groovy {
|
groovy {
|
||||||
srcDirs = ['src']
|
srcDirs = ['src']
|
||||||
}
|
}
|
||||||
resources {
|
}
|
||||||
srcDirs = ['resources']
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
eclipse {
|
test {
|
||||||
|
groovy {
|
||||||
jdt {
|
srcDirs = ['test']
|
||||||
file {
|
|
||||||
withProperties { properties ->
|
|
||||||
def formatterPrefs = new Properties()
|
|
||||||
rootProject.file("../gradle/org.eclipse.jdt.core.formatter.prefs")
|
|
||||||
.withInputStream { formatterPrefs.load(it) }
|
|
||||||
properties.putAll(formatterPrefs)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
java {
|
||||||
|
toolchain {
|
||||||
|
languageVersion = JavaLanguageVersion.of(17)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
|
||||||
7
buildSrc/settings.gradle
Normal file
7
buildSrc/settings.gradle
Normal file
|
|
@ -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'
|
||||||
|
|
@ -5,14 +5,12 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
plugins {
|
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.
|
// Apply the java Plugin to add support for Java.
|
||||||
id 'java'
|
id 'java'
|
||||||
|
|
||||||
|
// Git based versioning
|
||||||
|
id 'pl.allegro.tech.build.axion-release'
|
||||||
|
|
||||||
// Apply eclipse plugin
|
// Apply eclipse plugin
|
||||||
id 'eclipse'
|
id 'eclipse'
|
||||||
|
|
||||||
|
|
@ -23,6 +21,7 @@ plugins {
|
||||||
repositories {
|
repositories {
|
||||||
// Use Maven Central for resolving dependencies.
|
// Use Maven Central for resolving dependencies.
|
||||||
mavenCentral()
|
mavenCentral()
|
||||||
|
mavenLocal()
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
|
|
@ -55,25 +54,32 @@ sourceSets {
|
||||||
|
|
||||||
java {
|
java {
|
||||||
toolchain {
|
toolchain {
|
||||||
languageVersion = JavaLanguageVersion.of(21)
|
languageVersion = JavaLanguageVersion.of(17)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
scmVersion {
|
||||||
|
versionIncrementer 'incrementMinor'
|
||||||
|
tag {
|
||||||
|
def shortened = project.name.startsWith(project.group + ".") ?
|
||||||
|
project.name.substring(project.group.length() + 1) : project.name
|
||||||
|
prefix = shortened.replace('.', '-') + "-"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
version = scmVersion.version
|
||||||
|
ext.isSnapshot = version.endsWith('-SNAPSHOT')
|
||||||
|
|
||||||
jar {
|
jar {
|
||||||
manifest {
|
manifest {
|
||||||
def matchExpr = [ project.tagName + "*" ]
|
inputs.property("gitDescriptor", { grgit.describe(always: true) })
|
||||||
|
|
||||||
inputs.property("gitDescriptor",
|
|
||||||
{ grgit.describe(always: true, match: matchExpr) })
|
|
||||||
|
|
||||||
// Set Git revision information in the manifests of built bundles
|
// Set Git revision information in the manifests of built bundles
|
||||||
def gitDesc = grgit.describe(always: true, match: matchExpr)
|
|
||||||
attributes([
|
attributes([
|
||||||
"Implementation-Title": project.name,
|
"Implementation-Title": project.name,
|
||||||
"Implementation-Version": "$project.version (built from ${gitDesc})",
|
"Implementation-Version": "$project.version (${grgit.describe(always: true)})",
|
||||||
"Implementation-Vendor": grgit.repository.jgit.repository.config.getString("user", null, "name")
|
"Implementation-Vendor": grgit.repository.jgit.repository.config.getString("user", null, "name")
|
||||||
+ " (" + grgit.repository.jgit.repository.config.getString("user", null, "email") + ")",
|
+ " (" + grgit.repository.jgit.repository.config.getString("user", null, "email") + ")",
|
||||||
"Git-Descriptor": gitDesc,
|
"Git-Descriptor": grgit.describe(always: true),
|
||||||
"Git-SHA": grgit.head().id,
|
"Git-SHA": grgit.head().id,
|
||||||
])
|
])
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,10 @@
|
||||||
plugins {
|
plugins {
|
||||||
// Apply the common versioning conventions.
|
// Apply the common convention plugin for shared build configuration between library and application projects.
|
||||||
id 'org.jdrupes.vmoperator.versioning-conventions'
|
id 'org.jdrupes.vmoperator.java-common-conventions'
|
||||||
|
|
||||||
id 'org.ajoberstar.git-publish'
|
id 'org.ajoberstar.git-publish'
|
||||||
}
|
}
|
||||||
|
|
||||||
repositories {
|
|
||||||
// Use Maven Central for resolving dependencies.
|
|
||||||
mavenCentral()
|
|
||||||
}
|
|
||||||
|
|
||||||
var docDestinationDir = file("${rootProject.buildDir}/javadoc")
|
var docDestinationDir = file("${rootProject.buildDir}/javadoc")
|
||||||
|
|
||||||
configurations {
|
configurations {
|
||||||
|
|
@ -22,28 +17,31 @@ configurations {
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
markdownDoclet "org.jdrupes.mdoclet:doclet:4.0.0"
|
markdownDoclet "org.jdrupes.mdoclet:doclet:3.1.0"
|
||||||
javadocTaglets "org.jdrupes.taglets:plantuml-taglet:3.0.0"
|
javadocTaglets "org.jdrupes.taglets:plantuml-taglet:2.1.0"
|
||||||
}
|
}
|
||||||
|
|
||||||
task apidocs (type: JavaExec) {
|
task javadocResources(type: Copy) {
|
||||||
|
into file(docDestinationDir)
|
||||||
|
from ("${rootProject.rootDir}/misc") {
|
||||||
|
include '*.woff2'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
task java11doc (type: JavaExec) {
|
||||||
// Does not work on JitPack, no /usr/bin/dot
|
// 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)
|
outputs.dir(docDestinationDir)
|
||||||
|
|
||||||
inputs.file rootProject.file('overview.md')
|
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',
|
jvmArgs = ['--add-exports=jdk.javadoc/jdk.javadoc.internal.tool=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.tree=ALL-UNNAMED',
|
main = 'jdk.javadoc.internal.tool.Main'
|
||||||
'--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'
|
|
||||||
|
|
||||||
gradle.projectsEvaluated {
|
gradle.projectsEvaluated {
|
||||||
// Make sure that other projects' compileClasspaths are resolved
|
// Make sure that other projects' compileClasspaths are resolved
|
||||||
|
|
@ -66,8 +64,8 @@ task apidocs (type: JavaExec) {
|
||||||
'-package',
|
'-package',
|
||||||
'-use',
|
'-use',
|
||||||
'-linksource',
|
'-linksource',
|
||||||
'-link', 'https://docs.oracle.com/en/java/javase/21/docs/api/',
|
'-link', 'https://docs.oracle.com/en/java/javase/17/docs/api/',
|
||||||
'-link', 'https://jgrapes.org/latest-release/javadoc/',
|
'-link', 'https://mnlipp.github.io/jgrapes/latest-release/javadoc/',
|
||||||
'-link', 'https://freemarker.apache.org/docs/api/',
|
'-link', 'https://freemarker.apache.org/docs/api/',
|
||||||
'--add-exports', 'jdk.javadoc/jdk.javadoc.internal.tool=ALL-UNNAMED',
|
'--add-exports', 'jdk.javadoc/jdk.javadoc.internal.tool=ALL-UNNAMED',
|
||||||
'--add-exports', 'jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED',
|
'--add-exports', 'jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED',
|
||||||
|
|
@ -85,7 +83,7 @@ task apidocs (type: JavaExec) {
|
||||||
'-bottom', rootProject.file("misc/javadoc.bottom.txt").text,
|
'-bottom', rootProject.file("misc/javadoc.bottom.txt").text,
|
||||||
'--allow-script-in-comments',
|
'--allow-script-in-comments',
|
||||||
'-Xdoclint:-html',
|
'-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',
|
'--add-exports=jdk.javadoc/jdk.javadoc.internal.doclets.formats.html=ALL-UNNAMED',
|
||||||
'-quiet'
|
'-quiet'
|
||||||
]
|
]
|
||||||
|
|
@ -94,27 +92,34 @@ task apidocs (type: JavaExec) {
|
||||||
ignoreExitValue true
|
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
|
// Prepare github authentication for plugins
|
||||||
if (System.properties['org.ajoberstar.grgit.auth.username'] == null) {
|
if (System.properties['org.ajoberstar.grgit.auth.username'] == null) {
|
||||||
System.setProperty('org.ajoberstar.grgit.auth.username',
|
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 java11doc
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,36 +0,0 @@
|
||||||
/*
|
|
||||||
* This file was generated by the Gradle 'init' task.
|
|
||||||
*
|
|
||||||
* This project uses @Incubating APIs which are subject to change.
|
|
||||||
*/
|
|
||||||
|
|
||||||
plugins {
|
|
||||||
// Required by axion-release
|
|
||||||
id 'org.ajoberstar.grgit'
|
|
||||||
// Git based versioning
|
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
project.version = scmVersion.version
|
|
||||||
ext.isSnapshot = version.endsWith('-SNAPSHOT')
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<!--
|
<!--
|
||||||
JGrapes Event Driven Framework
|
Moodle Tools Console
|
||||||
Copyright (C) 2016, 2018 Michael N. Lipp
|
Copyright (C) 2022 Michael N. Lipp
|
||||||
|
|
||||||
This program is free software; you can redistribute it and/or modify it
|
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
|
under the terms of the GNU Affero General Public License as published by
|
||||||
|
|
@ -30,11 +30,8 @@
|
||||||
<property name="severity" value="warning"/>
|
<property name="severity" value="warning"/>
|
||||||
<property name="charset" value="UTF-8"/>
|
<property name="charset" value="UTF-8"/>
|
||||||
<property name="fileExtensions" value="java, properties, xml"/>
|
<property name="fileExtensions" value="java, properties, xml"/>
|
||||||
<module name="SuppressWarningsFilter"/>
|
|
||||||
<module name="TreeWalker">
|
<module name="TreeWalker">
|
||||||
<property name="tabWidth" value="4"/>
|
<property name="tabWidth" value="4"/>
|
||||||
|
|
||||||
<module name="SuppressWarningsHolder"/>
|
|
||||||
<module name="OuterTypeFilename"/>
|
<module name="OuterTypeFilename"/>
|
||||||
<module name="IllegalTokenText">
|
<module name="IllegalTokenText">
|
||||||
<property name="tokens" value="STRING_LITERAL, CHAR_LITERAL"/>
|
<property name="tokens" value="STRING_LITERAL, CHAR_LITERAL"/>
|
||||||
|
|
@ -53,6 +50,7 @@
|
||||||
<property name="tokens" value="LITERAL_TRY, LITERAL_FINALLY, LITERAL_IF, LITERAL_ELSE, LITERAL_SWITCH"/>
|
<property name="tokens" value="LITERAL_TRY, LITERAL_FINALLY, LITERAL_IF, LITERAL_ELSE, LITERAL_SWITCH"/>
|
||||||
</module>
|
</module>
|
||||||
<module name="NeedBraces"/>
|
<module name="NeedBraces"/>
|
||||||
|
<module name="RightCurly"/>
|
||||||
<module name="RightCurly">
|
<module name="RightCurly">
|
||||||
<property name="option" value="alone"/>
|
<property name="option" value="alone"/>
|
||||||
<property name="tokens" value="CLASS_DEF, METHOD_DEF, CTOR_DEF, LITERAL_FOR, LITERAL_WHILE, STATIC_INIT, INSTANCE_INIT"/>
|
<property name="tokens" value="CLASS_DEF, METHOD_DEF, CTOR_DEF, LITERAL_FOR, LITERAL_WHILE, STATIC_INIT, INSTANCE_INIT"/>
|
||||||
|
|
@ -106,7 +104,7 @@
|
||||||
<module name="NoFinalizer"/>
|
<module name="NoFinalizer"/>
|
||||||
<module name="AbbreviationAsWordInName">
|
<module name="AbbreviationAsWordInName">
|
||||||
<property name="allowedAbbreviationLength" value="1"/>
|
<property name="allowedAbbreviationLength" value="1"/>
|
||||||
<property name="allowedAbbreviations" value="IO,MX,KV,DTO"/>
|
<property name="allowedAbbreviations" value="IO,MX,KV"/>
|
||||||
<property name="ignoreFinal" value="false"/>
|
<property name="ignoreFinal" value="false"/>
|
||||||
</module>
|
</module>
|
||||||
<module name="OverloadMethodsDeclarationOrder"/>
|
<module name="OverloadMethodsDeclarationOrder"/>
|
||||||
|
|
|
||||||
|
|
@ -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/<group>/<version>/<plural>
|
|
||||||
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
|
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -8,7 +8,3 @@ resources:
|
||||||
- vmop-image-repository-pvc.yaml
|
- vmop-image-repository-pvc.yaml
|
||||||
- vmop-config-map.yaml
|
- vmop-config-map.yaml
|
||||||
- vmop-deployment.yaml
|
- vmop-deployment.yaml
|
||||||
- vmop-service.yaml
|
|
||||||
- vmrunner-role.yaml
|
|
||||||
- vmrunner-service-account.yaml
|
|
||||||
- vmrunner-role-binding.yaml
|
|
||||||
|
|
|
||||||
|
|
@ -10,15 +10,13 @@ data:
|
||||||
"/Manager": {}
|
"/Manager": {}
|
||||||
|
|
||||||
logging.properties: |
|
logging.properties: |
|
||||||
handlers=java.util.logging.ConsoleHandler, \
|
handlers=java.util.logging.ConsoleHandler
|
||||||
org.jgrapes.webconlet.logviewer.LogViewerHandler
|
|
||||||
|
|
||||||
org.jgrapes.level=FINE
|
org.jgrapes.level=FINE
|
||||||
|
org.jgrapes.core.handlerTracking.level=FINER
|
||||||
|
|
||||||
org.jdrupes.vmoperator.manager.level=FINE
|
org.jdrupes.vmoperator.manager.level=FINE
|
||||||
|
|
||||||
java.util.logging.ConsoleHandler.level=ALL
|
java.util.logging.ConsoleHandler.level=ALL
|
||||||
java.util.logging.ConsoleHandler.formatter=java.util.logging.SimpleFormatter
|
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
|
java.util.logging.SimpleFormatter.format=%1$tb %1$td %1$tT %4$s %5$s%6$s%n
|
||||||
|
|
||||||
org.jgrapes.webconlet.logviewer.LogViewerHandler.level=CONFIG
|
|
||||||
|
|
||||||
|
|
@ -21,25 +21,12 @@ spec:
|
||||||
- name: vm-operator
|
- name: vm-operator
|
||||||
image: >-
|
image: >-
|
||||||
ghcr.io/mnlipp/org.jdrupes.vmoperator.manager:latest
|
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:
|
volumeMounts:
|
||||||
- name: config
|
- name: config
|
||||||
mountPath: /etc/opt/vmoperator
|
mountPath: /etc/opt/vmoperator
|
||||||
- name: vmop-image-repository
|
- name: vmop-image-repository
|
||||||
mountPath: /var/local/vmop-image-repository
|
mountPath: /var/local/vmop-image-repository
|
||||||
|
imagePullPolicy: Always
|
||||||
securityContext:
|
securityContext:
|
||||||
capabilities:
|
capabilities:
|
||||||
drop:
|
drop:
|
||||||
|
|
@ -56,6 +43,7 @@ spec:
|
||||||
nodeSelector:
|
nodeSelector:
|
||||||
kubernetes.io/os: linux
|
kubernetes.io/os: linux
|
||||||
serviceAccountName: vm-operator
|
serviceAccountName: vm-operator
|
||||||
|
serviceAccount: vm-operator
|
||||||
securityContext:
|
securityContext:
|
||||||
runAsUser: 65534
|
runAsUser: 65534
|
||||||
runAsNonRoot: true
|
runAsNonRoot: true
|
||||||
|
|
|
||||||
|
|
@ -9,15 +9,8 @@ rules:
|
||||||
- vmoperator.jdrupes.org
|
- vmoperator.jdrupes.org
|
||||||
resources:
|
resources:
|
||||||
- vms
|
- vms
|
||||||
- vmpools
|
|
||||||
verbs:
|
verbs:
|
||||||
- '*'
|
- '*'
|
||||||
- apiGroups:
|
|
||||||
- vmoperator.jdrupes.org
|
|
||||||
resources:
|
|
||||||
- vms/status
|
|
||||||
verbs:
|
|
||||||
- patch
|
|
||||||
- apiGroups:
|
- apiGroups:
|
||||||
- apps
|
- apps
|
||||||
resources:
|
resources:
|
||||||
|
|
@ -35,12 +28,8 @@ rules:
|
||||||
- apiGroups:
|
- apiGroups:
|
||||||
- ""
|
- ""
|
||||||
resources:
|
resources:
|
||||||
- persistentvolumeclaims
|
|
||||||
- pods
|
- pods
|
||||||
verbs:
|
verbs:
|
||||||
- watch
|
|
||||||
- list
|
- list
|
||||||
- get
|
|
||||||
- create
|
|
||||||
- delete
|
- delete
|
||||||
- patch
|
- patch
|
||||||
|
|
|
||||||
|
|
@ -1,12 +0,0 @@
|
||||||
apiVersion: v1
|
|
||||||
kind: Service
|
|
||||||
metadata:
|
|
||||||
name: vm-operator
|
|
||||||
spec:
|
|
||||||
ports:
|
|
||||||
- port: 8080
|
|
||||||
protocol: TCP
|
|
||||||
targetPort: 8080
|
|
||||||
selector:
|
|
||||||
app.kubernetes.io/name: vm-operator
|
|
||||||
app.kubernetes.io/component: manager
|
|
||||||
|
|
@ -1,13 +0,0 @@
|
||||||
apiVersion: rbac.authorization.k8s.io/v1
|
|
||||||
kind: RoleBinding
|
|
||||||
metadata:
|
|
||||||
name: vm-runner
|
|
||||||
labels:
|
|
||||||
app.kubernetes.io/name: vm-operator
|
|
||||||
roleRef:
|
|
||||||
apiGroup: rbac.authorization.k8s.io
|
|
||||||
kind: Role
|
|
||||||
name: vm-runner
|
|
||||||
subjects:
|
|
||||||
- kind: ServiceAccount
|
|
||||||
name: vm-runner
|
|
||||||
|
|
@ -1,27 +0,0 @@
|
||||||
apiVersion: rbac.authorization.k8s.io/v1
|
|
||||||
kind: Role
|
|
||||||
metadata:
|
|
||||||
name: vm-runner
|
|
||||||
labels:
|
|
||||||
app.kubernetes.io/name: vm-operator
|
|
||||||
rules:
|
|
||||||
- apiGroups:
|
|
||||||
- vmoperator.jdrupes.org
|
|
||||||
resources:
|
|
||||||
- vms
|
|
||||||
verbs:
|
|
||||||
- list
|
|
||||||
- get
|
|
||||||
- patch
|
|
||||||
- apiGroups:
|
|
||||||
- vmoperator.jdrupes.org
|
|
||||||
resources:
|
|
||||||
- vms/status
|
|
||||||
verbs:
|
|
||||||
- patch
|
|
||||||
- apiGroups:
|
|
||||||
- events.k8s.io
|
|
||||||
resources:
|
|
||||||
- events
|
|
||||||
verbs:
|
|
||||||
- create
|
|
||||||
|
|
@ -1,6 +0,0 @@
|
||||||
kind: ServiceAccount
|
|
||||||
apiVersion: v1
|
|
||||||
metadata:
|
|
||||||
name: vm-runner
|
|
||||||
labels:
|
|
||||||
app.kubernetes.io/name: vm-operator
|
|
||||||
4
dev-example/.gitignore
vendored
4
dev-example/.gitignore
vendored
|
|
@ -1,4 +0,0 @@
|
||||||
/test-vm-ci.yaml
|
|
||||||
/kubeconfig.yaml
|
|
||||||
/crds/
|
|
||||||
/.vm-operator-cmd.rc
|
|
||||||
|
|
@ -3,17 +3,13 @@
|
||||||
The CRD must be deployed independently. Apart from that, the
|
The CRD must be deployed independently. Apart from that, the
|
||||||
`kustomize.yaml`
|
`kustomize.yaml`
|
||||||
|
|
||||||
* creates a small cdrom image repository and
|
* creates a small image repository and
|
||||||
|
|
||||||
* deploys the operator in namespace `vmop-dev` with a replica of 0.
|
* deploys the operator in namespace `vmop-dev` with a replica of 0.
|
||||||
|
|
||||||
This allows you to run the manager in your IDE.
|
This allows you to run the manager in your IDE.
|
||||||
|
|
||||||
The `kustomize.yaml` also changes the container image repository for
|
The `kustomize.yaml` also changes the image repository for the
|
||||||
the operator to a private repository for development. You have to
|
operator to a private repository for development. You have to
|
||||||
adapt this to your own repository if you also want to test your
|
either remove this or adapt it to your own repository if you
|
||||||
development version in a container.
|
also want to test your development version in a container.
|
||||||
|
|
||||||
If you want to run the unittests, this setup *must* be used with a private
|
|
||||||
container image repository which must match the one configured
|
|
||||||
for gradle pushImages.
|
|
||||||
|
|
|
||||||
|
|
@ -1,90 +1,7 @@
|
||||||
# Used for running manager outside Kubernetes.
|
# The values in comments are the defaults.
|
||||||
# Keep in sync with kustomize.yaml
|
|
||||||
"/Manager":
|
"/Manager":
|
||||||
# If provided, is shown at top left before namespace
|
|
||||||
# clusterName: "test"
|
|
||||||
# The controller manages the VM
|
|
||||||
"/Controller":
|
"/Controller":
|
||||||
namespace: vmop-dev
|
namespace: vmop-dev
|
||||||
"/Reconciler":
|
runnerData:
|
||||||
runnerDataPvc:
|
storageClassName: null
|
||||||
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
|
|
||||||
"/GuiSocketServer":
|
|
||||||
port: 8888
|
|
||||||
"/GuiHttpServer":
|
|
||||||
# This configures the GUI
|
|
||||||
"/ConsoleWeblet":
|
|
||||||
"/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"
|
|
||||||
"/RoleConfigurator":
|
|
||||||
rolesByUser:
|
|
||||||
# User admin has role admin
|
|
||||||
admin:
|
|
||||||
- admin
|
|
||||||
operator:
|
|
||||||
- operator
|
|
||||||
test1:
|
|
||||||
- user
|
|
||||||
test2:
|
|
||||||
- user
|
|
||||||
test3:
|
|
||||||
- user
|
|
||||||
# All users have role other
|
|
||||||
"*":
|
|
||||||
- other
|
|
||||||
replace: false
|
|
||||||
"/RoleConletFilter":
|
|
||||||
conletTypesByRole:
|
|
||||||
# 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
|
|
||||||
|
|
|
||||||
|
|
@ -1,47 +0,0 @@
|
||||||
#!/bin/bash
|
|
||||||
|
|
||||||
function usage() {
|
|
||||||
cat >&2 <<EOF
|
|
||||||
Usage: $0 [OPTION]... [TEMPLATE]
|
|
||||||
Generate VM CRDs using TEMPLATE.
|
|
||||||
|
|
||||||
-c, --count Count of VMs to generate
|
|
||||||
-d, --destination DIR Generate into given directory (default: ".")
|
|
||||||
-h, --help Print this help
|
|
||||||
-p, --prefix PREFIX Prefix for generated file (default: basename of template)
|
|
||||||
EOF
|
|
||||||
exit 1
|
|
||||||
}
|
|
||||||
|
|
||||||
count=0
|
|
||||||
destination=.
|
|
||||||
template=""
|
|
||||||
prefix=""
|
|
||||||
|
|
||||||
while [ "$#" -gt 0 ]; do
|
|
||||||
case "$1" in
|
|
||||||
-c|--count) shift; count=$1;;
|
|
||||||
-d|--destination) shift; destination="$1";;
|
|
||||||
-h|--help) shift; usage;;
|
|
||||||
-p|--prefix) shift; prefix="$1";;
|
|
||||||
-*) echo >&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
|
|
||||||
|
|
@ -20,80 +20,6 @@ patches:
|
||||||
storage: 10Gi
|
storage: 10Gi
|
||||||
storageClassName: local-path
|
storageClassName: local-path
|
||||||
|
|
||||||
- patch: |-
|
|
||||||
kind: ConfigMap
|
|
||||||
apiVersion: v1
|
|
||||||
metadata:
|
|
||||||
name: vm-operator
|
|
||||||
data:
|
|
||||||
# Keep in sync with config.yaml
|
|
||||||
config.yaml: |
|
|
||||||
"/Manager":
|
|
||||||
# clusterName: "test"
|
|
||||||
"/Controller":
|
|
||||||
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":
|
|
||||||
# This configures the GUI
|
|
||||||
"/ConsoleWeblet":
|
|
||||||
"/WebConsole":
|
|
||||||
"/LoginConlet":
|
|
||||||
users:
|
|
||||||
- name: admin
|
|
||||||
fullName: Administrator
|
|
||||||
password: "$2b$05$NiBd74ZGdplLC63ePZf1f.UtjMKkbQ23cQoO2OKOFalDBHWAOy21."
|
|
||||||
- name: test1
|
|
||||||
fullName: Test Account
|
|
||||||
password: "$2b$05$hZaI/jToXf/d3BctZdT38Or7H7h6Pn2W3WiB49p5AyhDHFkkYCvo2"
|
|
||||||
- name: test2
|
|
||||||
fullName: Test Account
|
|
||||||
password: "$2b$05$hZaI/jToXf/d3BctZdT38Or7H7h6Pn2W3WiB49p5AyhDHFkkYCvo2"
|
|
||||||
- name: test3
|
|
||||||
fullName: Test Account
|
|
||||||
password: "$2b$05$hZaI/jToXf/d3BctZdT38Or7H7h6Pn2W3WiB49p5AyhDHFkkYCvo2"
|
|
||||||
"/RoleConfigurator":
|
|
||||||
rolesByUser:
|
|
||||||
# User admin has role admin
|
|
||||||
admin:
|
|
||||||
- admin
|
|
||||||
test1:
|
|
||||||
- user
|
|
||||||
test2:
|
|
||||||
- user
|
|
||||||
test3:
|
|
||||||
- user
|
|
||||||
# All users have role other
|
|
||||||
"*":
|
|
||||||
- other
|
|
||||||
replace: false
|
|
||||||
"/RoleConletFilter":
|
|
||||||
conletTypesByRole:
|
|
||||||
# Admins can use all conlets
|
|
||||||
admin:
|
|
||||||
- "*"
|
|
||||||
user:
|
|
||||||
- org.jdrupes.vmoperator.vmviewer.VmViewer
|
|
||||||
# Others cannot use any conlet (except login conlet to log out)
|
|
||||||
other:
|
|
||||||
- org.jgrapes.webconlet.locallogin.LoginConlet
|
|
||||||
"/ComponentCollector":
|
|
||||||
"/VmAccess":
|
|
||||||
displayResource:
|
|
||||||
preferredIpVersion: ipv4
|
|
||||||
syncPreviewsFor:
|
|
||||||
- role: user
|
|
||||||
- target:
|
- target:
|
||||||
group: apps
|
group: apps
|
||||||
version: v1
|
version: v1
|
||||||
|
|
@ -102,10 +28,7 @@ patches:
|
||||||
patch: |-
|
patch: |-
|
||||||
- op: replace
|
- op: replace
|
||||||
path: /spec/template/spec/containers/0/image
|
path: /spec/template/spec/containers/0/image
|
||||||
value: docker-registry.lan.mnl.de/vmoperator/org.jdrupes.vmoperator.manager:test
|
value: docker-registry.lan.mnl.de/vmoperator/org.jdrupes.vmoperator.manager:latest
|
||||||
- op: replace
|
|
||||||
path: /spec/template/spec/containers/0/imagePullPolicy
|
|
||||||
value: Always
|
|
||||||
- op: replace
|
- op: replace
|
||||||
path: /spec/replicas
|
path: /spec/replicas
|
||||||
value: 0
|
value: 0
|
||||||
|
|
|
||||||
|
|
@ -1,66 +0,0 @@
|
||||||
#!/bin/bash
|
|
||||||
|
|
||||||
function usage() {
|
|
||||||
cat >&2 <<EOF
|
|
||||||
Usage: $0 pool-name action
|
|
||||||
Applys action to all VMs in the pool.
|
|
||||||
|
|
||||||
--context Context to be passed to kubectl (required)
|
|
||||||
-n, --namespace Namespace to be passed to kubectl
|
|
||||||
|
|
||||||
Action is one of "start", "stop", "delete" or "delete-disks"
|
|
||||||
|
|
||||||
Defaults for context and namespace are read from .vm-operator-cmd.rc.
|
|
||||||
EOF
|
|
||||||
exit 1
|
|
||||||
}
|
|
||||||
|
|
||||||
unset pool
|
|
||||||
unset action
|
|
||||||
unset context
|
|
||||||
namespace=default
|
|
||||||
|
|
||||||
if [ -r .vm-operator-cmd.rc ]; then
|
|
||||||
. .vm-operator-cmd.rc
|
|
||||||
fi
|
|
||||||
|
|
||||||
while [ "$#" -gt 0 ]; do
|
|
||||||
case "$1" in
|
|
||||||
--context) shift; context="$1";;
|
|
||||||
--context=*) IFS='=' read -r option value <<< "$1"; context="$value";;
|
|
||||||
-n|--namespace) shift; namespace="$1";;
|
|
||||||
-*) echo >&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
|
|
||||||
|
|
@ -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
|
|
||||||
|
|
@ -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
|
|
||||||
|
|
@ -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
|
|
||||||
|
|
@ -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
|
|
||||||
|
|
@ -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)) %>
|
|
||||||
|
|
@ -5,36 +5,21 @@ metadata:
|
||||||
name: test-vm
|
name: test-vm
|
||||||
spec:
|
spec:
|
||||||
image:
|
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-arch
|
||||||
pullPolicy: Always
|
pullPolicy: Always
|
||||||
|
|
||||||
permissions:
|
|
||||||
- user: admin
|
|
||||||
may:
|
|
||||||
- "*"
|
|
||||||
|
|
||||||
resources:
|
|
||||||
requests:
|
|
||||||
cpu: 1
|
|
||||||
memory: 2Gi
|
|
||||||
|
|
||||||
guestShutdownStops: true
|
|
||||||
|
|
||||||
cloudInit: {}
|
|
||||||
|
|
||||||
vm:
|
vm:
|
||||||
# state: Running
|
state: Running
|
||||||
bootMenu: yes
|
bootMenu: yes
|
||||||
useTpm: true
|
maximumRam: "8 GiB"
|
||||||
maximumRam: 8Gi
|
currentRam: "4 GiB"
|
||||||
currentRam: 4Gi
|
|
||||||
maximumCpus: 4
|
maximumCpus: 4
|
||||||
currentCpus: 4
|
currentCpus: 4
|
||||||
|
|
||||||
networks:
|
networks:
|
||||||
# No bridge on test cluster
|
- tap:
|
||||||
- user: {}
|
mac: "00:16:3e:33:59:10"
|
||||||
|
|
||||||
disks:
|
disks:
|
||||||
- volumeClaimTemplate:
|
- volumeClaimTemplate:
|
||||||
metadata:
|
metadata:
|
||||||
|
|
@ -57,6 +42,3 @@ spec:
|
||||||
display:
|
display:
|
||||||
spice:
|
spice:
|
||||||
port: 5810
|
port: 5810
|
||||||
generateSecret: true
|
|
||||||
|
|
||||||
loadBalancerService: {}
|
|
||||||
|
|
|
||||||
|
|
@ -1,2 +0,0 @@
|
||||||
SUBSYSTEM=="virtio-ports", ATTR{name}=="org.jdrupes.vmop_agent.0", \
|
|
||||||
TAG+="systemd" ENV{SYSTEMD_WANTS}="vmop-agent.service"
|
|
||||||
|
|
@ -1,3 +0,0 @@
|
||||||
#!/bin/sh
|
|
||||||
|
|
||||||
sed -i '/AutomaticLogin/d' /etc/gdm/custom.conf
|
|
||||||
|
|
@ -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
|
|
||||||
|
|
@ -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
|
|
||||||
|
|
@ -10,10 +10,6 @@ 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
|
(and the VMs managed by it) and patches the repository PVC to create
|
||||||
a small volume using local-path.
|
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
|
|
||||||
small volume for a runner's data.
|
|
||||||
|
|
||||||
The `kustomize.yaml` does not include the test VM. Before creating
|
The `kustomize.yaml` does not include the test VM. Before creating
|
||||||
the test VM, you will again most likely want to change the
|
the test VM, you will again most likely want to change the
|
||||||
disk definition. The sample file uses a reference to some
|
disk definition. The sample file uses a reference to some
|
||||||
|
|
|
||||||
|
|
@ -22,17 +22,3 @@ patches:
|
||||||
storage: 10Gi
|
storage: 10Gi
|
||||||
# Default is to use the default storage class
|
# Default is to use the default storage class
|
||||||
storageClassName: local-path
|
storageClassName: local-path
|
||||||
|
|
||||||
- patch: |-
|
|
||||||
kind: ConfigMap
|
|
||||||
apiVersion: v1
|
|
||||||
metadata:
|
|
||||||
name: vm-operator
|
|
||||||
data:
|
|
||||||
config.yaml: |
|
|
||||||
"/Manager":
|
|
||||||
"/Controller":
|
|
||||||
"/Reconciler"
|
|
||||||
runnerDataPvc:
|
|
||||||
# Default is to use the default storage class
|
|
||||||
storageClassName: local-path
|
|
||||||
|
|
|
||||||
|
|
@ -15,8 +15,8 @@ spec:
|
||||||
vm:
|
vm:
|
||||||
maximumCpus: 4
|
maximumCpus: 4
|
||||||
currentCpus: 2
|
currentCpus: 2
|
||||||
maximumRam: 8Gi
|
maximumRam: "8 GiB"
|
||||||
currentRam: 4Gi
|
currentRam: "4 GiB"
|
||||||
|
|
||||||
networks:
|
networks:
|
||||||
- user: {}
|
- user: {}
|
||||||
|
|
@ -24,7 +24,7 @@ spec:
|
||||||
disks:
|
disks:
|
||||||
- volumeClaimTemplate:
|
- volumeClaimTemplate:
|
||||||
metadata:
|
metadata:
|
||||||
name: system
|
name: test-vm-system
|
||||||
spec:
|
spec:
|
||||||
storageClassName: ""
|
storageClassName: ""
|
||||||
selector:
|
selector:
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,5 @@ patches:
|
||||||
config.yaml: |
|
config.yaml: |
|
||||||
"/Manager":
|
"/Manager":
|
||||||
"/Controller":
|
"/Controller":
|
||||||
"/Reconciler":
|
runnerData:
|
||||||
runnerData:
|
storageClassName: rook-cephfs
|
||||||
storageClassName: rook-cephfs
|
|
||||||
|
|
|
||||||
|
|
@ -15,8 +15,8 @@ spec:
|
||||||
vm:
|
vm:
|
||||||
maximumCpus: 4
|
maximumCpus: 4
|
||||||
currentCpus: 2
|
currentCpus: 2
|
||||||
maximumRam: 8Gi
|
maximumRam: "8 GiB"
|
||||||
currentRam: 4Gi
|
currentRam: "4 GiB"
|
||||||
|
|
||||||
networks:
|
networks:
|
||||||
- user: {}
|
- user: {}
|
||||||
|
|
@ -24,7 +24,7 @@ spec:
|
||||||
disks:
|
disks:
|
||||||
- volumeClaimTemplate:
|
- volumeClaimTemplate:
|
||||||
metadata:
|
metadata:
|
||||||
name: system
|
name: test-vm-system
|
||||||
spec:
|
spec:
|
||||||
storageClassName: rook-ceph-block
|
storageClassName: rook-ceph-block
|
||||||
resources:
|
resources:
|
||||||
|
|
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
org.gradle.parallel=true
|
|
||||||
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Binary file not shown.
3
gradle/wrapper/gradle-wrapper.properties
vendored
3
gradle/wrapper/gradle-wrapper.properties
vendored
|
|
@ -1,7 +1,6 @@
|
||||||
distributionBase=GRADLE_USER_HOME
|
distributionBase=GRADLE_USER_HOME
|
||||||
distributionPath=wrapper/dists
|
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
|
networkTimeout=10000
|
||||||
validateDistributionUrl=true
|
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
zipStorePath=wrapper/dists
|
zipStorePath=wrapper/dists
|
||||||
|
|
|
||||||
31
gradlew
vendored
31
gradlew
vendored
|
|
@ -55,7 +55,7 @@
|
||||||
# Darwin, MinGW, and NonStop.
|
# Darwin, MinGW, and NonStop.
|
||||||
#
|
#
|
||||||
# (3) This script is generated from the Groovy template
|
# (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.
|
# within the Gradle project.
|
||||||
#
|
#
|
||||||
# You can find Gradle at https://github.com/gradle/gradle/.
|
# You can find Gradle at https://github.com/gradle/gradle/.
|
||||||
|
|
@ -83,8 +83,10 @@ done
|
||||||
# This is normally unused
|
# This is normally unused
|
||||||
# shellcheck disable=SC2034
|
# shellcheck disable=SC2034
|
||||||
APP_BASE_NAME=${0##*/}
|
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:-./}" && pwd -P ) || exit
|
||||||
APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && 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.
|
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||||
MAX_FD=maximum
|
MAX_FD=maximum
|
||||||
|
|
@ -131,13 +133,10 @@ location of your Java installation."
|
||||||
fi
|
fi
|
||||||
else
|
else
|
||||||
JAVACMD=java
|
JAVACMD=java
|
||||||
if ! command -v java >/dev/null 2>&1
|
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||||
then
|
|
||||||
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
|
Please set the JAVA_HOME variable in your environment to match the
|
||||||
location of your Java installation."
|
location of your Java installation."
|
||||||
fi
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Increase the maximum file descriptors if we can.
|
# Increase the maximum file descriptors if we can.
|
||||||
|
|
@ -145,7 +144,7 @@ if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
|
||||||
case $MAX_FD in #(
|
case $MAX_FD in #(
|
||||||
max*)
|
max*)
|
||||||
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
|
# 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 ) ||
|
MAX_FD=$( ulimit -H -n ) ||
|
||||||
warn "Could not query maximum file descriptor limit"
|
warn "Could not query maximum file descriptor limit"
|
||||||
esac
|
esac
|
||||||
|
|
@ -153,7 +152,7 @@ if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
|
||||||
'' | soft) :;; #(
|
'' | soft) :;; #(
|
||||||
*)
|
*)
|
||||||
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
|
# 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" ||
|
ulimit -n "$MAX_FD" ||
|
||||||
warn "Could not set maximum file descriptor limit to $MAX_FD"
|
warn "Could not set maximum file descriptor limit to $MAX_FD"
|
||||||
esac
|
esac
|
||||||
|
|
@ -198,15 +197,11 @@ if "$cygwin" || "$msys" ; then
|
||||||
done
|
done
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# Collect all arguments for the java command;
|
||||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
|
||||||
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
# shell script including quotes and variable substitutions, so put them in
|
||||||
|
# double quotes to make sure that they get re-expanded; and
|
||||||
# Collect all arguments for the java command:
|
# * put everything else in single quotes, so that it's not re-expanded.
|
||||||
# * 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.
|
|
||||||
|
|
||||||
set -- \
|
set -- \
|
||||||
"-Dorg.gradle.appname=$APP_BASE_NAME" \
|
"-Dorg.gradle.appname=$APP_BASE_NAME" \
|
||||||
|
|
|
||||||
20
gradlew.bat
vendored
20
gradlew.bat
vendored
|
|
@ -43,11 +43,11 @@ set JAVA_EXE=java.exe
|
||||||
%JAVA_EXE% -version >NUL 2>&1
|
%JAVA_EXE% -version >NUL 2>&1
|
||||||
if %ERRORLEVEL% equ 0 goto execute
|
if %ERRORLEVEL% equ 0 goto execute
|
||||||
|
|
||||||
echo. 1>&2
|
echo.
|
||||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
|
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||||
echo. 1>&2
|
echo.
|
||||||
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
|
echo Please set the JAVA_HOME variable in your environment to match the
|
||||||
echo location of your Java installation. 1>&2
|
echo location of your Java installation.
|
||||||
|
|
||||||
goto fail
|
goto fail
|
||||||
|
|
||||||
|
|
@ -57,11 +57,11 @@ set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||||
|
|
||||||
if exist "%JAVA_EXE%" goto execute
|
if exist "%JAVA_EXE%" goto execute
|
||||||
|
|
||||||
echo. 1>&2
|
echo.
|
||||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
|
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
||||||
echo. 1>&2
|
echo.
|
||||||
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
|
echo Please set the JAVA_HOME variable in your environment to match the
|
||||||
echo location of your Java installation. 1>&2
|
echo location of your Java installation.
|
||||||
|
|
||||||
goto fail
|
goto fail
|
||||||
|
|
||||||
|
|
|
||||||
BIN
misc/DejaVuSans-Bold.woff2
Normal file
BIN
misc/DejaVuSans-Bold.woff2
Normal file
Binary file not shown.
BIN
misc/DejaVuSans.woff2
Normal file
BIN
misc/DejaVuSans.woff2
Normal file
Binary file not shown.
BIN
misc/DejaVuSansMono-Bold.woff2
Normal file
BIN
misc/DejaVuSansMono-Bold.woff2
Normal file
Binary file not shown.
BIN
misc/DejaVuSansMono.woff2
Normal file
BIN
misc/DejaVuSansMono.woff2
Normal file
Binary file not shown.
BIN
misc/DejaVuSerif-Bold.woff2
Normal file
BIN
misc/DejaVuSerif-Bold.woff2
Normal file
Binary file not shown.
BIN
misc/DejaVuSerif.woff2
Normal file
BIN
misc/DejaVuSerif.woff2
Normal file
Binary file not shown.
|
|
@ -1,2 +0,0 @@
|
||||||
:root { --body-font-size: 16px;}
|
|
||||||
:root { --code-font-size: 16px;}
|
|
||||||
|
|
@ -4,33 +4,11 @@
|
||||||
<a href="https://github.com/site/terms" target="_top">Terms</a>
|
<a href="https://github.com/site/terms" target="_top">Terms</a>
|
||||||
— <a href="https://github.com/site/privacy" target="_top">Privacy</a></p>
|
— <a href="https://github.com/site/privacy" target="_top">Privacy</a></p>
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
if (location.hostname.indexOf("github") !== -1 || location.hostname.indexOf("jdrupes.org") !== -1) {
|
if (location.hostname.indexOf("github") !== -1) {
|
||||||
document.getElementById("githubfooter").style.visibility="visible";
|
document.getElementById("githubfooter").style.visibility="visible";
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
<noscript>
|
<noscript>
|
||||||
<div>JavaScript is disabled on your browser, terms and privacy links may not be shown correctly.</div>
|
<div>JavaScript is disabled on your browser, terms and privacy links may not be shown correctly.</div>
|
||||||
</noscript>
|
</noscript>
|
||||||
<!-- Matomo anonymous, no cookies (https://matomo.org/blog/2018/04/how-to-not-process-any-personal-data-with-matomo-and-what-it-means-for-you/) -->
|
|
||||||
<script>
|
|
||||||
var _paq = _paq || [];
|
|
||||||
/* tracker methods like "setCustomDimension" should be called before "trackPageView" */
|
|
||||||
_paq.push(["setDocumentTitle", document.domain + "/" + document.title]);
|
|
||||||
_paq.push(["setCookieDomain", "*.mnlipp.github.io"]);
|
|
||||||
_paq.push(["setDomains", ["*.mnlipp.github.io", "*.jdrupes.org", "kubernetes-vm-operator.readthedocs.io"]]);
|
|
||||||
_paq.push(['disableCookies']);
|
|
||||||
_paq.push(['trackPageView']);
|
|
||||||
_paq.push(['enableLinkTracking']);
|
|
||||||
(function() {
|
|
||||||
var u="https://piwik.mnl.de/";
|
|
||||||
_paq.push(['setTrackerUrl', u+'piwik.php']);
|
|
||||||
_paq.push(['setSiteId', '17']);
|
|
||||||
var d=document, g=d.createElement('script'), s=d.getElementsByTagName('script')[0];
|
|
||||||
g.type='text/javascript'; g.async=true; g.defer=true; g.src=u+'piwik.js'; s.parentNode.insertBefore(g,s);
|
|
||||||
})();
|
|
||||||
</script>
|
|
||||||
<noscript><p><img referrerpolicy="no-referrer-when-downgrade"
|
|
||||||
src="//piwik.mnl.de/matomo.php?idsite=17&rec=1&action_name=VM-Operator" style="border:0;" alt="" /></p></noscript>
|
|
||||||
<!-- End Matomo Code -->
|
|
||||||
<script defer src="https://gotit.mnl.de/script.js" data-website-id="14b277ad-d330-4a54-82f1-a77d111240ac"></script>
|
|
||||||
</div>
|
</div>
|
||||||
912
misc/stylesheet.css
Normal file
912
misc/stylesheet.css
Normal file
|
|
@ -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, \
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="768" height="768">\
|
||||||
|
<path d="M584 664H104V184h216V80H0v688h688V448H584zM384 0l132 \
|
||||||
|
132-240 240 120 120 240-240 132 132V0z" fill="%234a6782"/>\
|
||||||
|
</svg>');
|
||||||
|
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, \
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="768" height="768">\
|
||||||
|
<path d="M584 664H104V184h216V80H0v688h688V448H584zM384 0l132 \
|
||||||
|
132-240 240 120 120 240-240 132 132V0z" fill="%23bb7a2a"/>\
|
||||||
|
</svg>');
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,10 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
|
|
||||||
<fileset-config file-format-version="1.2.0" simple-config="false" sync-formatter="false">
|
|
||||||
<local-check-config name="Project Checks" location="/VM-Operator/checkstyle.xml" type="project" description="">
|
|
||||||
<additional-data name="protect-config-file" value="false"/>
|
|
||||||
</local-check-config>
|
|
||||||
<fileset name="all" enabled="true" check-config-name="Project Checks" local="true">
|
|
||||||
<file-match-pattern match-pattern="." include-pattern="true"/>
|
|
||||||
</fileset>
|
|
||||||
</fileset-config>
|
|
||||||
|
|
@ -1,7 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<eclipse-pmd xmlns="http://acanda.ch/eclipse-pmd/0.8" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://acanda.ch/eclipse-pmd/0.8 http://acanda.ch/eclipse-pmd/eclipse-pmd-0.8.xsd">
|
|
||||||
<analysis enabled="true" />
|
|
||||||
<rulesets>
|
|
||||||
<ruleset name="Custom Rules" ref="VM-Operator/ruleset.xml" refcontext="workspace" />
|
|
||||||
</rulesets>
|
|
||||||
</eclipse-pmd>
|
|
||||||
|
|
@ -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 <https\://www.gnu.org/licenses/>.\n */
|
|
||||||
project_specific_settings=true
|
|
||||||
visibility_package=false
|
|
||||||
visibility_private=false
|
|
||||||
visibility_protected=false
|
|
||||||
|
|
@ -1,13 +0,0 @@
|
||||||
arguments=
|
|
||||||
auto.sync=false
|
|
||||||
build.scans.enabled=false
|
|
||||||
connection.gradle.distribution=GRADLE_DISTRIBUTION(WRAPPER)
|
|
||||||
connection.project.dir=..
|
|
||||||
eclipse.preferences.version=1
|
|
||||||
gradle.user.home=
|
|
||||||
java.home=
|
|
||||||
jvm.arguments=
|
|
||||||
offline.mode=false
|
|
||||||
override.workspace.settings=false
|
|
||||||
show.console.view=false
|
|
||||||
show.executions.view=false
|
|
||||||
|
|
@ -1,2 +0,0 @@
|
||||||
eclipse.preferences.version=1
|
|
||||||
encoding/<project>=UTF-8
|
|
||||||
|
|
@ -1,2 +0,0 @@
|
||||||
eclipse.preferences.version=1
|
|
||||||
line.separator=\n
|
|
||||||
|
|
@ -1,17 +0,0 @@
|
||||||
/*
|
|
||||||
* This file was generated by the Gradle 'init' task.
|
|
||||||
*
|
|
||||||
* This project uses @Incubating APIs which are subject to change.
|
|
||||||
*/
|
|
||||||
|
|
||||||
plugins {
|
|
||||||
id 'org.jdrupes.vmoperator.java-library-conventions'
|
|
||||||
}
|
|
||||||
|
|
||||||
dependencies {
|
|
||||||
api project(':org.jdrupes.vmoperator.util')
|
|
||||||
api '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]'
|
|
||||||
}
|
|
||||||
|
|
@ -1,127 +0,0 @@
|
||||||
/*
|
|
||||||
* VM-Operator
|
|
||||||
* Copyright (C) 2023 Michael N. Lipp
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU Affero General Public License as
|
|
||||||
* published by the Free Software Foundation, either version 3 of the
|
|
||||||
* License, or (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU Affero General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Affero General Public License
|
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.jdrupes.vmoperator.common;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Some constants.
|
|
||||||
*/
|
|
||||||
@SuppressWarnings("PMD.DataClass")
|
|
||||||
public class Constants {
|
|
||||||
|
|
||||||
/** The Constant APP_NAME. */
|
|
||||||
public static final String APP_NAME = "vm-runner";
|
|
||||||
|
|
||||||
/** 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 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";
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,113 +0,0 @@
|
||||||
/*
|
|
||||||
* VM-Operator
|
|
||||||
* Copyright (C) 2023 Michael N. Lipp
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU Affero General Public License as
|
|
||||||
* published by the Free Software Foundation, either version 3 of the
|
|
||||||
* License, or (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU Affero General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Affero General Public License
|
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.jdrupes.vmoperator.common;
|
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
|
||||||
import java.math.BigInteger;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.regex.Pattern;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Provides methods for parsing "official" memory sizes..
|
|
||||||
*/
|
|
||||||
@SuppressWarnings("PMD.UseUtilityClass")
|
|
||||||
public class Convertions {
|
|
||||||
|
|
||||||
@SuppressWarnings({ "PMD.UseConcurrentHashMap",
|
|
||||||
"PMD.FieldNamingConventions" })
|
|
||||||
private static final Map<String, BigInteger> unitMap = new HashMap<>();
|
|
||||||
@SuppressWarnings({ "PMD.FieldNamingConventions" })
|
|
||||||
private static final List<Map.Entry<String, BigInteger>> unitMappings;
|
|
||||||
@SuppressWarnings({ "PMD.FieldNamingConventions" })
|
|
||||||
private static final Pattern memorySize
|
|
||||||
= Pattern.compile("^\\s*(\\d+(\\.\\d+)?)\\s*([A-Za-z]*)\\s*");
|
|
||||||
|
|
||||||
static {
|
|
||||||
// SI units and common abbreviations
|
|
||||||
BigInteger factor = BigInteger.ONE;
|
|
||||||
unitMap.put("", factor);
|
|
||||||
BigInteger scale = BigInteger.valueOf(1000);
|
|
||||||
for (var unit : List.of("B", "kB", "MB", "GB", "TB", "PB", "EB")) {
|
|
||||||
unitMap.put(unit, factor);
|
|
||||||
factor = factor.multiply(scale);
|
|
||||||
}
|
|
||||||
// Binary units
|
|
||||||
factor = BigInteger.valueOf(1024);
|
|
||||||
scale = BigInteger.valueOf(1024);
|
|
||||||
for (var unit : List.of("KiB", "MiB", "GiB", "TiB", "PiB", "EiB")) {
|
|
||||||
unitMap.put(unit, factor);
|
|
||||||
factor = factor.multiply(scale);
|
|
||||||
}
|
|
||||||
unitMappings = unitMap.entrySet().stream()
|
|
||||||
.sorted((a, b) -> -1 * a.getValue().compareTo(b.getValue()))
|
|
||||||
.toList();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Parses a memory size specification.
|
|
||||||
*
|
|
||||||
* @param amount the amount
|
|
||||||
* @return the big integer
|
|
||||||
*/
|
|
||||||
public static BigInteger parseMemory(Object amount) {
|
|
||||||
if (amount == null) {
|
|
||||||
return (BigInteger) amount;
|
|
||||||
}
|
|
||||||
if (amount instanceof BigInteger number) {
|
|
||||||
return number;
|
|
||||||
}
|
|
||||||
if (amount instanceof Number number) {
|
|
||||||
return BigInteger.valueOf(number.longValue());
|
|
||||||
}
|
|
||||||
var matcher = memorySize.matcher(amount.toString());
|
|
||||||
if (!matcher.matches()) {
|
|
||||||
throw new NumberFormatException(amount.toString());
|
|
||||||
}
|
|
||||||
var unit = BigInteger.ONE;
|
|
||||||
if (matcher.group(3) != null) {
|
|
||||||
unit = unitMap.get(matcher.group(3));
|
|
||||||
if (unit == null) {
|
|
||||||
throw new NumberFormatException("Illegal unit \""
|
|
||||||
+ matcher.group(3) + "\" in \"" + amount.toString() + "\"");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
var number = matcher.group(1);
|
|
||||||
return new BigDecimal(number).multiply(new BigDecimal(unit))
|
|
||||||
.toBigInteger();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Format memory size for humans.
|
|
||||||
*
|
|
||||||
* @param size the size
|
|
||||||
* @return the string
|
|
||||||
*/
|
|
||||||
public static String formatMemory(BigInteger size) {
|
|
||||||
for (var mapping : unitMappings) {
|
|
||||||
if (size.compareTo(mapping.getValue()) >= 0
|
|
||||||
&& size.mod(mapping.getValue()).equals(BigInteger.ZERO)) {
|
|
||||||
return (size.divide(mapping.getValue()).toString()
|
|
||||||
+ " " + mapping.getKey()).trim();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return size.toString();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -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 <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.jdrupes.vmoperator.common;
|
|
||||||
|
|
||||||
import com.google.gson.Gson;
|
|
||||||
import com.google.gson.InstanceCreator;
|
|
||||||
import com.google.gson.JsonObject;
|
|
||||||
import com.google.gson.TypeAdapter;
|
|
||||||
import com.google.gson.TypeAdapterFactory;
|
|
||||||
import com.google.gson.reflect.TypeToken;
|
|
||||||
import com.google.gson.stream.JsonReader;
|
|
||||||
import com.google.gson.stream.JsonWriter;
|
|
||||||
import 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 <O> the generic type
|
|
||||||
* @param <L> the generic type
|
|
||||||
*/
|
|
||||||
public class DynamicTypeAdapterFactory<O extends K8sDynamicModel,
|
|
||||||
L extends K8sDynamicModelsBase<O>> implements TypeAdapterFactory {
|
|
||||||
|
|
||||||
private final Class<O> objectClass;
|
|
||||||
private final Class<L> 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<O> objectClass,
|
|
||||||
Class<L> objectListClass) {
|
|
||||||
this.objectClass = objectClass;
|
|
||||||
this.objectListClass = objectListClass;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a type adapter for the given type.
|
|
||||||
*
|
|
||||||
* @param <T> the generic type
|
|
||||||
* @param gson the gson
|
|
||||||
* @param typeToken the type token
|
|
||||||
* @return the type adapter or null if the type is not handles by
|
|
||||||
* this factory
|
|
||||||
*/
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
@Override
|
|
||||||
public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> typeToken) {
|
|
||||||
if (TypeToken.get(objectClass).equals(typeToken)) {
|
|
||||||
return (TypeAdapter<T>) new ModelCreator(gson);
|
|
||||||
}
|
|
||||||
if (TypeToken.get(objectListClass).equals(typeToken)) {
|
|
||||||
return (TypeAdapter<T>) new ModelsCreator(gson);
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The Class ModelCreator.
|
|
||||||
*/
|
|
||||||
private class ModelCreator extends TypeAdapter<O>
|
|
||||||
implements InstanceCreator<O> {
|
|
||||||
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<L>
|
|
||||||
implements InstanceCreator<L> {
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -1,249 +0,0 @@
|
||||||
/*
|
|
||||||
* VM-Operator
|
|
||||||
* Copyright (C) 2023,2024 Michael N. Lipp
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU Affero General Public License as
|
|
||||||
* published by the Free Software Foundation, either version 3 of the
|
|
||||||
* License, or (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU Affero General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Affero General Public License
|
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.jdrupes.vmoperator.common;
|
|
||||||
|
|
||||||
import com.google.gson.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.models.V1ObjectMeta;
|
|
||||||
import io.kubernetes.client.openapi.models.V1ObjectReference;
|
|
||||||
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.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" })
|
|
||||||
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.
|
|
||||||
*
|
|
||||||
* @param <T> the generic type
|
|
||||||
* @param response the response
|
|
||||||
* @return the optional
|
|
||||||
* @throws ApiException the API exception
|
|
||||||
*/
|
|
||||||
public static <T extends KubernetesType> Optional<T>
|
|
||||||
optional(KubernetesApiResponse<T> response) throws ApiException {
|
|
||||||
if (response.isSuccess()) {
|
|
||||||
return Optional.of(response.getObject());
|
|
||||||
}
|
|
||||||
if (response.getHttpStatusCode() == HttpURLConnection.HTTP_NOT_FOUND) {
|
|
||||||
return Optional.empty();
|
|
||||||
}
|
|
||||||
response.throwsApiException();
|
|
||||||
// Never reached
|
|
||||||
return Optional.empty();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a new context with the given version as preferred version.
|
|
||||||
*
|
|
||||||
* @param context the context
|
|
||||||
* @param version the version
|
|
||||||
* @return the API resource
|
|
||||||
*/
|
|
||||||
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<String, Object> yamlData
|
|
||||||
= new Yaml(new SafeConstructor(new LoaderOptions())).load(yaml);
|
|
||||||
|
|
||||||
// There's no short-cut from Java (collections) to Gson
|
|
||||||
var gson = client.getJSON().getGson();
|
|
||||||
var jsonText = gson.toJson(yamlData);
|
|
||||||
return gson.fromJson(jsonText, JsonObject.class);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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<APIResource> context(ApiClient client,
|
|
||||||
String group, String version, String kind) throws ApiException {
|
|
||||||
var apiMatch = new Discovery(client).findAll().stream()
|
|
||||||
.filter(r -> r.getGroup().equals(group) && r.getKind().equals(kind)
|
|
||||||
&& (Strings.isNullOrEmpty(version)
|
|
||||||
|| r.getVersions().contains(version)))
|
|
||||||
.findFirst();
|
|
||||||
if (apiMatch.isEmpty()) {
|
|
||||||
return Optional.empty();
|
|
||||||
}
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Apply the given patch data.
|
|
||||||
*
|
|
||||||
* @param <T> the generic type
|
|
||||||
* @param <LT> the generic type
|
|
||||||
* @param api the api
|
|
||||||
* @param existing the existing
|
|
||||||
* @param update the update
|
|
||||||
* @return the t
|
|
||||||
* @throws ApiException the api exception
|
|
||||||
*/
|
|
||||||
@SuppressWarnings("PMD.GenericsNaming")
|
|
||||||
public static <T extends KubernetesObject, LT extends KubernetesListObject>
|
|
||||||
T apply(GenericKubernetesApi<T, LT> api, T existing, String update)
|
|
||||||
throws ApiException {
|
|
||||||
PatchOptions opts = new PatchOptions();
|
|
||||||
opts.setForce(true);
|
|
||||||
opts.setFieldManager("kubernetes-java-kubectl-apply");
|
|
||||||
var response = api.patch(existing.getMetadata().getNamespace(),
|
|
||||||
existing.getMetadata().getName(), V1Patch.PATCH_FORMAT_APPLY_YAML,
|
|
||||||
new V1Patch(update), opts).throwsApiException();
|
|
||||||
return response.getObject();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -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 <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.jdrupes.vmoperator.common;
|
|
||||||
|
|
||||||
import io.kubernetes.client.openapi.ApiCallback;
|
|
||||||
import io.kubernetes.client.openapi.ApiClient;
|
|
||||||
import io.kubernetes.client.openapi.ApiException;
|
|
||||||
import io.kubernetes.client.openapi.ApiResponse;
|
|
||||||
import io.kubernetes.client.openapi.JSON;
|
|
||||||
import io.kubernetes.client.openapi.Pair;
|
|
||||||
import io.kubernetes.client.openapi.auth.Authentication;
|
|
||||||
import io.kubernetes.client.util.ClientBuilder;
|
|
||||||
import io.kubernetes.client.util.generic.options.PatchOptions;
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.lang.reflect.Type;
|
|
||||||
import java.text.DateFormat;
|
|
||||||
import java.time.format.DateTimeFormatter;
|
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import javax.net.ssl.KeyManager;
|
|
||||||
import okhttp3.Call;
|
|
||||||
import okhttp3.OkHttpClient;
|
|
||||||
import okhttp3.Request;
|
|
||||||
import okhttp3.Request.Builder;
|
|
||||||
import okhttp3.RequestBody;
|
|
||||||
import okhttp3.Response;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A client with some additional properties.
|
|
||||||
*/
|
|
||||||
@SuppressWarnings({ "PMD.ExcessivePublicCount", "PMD.TooManyMethods",
|
|
||||||
"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<String, Authentication> 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<Pair> 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<Pair> 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 <T> 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> 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 <T> the generic type
|
|
||||||
* @param call the call
|
|
||||||
* @return the api response
|
|
||||||
* @throws ApiException the api exception
|
|
||||||
* @see ApiClient#execute(okhttp3.Call)
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public <T> ApiResponse<T> execute(Call call) throws ApiException {
|
|
||||||
return apiClient().execute(call);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Execute.
|
|
||||||
*
|
|
||||||
* @param <T> 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 <T> ApiResponse<T> execute(Call call, Type returnType)
|
|
||||||
throws ApiException {
|
|
||||||
return apiClient().execute(call, returnType);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Execute async.
|
|
||||||
*
|
|
||||||
* @param <T> the generic type
|
|
||||||
* @param call the call
|
|
||||||
* @param callback the callback
|
|
||||||
* @see ApiClient#executeAsync(okhttp3.Call, io.kubernetes.client.openapi.ApiCallback)
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public <T> void executeAsync(Call call, ApiCallback<T> callback) {
|
|
||||||
apiClient().executeAsync(call, callback);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Execute async.
|
|
||||||
*
|
|
||||||
* @param <T> 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 <T> void executeAsync(Call call, Type returnType,
|
|
||||||
ApiCallback<T> callback) {
|
|
||||||
apiClient().executeAsync(call, returnType, callback);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handle response.
|
|
||||||
*
|
|
||||||
* @param <T> 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> 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<Pair> queryParams,
|
|
||||||
List<Pair> collectionQueryParams, Object body,
|
|
||||||
Map<String, String> headerParams, Map<String, String> cookieParams,
|
|
||||||
Map<String, Object> formParams, String[] authNames,
|
|
||||||
ApiCallback callback) throws ApiException {
|
|
||||||
return apiClient().buildCall(path, method, queryParams,
|
|
||||||
collectionQueryParams, body, headerParams, cookieParams, formParams,
|
|
||||||
authNames, callback);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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<Pair> queryParams, List<Pair> collectionQueryParams,
|
|
||||||
Object body, Map<String, String> headerParams,
|
|
||||||
Map<String, String> cookieParams, Map<String, Object> formParams,
|
|
||||||
String[] authNames, ApiCallback callback) throws ApiException {
|
|
||||||
return apiClient().buildRequest(path, method, queryParams,
|
|
||||||
collectionQueryParams, body, headerParams, cookieParams, formParams,
|
|
||||||
authNames, callback);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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<Pair> queryParams,
|
|
||||||
List<Pair> 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<String, String> 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<String, String> 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<Pair> queryParams,
|
|
||||||
Map<String, String> headerParams,
|
|
||||||
Map<String, String> 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<String, Object> 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<String, Object> 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);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -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 <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.jdrupes.vmoperator.common;
|
|
||||||
|
|
||||||
import io.kubernetes.client.Discovery.APIResource;
|
|
||||||
import io.kubernetes.client.apimachinery.GroupVersionKind;
|
|
||||||
import io.kubernetes.client.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 <O> the generic type
|
|
||||||
* @param <L> the generic type
|
|
||||||
*/
|
|
||||||
@SuppressWarnings({ "PMD.CouplingBetweenObjects" })
|
|
||||||
public class K8sClusterGenericStub<O extends KubernetesObject,
|
|
||||||
L extends KubernetesListObject> {
|
|
||||||
protected final K8sClient client;
|
|
||||||
private final GenericKubernetesApi<O, L> 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<O> objectClass,
|
|
||||||
Class<L> objectListClass, K8sClient client, APIResource context,
|
|
||||||
String name) {
|
|
||||||
this.client = client;
|
|
||||||
this.name = name;
|
|
||||||
|
|
||||||
// Bind version
|
|
||||||
var foundVersion = context.getPreferredVersion();
|
|
||||||
GenericKubernetesApi<O, L> 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<O> 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<O> updateStatus(O object,
|
|
||||||
Function<O, Object> status) throws ApiException {
|
|
||||||
return K8s.optional(api.updateStatus(object, status));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Updates the status.
|
|
||||||
*
|
|
||||||
* @param status the status
|
|
||||||
* @return the kubernetes api response
|
|
||||||
* the updated model or empty if not successful
|
|
||||||
* @throws ApiException the api exception
|
|
||||||
*/
|
|
||||||
public Optional<O> updateStatus(Function<O, Object> status)
|
|
||||||
throws ApiException {
|
|
||||||
return updateStatus(api.get(name).throwsApiException().getObject(),
|
|
||||||
status);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Patch the object.
|
|
||||||
*
|
|
||||||
* @param patchType the patch type
|
|
||||||
* @param patch the patch
|
|
||||||
* @param options the options
|
|
||||||
* @return the kubernetes api response
|
|
||||||
* @throws ApiException the api exception
|
|
||||||
*/
|
|
||||||
public Optional<O> patch(String patchType, V1Patch patch,
|
|
||||||
PatchOptions options) throws ApiException {
|
|
||||||
return K8s
|
|
||||||
.optional(api.patch(name, patchType, patch, options));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Patch the object using default options.
|
|
||||||
*
|
|
||||||
* @param patchType the patch type
|
|
||||||
* @param patch the patch
|
|
||||||
* @return the kubernetes api response
|
|
||||||
* @throws ApiException the api exception
|
|
||||||
*/
|
|
||||||
public Optional<O>
|
|
||||||
patch(String patchType, V1Patch patch) throws ApiException {
|
|
||||||
PatchOptions opts = new PatchOptions();
|
|
||||||
return patch(patchType, patch, opts);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A supplier for generic stubs.
|
|
||||||
*
|
|
||||||
* @param <O> the object type
|
|
||||||
* @param <L> the object list type
|
|
||||||
* @param <R> the result type
|
|
||||||
*/
|
|
||||||
@FunctionalInterface
|
|
||||||
public interface GenericSupplier<O extends KubernetesObject,
|
|
||||||
L extends KubernetesListObject,
|
|
||||||
R extends K8sClusterGenericStub<O, L>> {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets a new stub.
|
|
||||||
*
|
|
||||||
* @param objectClass the object class
|
|
||||||
* @param objectListClass the object list class
|
|
||||||
* @param client the client
|
|
||||||
* @param context the API resource
|
|
||||||
* @param name the name
|
|
||||||
* @return the result
|
|
||||||
*/
|
|
||||||
R get(Class<O> objectClass, Class<L> 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 <O> the object type
|
|
||||||
* @param <L> the object list type
|
|
||||||
* @param <R> the stub type
|
|
||||||
* @param objectClass the object class
|
|
||||||
* @param objectListClass the object list class
|
|
||||||
* @param client the client
|
|
||||||
* @param gvk the group, version and kind
|
|
||||||
* @param name the name
|
|
||||||
* @param provider the provider
|
|
||||||
* @return the stub if the object exists
|
|
||||||
* @throws ApiException the api exception
|
|
||||||
*/
|
|
||||||
public static <O extends KubernetesObject, L extends KubernetesListObject,
|
|
||||||
R extends K8sClusterGenericStub<O, L>>
|
|
||||||
R get(Class<O> objectClass, Class<L> objectListClass,
|
|
||||||
K8sClient client, GroupVersionKind gvk, String name,
|
|
||||||
GenericSupplier<O, L, R> provider) throws ApiException {
|
|
||||||
var context = K8s.context(client, gvk.getGroup(), gvk.getVersion(),
|
|
||||||
gvk.getKind());
|
|
||||||
if (context.isEmpty()) {
|
|
||||||
throw new ApiException("No known API for " + gvk.getGroup()
|
|
||||||
+ "/" + gvk.getVersion() + " " + gvk.getKind());
|
|
||||||
}
|
|
||||||
return provider.get(objectClass, objectListClass, client, context.get(),
|
|
||||||
name);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get an object stub.
|
|
||||||
*
|
|
||||||
* @param <O> the object type
|
|
||||||
* @param <L> the object list type
|
|
||||||
* @param <R> the stub type
|
|
||||||
* @param objectClass the object class
|
|
||||||
* @param objectListClass the object list class
|
|
||||||
* @param client the client
|
|
||||||
* @param context the context
|
|
||||||
* @param name the name
|
|
||||||
* @param provider the provider
|
|
||||||
* @return the stub if the object exists
|
|
||||||
* @throws ApiException the api exception
|
|
||||||
*/
|
|
||||||
public static <O extends KubernetesObject, L extends KubernetesListObject,
|
|
||||||
R extends K8sClusterGenericStub<O, L>>
|
|
||||||
R get(Class<O> objectClass, Class<L> objectListClass,
|
|
||||||
K8sClient client, APIResource context, String name,
|
|
||||||
GenericSupplier<O, L, R> provider) {
|
|
||||||
return provider.get(objectClass, objectListClass, client, context,
|
|
||||||
name);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get an object stub for a newly created object.
|
|
||||||
*
|
|
||||||
* @param <O> the object type
|
|
||||||
* @param <L> the object list type
|
|
||||||
* @param <R> the stub type
|
|
||||||
* @param objectClass the object class
|
|
||||||
* @param objectListClass the object list class
|
|
||||||
* @param client the client
|
|
||||||
* @param context the context
|
|
||||||
* @param model the model
|
|
||||||
* @param provider the provider
|
|
||||||
* @return the stub if the object exists
|
|
||||||
* @throws ApiException the api exception
|
|
||||||
*/
|
|
||||||
public static <O extends KubernetesObject, L extends KubernetesListObject,
|
|
||||||
R extends K8sClusterGenericStub<O, L>>
|
|
||||||
R create(Class<O> objectClass, Class<L> objectListClass,
|
|
||||||
K8sClient client, APIResource context, O model,
|
|
||||||
GenericSupplier<O, L, R> provider) throws ApiException {
|
|
||||||
var api = new GenericKubernetesApi<>(objectClass, objectListClass,
|
|
||||||
context.getGroup(), context.getPreferredVersion(),
|
|
||||||
context.getResourcePlural(), client);
|
|
||||||
api.create(model).throwsApiException();
|
|
||||||
return provider.get(objectClass, objectListClass, client,
|
|
||||||
context, model.getMetadata().getName());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the stubs for the objects that match
|
|
||||||
* the criteria from the given options.
|
|
||||||
*
|
|
||||||
* @param <O> the object type
|
|
||||||
* @param <L> the object list type
|
|
||||||
* @param <R> the stub type
|
|
||||||
* @param objectClass the object class
|
|
||||||
* @param objectListClass the object list class
|
|
||||||
* @param client the client
|
|
||||||
* @param context the context
|
|
||||||
* @param options the options
|
|
||||||
* @param provider the provider
|
|
||||||
* @return the collection
|
|
||||||
* @throws ApiException the api exception
|
|
||||||
*/
|
|
||||||
public static <O extends KubernetesObject, L extends KubernetesListObject,
|
|
||||||
R extends K8sClusterGenericStub<O, L>>
|
|
||||||
Collection<R> list(Class<O> objectClass, Class<L> objectListClass,
|
|
||||||
K8sClient client, APIResource context,
|
|
||||||
ListOptions options, GenericSupplier<O, L, R> provider)
|
|
||||||
throws ApiException {
|
|
||||||
var result = new ArrayList<R>();
|
|
||||||
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<String> candidateVersions(APIResource context) {
|
|
||||||
var result = new LinkedList<>(context.getVersions());
|
|
||||||
result.remove(context.getPreferredVersion());
|
|
||||||
result.add(0, context.getPreferredVersion());
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -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 <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.jdrupes.vmoperator.common;
|
|
||||||
|
|
||||||
import com.google.gson.Gson;
|
|
||||||
import com.google.gson.JsonObject;
|
|
||||||
import io.kubernetes.client.common.KubernetesObject;
|
|
||||||
import io.kubernetes.client.openapi.models.V1ObjectMeta;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Represents a Kubernetes object using a JSON data structure.
|
|
||||||
* Some information that is common to all Kubernetes objects,
|
|
||||||
* notably the metadata, is made available through the methods
|
|
||||||
* defined by {@link KubernetesObject}.
|
|
||||||
*/
|
|
||||||
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();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -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 <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.jdrupes.vmoperator.common;
|
|
||||||
|
|
||||||
import com.google.gson.Gson;
|
|
||||||
import com.google.gson.JsonObject;
|
|
||||||
import io.kubernetes.client.common.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<K8sDynamicModel> {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -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 <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.jdrupes.vmoperator.common;
|
|
||||||
|
|
||||||
import com.google.gson.Gson;
|
|
||||||
import com.google.gson.JsonElement;
|
|
||||||
import com.google.gson.JsonObject;
|
|
||||||
import io.kubernetes.client.common.KubernetesListObject;
|
|
||||||
import io.kubernetes.client.openapi.Configuration;
|
|
||||||
import io.kubernetes.client.openapi.models.V1ListMeta;
|
|
||||||
import java.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<T extends K8sDynamicModel>
|
|
||||||
implements KubernetesListObject {
|
|
||||||
|
|
||||||
private final JsonObject data;
|
|
||||||
private final V1ListMeta metadata;
|
|
||||||
private final List<T> 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<T> 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<T> 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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -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 <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.jdrupes.vmoperator.common;
|
|
||||||
|
|
||||||
import io.kubernetes.client.Discovery.APIResource;
|
|
||||||
import io.kubernetes.client.apimachinery.GroupVersionKind;
|
|
||||||
import io.kubernetes.client.openapi.ApiException;
|
|
||||||
import 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<K8sDynamicModel, K8sDynamicModels> {
|
|
||||||
|
|
||||||
private static DynamicTypeAdapterFactory<K8sDynamicModel,
|
|
||||||
K8sDynamicModels> 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<K8sDynamicStub> 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<K8sDynamicStub> 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<K8sDynamicModel, K8sDynamicModels> {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Instantiates a new dynamic model type adapter factory.
|
|
||||||
*/
|
|
||||||
public K8sDynamicModelTypeAdapterFactory() {
|
|
||||||
super(K8sDynamicModel.class, K8sDynamicModels.class);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -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 <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
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<O extends K8sDynamicModel,
|
|
||||||
L extends K8sDynamicModelsBase<O>> extends K8sGenericStub<O, L> {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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<O> objectClass,
|
|
||||||
Class<L> objectListClass, DynamicTypeAdapterFactory<O, L> taf,
|
|
||||||
K8sClient client, APIResource context, String namespace,
|
|
||||||
String name) {
|
|
||||||
super(objectClass, objectListClass, client, context, namespace, name);
|
|
||||||
taf.register(client);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -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 <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.jdrupes.vmoperator.common;
|
|
||||||
|
|
||||||
import io.kubernetes.client.Discovery.APIResource;
|
|
||||||
import io.kubernetes.client.apimachinery.GroupVersionKind;
|
|
||||||
import io.kubernetes.client.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 <O> the generic type
|
|
||||||
* @param <L> the generic type
|
|
||||||
*/
|
|
||||||
@SuppressWarnings({ "PMD.TooManyMethods" })
|
|
||||||
public class K8sGenericStub<O extends KubernetesObject,
|
|
||||||
L extends KubernetesListObject> {
|
|
||||||
protected final K8sClient client;
|
|
||||||
private final GenericKubernetesApi<O, L> 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<O> objectClass, Class<L> 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<O, L> 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<O> 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<O> updateStatus(O object, Function<O, Object> 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<O> updateStatus(Function<O, Object> 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<O> updateStatus(Function<O, Object> 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<O> updateStatus(Function<O, Object> 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<O> updateStatus(Function<O, Object> 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<O> 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<O>
|
|
||||||
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<O> 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<O> 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<O> update(O object, UpdateOptions options)
|
|
||||||
throws ApiException {
|
|
||||||
return api.update(object, options).throwsApiException();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A supplier for generic stubs.
|
|
||||||
*
|
|
||||||
* @param <O> the object type
|
|
||||||
* @param <L> the object list type
|
|
||||||
* @param <R> the result type
|
|
||||||
*/
|
|
||||||
@FunctionalInterface
|
|
||||||
public interface GenericSupplier<O extends KubernetesObject,
|
|
||||||
L extends KubernetesListObject, R extends K8sGenericStub<O, L>> {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets a new stub.
|
|
||||||
*
|
|
||||||
* @param client the client
|
|
||||||
* @param namespace the namespace
|
|
||||||
* @param name the name
|
|
||||||
* @return the result
|
|
||||||
*/
|
|
||||||
R get(K8sClient client, String namespace, String name);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
@SuppressWarnings("PMD.UseLocaleWithCaseConversions")
|
|
||||||
public String toString() {
|
|
||||||
return (Strings.isNullOrEmpty(group()) ? "" : group() + "/")
|
|
||||||
+ version().toUpperCase() + kind() + " " + namespace + ":" + name;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get a namespaced object stub for a newly created object.
|
|
||||||
*
|
|
||||||
* @param <O> the object type
|
|
||||||
* @param <L> the object list type
|
|
||||||
* @param <R> the stub type
|
|
||||||
* @param objectClass the object class
|
|
||||||
* @param objectListClass the object list class
|
|
||||||
* @param client the client
|
|
||||||
* @param context the context
|
|
||||||
* @param model the model
|
|
||||||
* @param provider the provider
|
|
||||||
* @return the stub if the object exists
|
|
||||||
* @throws ApiException the api exception
|
|
||||||
*/
|
|
||||||
public static <O extends KubernetesObject, L extends KubernetesListObject,
|
|
||||||
R extends K8sGenericStub<O, L>>
|
|
||||||
R create(Class<O> objectClass, Class<L> objectListClass,
|
|
||||||
K8sClient client, APIResource context, O model,
|
|
||||||
GenericSupplier<O, L, R> provider) throws ApiException {
|
|
||||||
var api = new GenericKubernetesApi<>(objectClass, objectListClass,
|
|
||||||
context.getGroup(), context.getPreferredVersion(),
|
|
||||||
context.getResourcePlural(), client);
|
|
||||||
api.create(model).throwsApiException();
|
|
||||||
return provider.get(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 <O> the object type
|
|
||||||
* @param <L> the object list type
|
|
||||||
* @param <R> the stub type
|
|
||||||
* @param objectClass the object class
|
|
||||||
* @param objectListClass the object list class
|
|
||||||
* @param client the client
|
|
||||||
* @param context the context
|
|
||||||
* @param namespace the namespace
|
|
||||||
* @param options the options
|
|
||||||
* @param provider the provider
|
|
||||||
* @return the collection
|
|
||||||
* @throws ApiException the api exception
|
|
||||||
*/
|
|
||||||
public static <O extends KubernetesObject, L extends KubernetesListObject,
|
|
||||||
R extends K8sGenericStub<O, L>>
|
|
||||||
Collection<R> list(Class<O> objectClass, Class<L> objectListClass,
|
|
||||||
K8sClient client, APIResource context, String namespace,
|
|
||||||
ListOptions options, GenericSupplier<O, L, R> provider)
|
|
||||||
throws ApiException {
|
|
||||||
var result = new ArrayList<R>();
|
|
||||||
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<String> 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();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -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 <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
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 <O> the object type for the context
|
|
||||||
* @param <L> the object list type for the context
|
|
||||||
*/
|
|
||||||
public class K8sObserver<O extends KubernetesObject,
|
|
||||||
L extends KubernetesListObject> {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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<O, L> api;
|
|
||||||
protected final APIResource context;
|
|
||||||
protected final String namespace;
|
|
||||||
protected final ListOptions options;
|
|
||||||
protected final Thread thread;
|
|
||||||
protected BiConsumer<K8sClient, Response<O>> handler;
|
|
||||||
protected BiConsumer<K8sObserver<O, L>, 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<O> objectClass, Class<L> 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<O, L>
|
|
||||||
handler(BiConsumer<K8sClient, Response<O>> 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<O, L> onTerminated(
|
|
||||||
BiConsumer<K8sObserver<O, L>, Throwable> onTerminated) {
|
|
||||||
this.onTerminated = onTerminated;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Start the observer.
|
|
||||||
*
|
|
||||||
* @return the observer
|
|
||||||
*/
|
|
||||||
public K8sObserver<O, L> start() {
|
|
||||||
if (handler == null) {
|
|
||||||
throw new IllegalStateException("No handler defined");
|
|
||||||
}
|
|
||||||
thread.start();
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Stops the observer.
|
|
||||||
*
|
|
||||||
* @return the observer
|
|
||||||
*/
|
|
||||||
public K8sObserver<O, L> 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -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 <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.jdrupes.vmoperator.common;
|
|
||||||
|
|
||||||
import io.kubernetes.client.Discovery.APIResource;
|
|
||||||
import io.kubernetes.client.openapi.models.V1ConfigMap;
|
|
||||||
import io.kubernetes.client.openapi.models.V1ConfigMapList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A stub for config maps (v1).
|
|
||||||
*/
|
|
||||||
public class K8sV1ConfigMapStub
|
|
||||||
extends K8sGenericStub<V1ConfigMap, V1ConfigMapList> {
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -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 <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.jdrupes.vmoperator.common;
|
|
||||||
|
|
||||||
import io.kubernetes.client.Discovery.APIResource;
|
|
||||||
import io.kubernetes.client.custom.V1Patch;
|
|
||||||
import io.kubernetes.client.openapi.ApiException;
|
|
||||||
import io.kubernetes.client.openapi.models.V1Deployment;
|
|
||||||
import io.kubernetes.client.openapi.models.V1DeploymentList;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Optional;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A stub for pods (v1).
|
|
||||||
*/
|
|
||||||
public class K8sV1DeploymentStub
|
|
||||||
extends K8sGenericStub<V1Deployment, V1DeploymentList> {
|
|
||||||
|
|
||||||
/** 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<V1Deployment> 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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -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 <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.jdrupes.vmoperator.common;
|
|
||||||
|
|
||||||
import io.kubernetes.client.Discovery.APIResource;
|
|
||||||
import io.kubernetes.client.openapi.ApiException;
|
|
||||||
import io.kubernetes.client.openapi.models.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<V1Node, V1NodeList> {
|
|
||||||
|
|
||||||
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<K8sV1NodeStub> 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<V1Node> objectClass,
|
|
||||||
Class<V1NodeList> objectListClass, K8sClient client,
|
|
||||||
APIResource context, String name) {
|
|
||||||
return new K8sV1NodeStub(client, name);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -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 <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.jdrupes.vmoperator.common;
|
|
||||||
|
|
||||||
import io.kubernetes.client.Discovery.APIResource;
|
|
||||||
import io.kubernetes.client.openapi.ApiException;
|
|
||||||
import io.kubernetes.client.openapi.models.V1Pod;
|
|
||||||
import io.kubernetes.client.openapi.models.V1PodList;
|
|
||||||
import io.kubernetes.client.util.generic.options.ListOptions;
|
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A stub for pods (v1).
|
|
||||||
*/
|
|
||||||
public class K8sV1PodStub extends K8sGenericStub<V1Pod, V1PodList> {
|
|
||||||
|
|
||||||
/** 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<K8sV1PodStub> 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));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -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 <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.jdrupes.vmoperator.common;
|
|
||||||
|
|
||||||
import io.kubernetes.client.Discovery.APIResource;
|
|
||||||
import io.kubernetes.client.openapi.ApiException;
|
|
||||||
import io.kubernetes.client.openapi.models.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<V1PersistentVolumeClaim, V1PersistentVolumeClaimList> {
|
|
||||||
|
|
||||||
/** 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<K8sV1PvcStub> 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));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -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 <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.jdrupes.vmoperator.common;
|
|
||||||
|
|
||||||
import io.kubernetes.client.Discovery.APIResource;
|
|
||||||
import io.kubernetes.client.openapi.ApiException;
|
|
||||||
import io.kubernetes.client.openapi.models.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<V1Secret, V1SecretList> {
|
|
||||||
|
|
||||||
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<K8sV1SecretStub> list(K8sClient client,
|
|
||||||
String namespace, ListOptions options) throws ApiException {
|
|
||||||
return K8sGenericStub.list(V1Secret.class, V1SecretList.class, client,
|
|
||||||
CONTEXT, namespace, options, K8sV1SecretStub::new);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -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 <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.jdrupes.vmoperator.common;
|
|
||||||
|
|
||||||
import io.kubernetes.client.Discovery.APIResource;
|
|
||||||
import io.kubernetes.client.openapi.ApiException;
|
|
||||||
import io.kubernetes.client.openapi.models.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<V1Service, V1ServiceList> {
|
|
||||||
|
|
||||||
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<K8sV1ServiceStub> 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));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -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 <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.jdrupes.vmoperator.common;
|
|
||||||
|
|
||||||
import io.kubernetes.client.Discovery.APIResource;
|
|
||||||
import io.kubernetes.client.openapi.models.V1StatefulSet;
|
|
||||||
import io.kubernetes.client.openapi.models.V1StatefulSetList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A stub for stateful sets (v1).
|
|
||||||
*/
|
|
||||||
public class K8sV1StatefulSetStub
|
|
||||||
extends K8sGenericStub<V1StatefulSet, V1StatefulSetList> {
|
|
||||||
|
|
||||||
/** 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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -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 <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
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<String, Permission> 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<Permission> 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<Permission> 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<String, Object> spec() {
|
|
||||||
return model.getSpec();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get a value from the spec using {@link DataPath#get}.
|
|
||||||
*
|
|
||||||
* @param <T> the generic type
|
|
||||||
* @param selectors the selectors
|
|
||||||
* @return the value, if found
|
|
||||||
*/
|
|
||||||
public <T> Optional<T> fromSpec(Object... selectors) {
|
|
||||||
return DataPath.get(spec(), selectors);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The pools that this VM belongs to.
|
|
||||||
*
|
|
||||||
* @return the list
|
|
||||||
*/
|
|
||||||
public List<String> pools() {
|
|
||||||
return this.<List<String>> fromSpec("pools")
|
|
||||||
.orElse(Collections.emptyList());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get a value from the `spec().get("vm")` using {@link DataPath#get}.
|
|
||||||
*
|
|
||||||
* @param <T> the generic type
|
|
||||||
* @param selectors the selectors
|
|
||||||
* @return the value, if found
|
|
||||||
*/
|
|
||||||
public <T> Optional<T> fromVm(Object... selectors) {
|
|
||||||
return DataPath.get(spec(), "vm")
|
|
||||||
.flatMap(vm -> DataPath.get(vm, selectors));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the status.
|
|
||||||
*
|
|
||||||
* @return the status
|
|
||||||
*/
|
|
||||||
public Map<String, Object> status() {
|
|
||||||
return model.getStatus();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get a value from the status using {@link DataPath#get}.
|
|
||||||
*
|
|
||||||
* @param <T> the generic type
|
|
||||||
* @param selectors the selectors
|
|
||||||
* @return the value, if found
|
|
||||||
*/
|
|
||||||
public <T> Optional<T> fromStatus(Object... selectors) {
|
|
||||||
return DataPath.get(status(), selectors);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The assignment information.
|
|
||||||
*
|
|
||||||
* @return the optional
|
|
||||||
*/
|
|
||||||
public Optional<Assignment> assignment() {
|
|
||||||
return this.<Map<String, Object>> fromStatus(Status.ASSIGNMENT)
|
|
||||||
.filter(m -> !m.isEmpty()).map(a -> new Assignment(
|
|
||||||
a.get("pool").toString(), a.get("user").toString(),
|
|
||||||
Instant.parse(a.get("lastUsed").toString())));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return a condition from the status.
|
|
||||||
*
|
|
||||||
* @param name the condition's name
|
|
||||||
* @return the status, if the condition is defined
|
|
||||||
*/
|
|
||||||
public Optional<V1Condition> condition(String name) {
|
|
||||||
return this.<List<Map<String, Object>>> fromStatus("conditions")
|
|
||||||
.orElse(Collections.emptyList()).stream()
|
|
||||||
.filter(cond -> DataPath.get(cond, "type")
|
|
||||||
.map(name::equals).orElse(false))
|
|
||||||
.findFirst()
|
|
||||||
.map(cond -> objectMapper.convertValue(cond, V1Condition.class));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return a condition's status.
|
|
||||||
*
|
|
||||||
* @param name the condition's name
|
|
||||||
* @return the status, if the condition is defined
|
|
||||||
*/
|
|
||||||
public Optional<Boolean> conditionStatus(String name) {
|
|
||||||
return this.<List<Map<String, Object>>> fromStatus("conditions")
|
|
||||||
.orElse(Collections.emptyList()).stream()
|
|
||||||
.filter(cond -> DataPath.get(cond, "type")
|
|
||||||
.map(name::equals).orElse(false))
|
|
||||||
.findFirst().map(cond -> DataPath.get(cond, "status")
|
|
||||||
.map("True"::equals).orElse(false));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return true if the console is in use.
|
|
||||||
*
|
|
||||||
* @return true, if successful
|
|
||||||
*/
|
|
||||||
public boolean consoleConnected() {
|
|
||||||
return conditionStatus("ConsoleConnected").orElse(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the last known console user.
|
|
||||||
*
|
|
||||||
* @return the optional
|
|
||||||
*/
|
|
||||||
public Optional<String> consoleUser() {
|
|
||||||
return this.<String> fromStatus(Status.CONSOLE_USER);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set extra data (unknown to kubernetes).
|
|
||||||
* @return the VM definition
|
|
||||||
*/
|
|
||||||
/* default */ VmDefinition extra(VmExtraData extraData) {
|
|
||||||
this.extraData = extraData;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the extra data.
|
|
||||||
*
|
|
||||||
* @return the data
|
|
||||||
*/
|
|
||||||
public VmExtraData extra() {
|
|
||||||
return extraData;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the definition's name.
|
|
||||||
*
|
|
||||||
* @return the string
|
|
||||||
*/
|
|
||||||
public String name() {
|
|
||||||
return metadata().getName();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the definition's namespace.
|
|
||||||
*
|
|
||||||
* @return the string
|
|
||||||
*/
|
|
||||||
public String namespace() {
|
|
||||||
return metadata().getNamespace();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the requested VM state.
|
|
||||||
*
|
|
||||||
* @return the string
|
|
||||||
*/
|
|
||||||
public RequestedVmState vmState() {
|
|
||||||
return fromVm("state")
|
|
||||||
.map(s -> "Running".equals(s) ? RequestedVmState.RUNNING
|
|
||||||
: RequestedVmState.STOPPED)
|
|
||||||
.orElse(RequestedVmState.STOPPED);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Collect all permissions for the given user with the given roles.
|
|
||||||
* If permission "takeConsole" is granted, the result will also
|
|
||||||
* contain "accessConsole" to simplify checks.
|
|
||||||
*
|
|
||||||
* @param user the user
|
|
||||||
* @param roles the roles
|
|
||||||
* @return the sets the
|
|
||||||
*/
|
|
||||||
public Set<Permission> permissionsFor(String user,
|
|
||||||
Collection<String> roles) {
|
|
||||||
var result = this.<List<Map<String, Object>>> fromSpec("permissions")
|
|
||||||
.orElse(Collections.emptyList()).stream()
|
|
||||||
.filter(p -> DataPath.get(p, "user").map(u -> u.equals(user))
|
|
||||||
.orElse(false)
|
|
||||||
|| DataPath.get(p, "role").map(roles::contains).orElse(false))
|
|
||||||
.map(p -> DataPath.<List<String>> get(p, "may")
|
|
||||||
.orElse(Collections.emptyList()).stream())
|
|
||||||
.flatMap(Function.identity())
|
|
||||||
.map(Permission::parse).map(Set::stream)
|
|
||||||
.flatMap(Function.identity())
|
|
||||||
.collect(Collectors.toCollection(HashSet::new));
|
|
||||||
|
|
||||||
// Take console implies access console, simplify checks
|
|
||||||
if (result.contains(Permission.TAKE_CONSOLE)) {
|
|
||||||
result.add(Permission.ACCESS_CONSOLE);
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check if the console is accessible. Always returns `true` if
|
|
||||||
* the VM is running and the permissions allow taking over the
|
|
||||||
* console. Else, returns `true` if
|
|
||||||
*
|
|
||||||
* * the permissions allow access to the console and
|
|
||||||
*
|
|
||||||
* * the VM is running and
|
|
||||||
*
|
|
||||||
* * the console is currently unused or used by the given user and
|
|
||||||
*
|
|
||||||
* * if user login is requested, the given user is logged in.
|
|
||||||
*
|
|
||||||
* @param user the user
|
|
||||||
* @param permissions the permissions
|
|
||||||
* @return true, if successful
|
|
||||||
*/
|
|
||||||
@SuppressWarnings("PMD.SimplifyBooleanReturns")
|
|
||||||
public boolean consoleAccessible(String user, Set<Permission> permissions) {
|
|
||||||
// Basic checks
|
|
||||||
if (!conditionStatus(Condition.RUNNING).orElse(false)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (permissions.contains(Permission.TAKE_CONSOLE)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (!permissions.contains(Permission.ACCESS_CONSOLE)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the console is in use by another user, deny access
|
|
||||||
if (conditionStatus(Condition.CONSOLE_CONNECTED).orElse(false)
|
|
||||||
&& !consoleUser().map(cu -> cu.equals(user)).orElse(false)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If no login is requested, allow access, else check if user matches
|
|
||||||
if (condition(Condition.USER_LOGGED_IN).map(V1Condition::getReason)
|
|
||||||
.map(r -> Reason.NOT_REQUESTED.equals(r)).orElse(false)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return user.equals(status().get(Status.LOGGED_IN_USER));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the display password serial.
|
|
||||||
*
|
|
||||||
* @return the optional
|
|
||||||
*/
|
|
||||||
public Optional<Long> displayPasswordSerial() {
|
|
||||||
return this.<Number> fromStatus(Status.DISPLAY_PASSWORD_SERIAL)
|
|
||||||
.map(Number::longValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Hash code.
|
|
||||||
*
|
|
||||||
* @return the int
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public int hashCode() {
|
|
||||||
return Objects.hash(metadata().getNamespace(), metadata().getName());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Equals.
|
|
||||||
*
|
|
||||||
* @param obj the obj
|
|
||||||
* @return true, if successful
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public boolean equals(Object obj) {
|
|
||||||
if (this == obj) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (obj == null) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (getClass() != obj.getClass()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
VmDefinition other = (VmDefinition) obj;
|
|
||||||
return Objects.equals(metadata().getNamespace(),
|
|
||||||
other.metadata().getNamespace())
|
|
||||||
&& Objects.equals(metadata().getName(), other.metadata().getName());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The Class Model.
|
|
||||||
*/
|
|
||||||
public static class Model {
|
|
||||||
|
|
||||||
private Map<String, Object> spec;
|
|
||||||
private Map<String, Object> status;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the spec.
|
|
||||||
*
|
|
||||||
* @return the spec
|
|
||||||
*/
|
|
||||||
public Map<String, Object> getSpec() {
|
|
||||||
return spec;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the spec.
|
|
||||||
*
|
|
||||||
* @param spec the spec to set
|
|
||||||
*/
|
|
||||||
public void setSpec(Map<String, Object> spec) {
|
|
||||||
this.spec = spec;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the status.
|
|
||||||
*
|
|
||||||
* @return the status
|
|
||||||
*/
|
|
||||||
public Map<String, Object> getStatus() {
|
|
||||||
return status;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the status.
|
|
||||||
*
|
|
||||||
* @param status the status to set
|
|
||||||
*/
|
|
||||||
public void setStatus(Map<String, Object> status) {
|
|
||||||
this.status = status;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -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 <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.jdrupes.vmoperator.common;
|
|
||||||
|
|
||||||
import io.kubernetes.client.Discovery.APIResource;
|
|
||||||
import io.kubernetes.client.apimachinery.GroupVersionKind;
|
|
||||||
import io.kubernetes.client.openapi.ApiException;
|
|
||||||
import 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 VmDefinitionStub
|
|
||||||
extends K8sDynamicStubBase<VmDefinition, VmDefinitions> {
|
|
||||||
|
|
||||||
private static DynamicTypeAdapterFactory<VmDefinition,
|
|
||||||
VmDefinitions> taf = new VmDefintionModelTypeAdapterFactory();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Instantiates a new stub for VM defintions.
|
|
||||||
*
|
|
||||||
* @param client the client
|
|
||||||
* @param context the context
|
|
||||||
* @param namespace the namespace
|
|
||||||
* @param name the name
|
|
||||||
*/
|
|
||||||
public VmDefinitionStub(K8sClient client, APIResource context,
|
|
||||||
String namespace, String name) {
|
|
||||||
super(VmDefinition.class, VmDefinitions.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 VmDefinitionStub get(K8sClient client,
|
|
||||||
GroupVersionKind gvk, String namespace, String name)
|
|
||||||
throws ApiException {
|
|
||||||
return new VmDefinitionStub(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 VmDefinitionStub get(K8sClient client,
|
|
||||||
APIResource context, String namespace, String name) {
|
|
||||||
return new VmDefinitionStub(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 VmDefinitionStub createFromYaml(K8sClient client,
|
|
||||||
APIResource context, Reader yaml) throws ApiException {
|
|
||||||
var model = new VmDefinition(client.getJSON().getGson(),
|
|
||||||
K8s.yamlToJson(client, yaml));
|
|
||||||
return K8sGenericStub.create(VmDefinition.class,
|
|
||||||
VmDefinitions.class, client, context, model,
|
|
||||||
(c, ns, n) -> new VmDefinitionStub(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<VmDefinitionStub> list(K8sClient client,
|
|
||||||
APIResource context, String namespace, ListOptions options)
|
|
||||||
throws ApiException {
|
|
||||||
return K8sGenericStub.list(VmDefinition.class,
|
|
||||||
VmDefinitions.class, client, context, namespace, options,
|
|
||||||
(c, ns, n) -> new VmDefinitionStub(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<VmDefinitionStub> list(K8sClient client,
|
|
||||||
APIResource context, String namespace)
|
|
||||||
throws ApiException {
|
|
||||||
return list(client, context, namespace, new ListOptions());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A factory for creating VmDefinitionModel(s) objects.
|
|
||||||
*/
|
|
||||||
public static class VmDefintionModelTypeAdapterFactory extends
|
|
||||||
DynamicTypeAdapterFactory<VmDefinition, VmDefinitions> {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Instantiates a new dynamic model type adapter factory.
|
|
||||||
*/
|
|
||||||
public VmDefintionModelTypeAdapterFactory() {
|
|
||||||
super(VmDefinition.class, VmDefinitions.class);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue