Feature/status (#5)
Update CR's status (condition Running, current cpu and ram allocation).
This commit is contained in:
commit
d8882359a6
36 changed files with 776 additions and 115 deletions
46
.project
46
.project
|
|
@ -2,37 +2,41 @@
|
||||||
<projectDescription>
|
<projectDescription>
|
||||||
<name>VM-Operator</name>
|
<name>VM-Operator</name>
|
||||||
<comment></comment>
|
<comment></comment>
|
||||||
<projects/>
|
<projects>
|
||||||
|
</projects>
|
||||||
|
<buildSpec>
|
||||||
|
<buildCommand>
|
||||||
|
<name>org.eclipse.jdt.core.javabuilder</name>
|
||||||
|
<arguments>
|
||||||
|
</arguments>
|
||||||
|
</buildCommand>
|
||||||
|
<buildCommand>
|
||||||
|
<name>org.eclipse.buildship.core.gradleprojectbuilder</name>
|
||||||
|
<arguments>
|
||||||
|
</arguments>
|
||||||
|
</buildCommand>
|
||||||
|
<buildCommand>
|
||||||
|
<name>net.sf.eclipsecs.core.CheckstyleBuilder</name>
|
||||||
|
<arguments>
|
||||||
|
</arguments>
|
||||||
|
</buildCommand>
|
||||||
|
<buildCommand>
|
||||||
|
<name>ch.acanda.eclipse.pmd.builder.PMDBuilder</name>
|
||||||
|
<arguments>
|
||||||
|
</arguments>
|
||||||
|
</buildCommand>
|
||||||
|
</buildSpec>
|
||||||
<natures>
|
<natures>
|
||||||
<nature>org.eclipse.jdt.core.javanature</nature>
|
<nature>org.eclipse.jdt.core.javanature</nature>
|
||||||
<nature>org.eclipse.buildship.core.gradleprojectnature</nature>
|
<nature>org.eclipse.buildship.core.gradleprojectnature</nature>
|
||||||
<nature>net.sf.eclipsecs.core.CheckstyleNature</nature>
|
<nature>net.sf.eclipsecs.core.CheckstyleNature</nature>
|
||||||
<nature>ch.acanda.eclipse.pmd.builder.PMDNature</nature>
|
<nature>ch.acanda.eclipse.pmd.builder.PMDNature</nature>
|
||||||
</natures>
|
</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>
|
<filteredResources>
|
||||||
<filter>
|
<filter>
|
||||||
<id>1</id>
|
<id>1</id>
|
||||||
|
<name></name>
|
||||||
<type>30</type>
|
<type>30</type>
|
||||||
<name/>
|
|
||||||
<matcher>
|
<matcher>
|
||||||
<id>org.eclipse.core.resources.regexFilterMatcher</id>
|
<id>org.eclipse.core.resources.regexFilterMatcher</id>
|
||||||
<arguments>node_modules|\.git|__CREATED_BY_JAVA_LANGUAGE_SERVER__</arguments>
|
<arguments>node_modules|\.git|__CREATED_BY_JAVA_LANGUAGE_SERVER__</arguments>
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ 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=/usr/lib/jvm/java-1.8.0-openjdk-1.8.0.362.b09-2.fc37.x86_64
|
java.home=
|
||||||
jvm.arguments=
|
jvm.arguments=
|
||||||
offline.mode=false
|
offline.mode=false
|
||||||
override.workspace.settings=true
|
override.workspace.settings=true
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,20 @@
|
||||||
<projects>
|
<projects>
|
||||||
</projects>
|
</projects>
|
||||||
<buildSpec>
|
<buildSpec>
|
||||||
|
<buildCommand>
|
||||||
|
<name>org.eclipse.jdt.core.javabuilder</name>
|
||||||
|
<arguments>
|
||||||
|
</arguments>
|
||||||
|
</buildCommand>
|
||||||
|
<buildCommand>
|
||||||
|
<name>org.eclipse.buildship.core.gradleprojectbuilder</name>
|
||||||
|
<arguments>
|
||||||
|
</arguments>
|
||||||
|
</buildCommand>
|
||||||
</buildSpec>
|
</buildSpec>
|
||||||
<natures>
|
<natures>
|
||||||
|
<nature>org.eclipse.jdt.groovy.core.groovyNature</nature>
|
||||||
|
<nature>org.eclipse.jdt.core.javanature</nature>
|
||||||
|
<nature>org.eclipse.buildship.core.gradleprojectnature</nature>
|
||||||
</natures>
|
</natures>
|
||||||
</projectDescription>
|
</projectDescription>
|
||||||
|
|
|
||||||
2
buildSrc/.settings/org.eclipse.core.resources.prefs
Normal file
2
buildSrc/.settings/org.eclipse.core.resources.prefs
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
eclipse.preferences.version=1
|
||||||
|
encoding/<project>=UTF-8
|
||||||
2
buildSrc/.settings/org.eclipse.core.runtime.prefs
Normal file
2
buildSrc/.settings/org.eclipse.core.runtime.prefs
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
eclipse.preferences.version=1
|
||||||
|
line.separator=\n
|
||||||
|
|
@ -1,24 +1,22 @@
|
||||||
#
|
|
||||||
#Sun May 07 20:03:15 CEST 2023
|
|
||||||
org.eclipse.jdt.core.compiler.codegen.methodParameters=do not generate
|
|
||||||
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
|
eclipse.preferences.version=1
|
||||||
|
org.eclipse.jdt.core.compiler.annotation.nonnull=org.eclipse.jdt.annotation.NonNull
|
||||||
|
org.eclipse.jdt.core.compiler.annotation.nullable=org.eclipse.jdt.annotation.Nullable
|
||||||
|
org.eclipse.jdt.core.compiler.annotation.nullanalysis=disabled
|
||||||
|
org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
|
||||||
|
org.eclipse.jdt.core.compiler.codegen.methodParameters=do not generate
|
||||||
|
org.eclipse.jdt.core.compiler.codegen.targetPlatform=21
|
||||||
|
org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
|
||||||
|
org.eclipse.jdt.core.compiler.compliance=21
|
||||||
|
org.eclipse.jdt.core.compiler.debug.lineNumber=generate
|
||||||
|
org.eclipse.jdt.core.compiler.debug.localVariable=generate
|
||||||
|
org.eclipse.jdt.core.compiler.debug.sourceFile=generate
|
||||||
|
org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
|
||||||
org.eclipse.jdt.core.compiler.problem.enablePreviewFeatures=disabled
|
org.eclipse.jdt.core.compiler.problem.enablePreviewFeatures=disabled
|
||||||
org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
|
org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
|
||||||
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.problem.nullAnnotationInferenceConflict=error
|
||||||
org.eclipse.jdt.core.compiler.codegen.targetPlatform=17
|
org.eclipse.jdt.core.compiler.problem.nullReference=warning
|
||||||
org.eclipse.jdt.core.compiler.problem.nullSpecViolation=error
|
org.eclipse.jdt.core.compiler.problem.nullSpecViolation=error
|
||||||
org.eclipse.jdt.core.compiler.problem.potentialNullReference=ignore
|
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.problem.reportPreviewFeatures=warning
|
||||||
org.eclipse.jdt.core.compiler.annotation.nullanalysis=disabled
|
org.eclipse.jdt.core.compiler.release=disabled
|
||||||
org.eclipse.jdt.core.compiler.debug.lineNumber=generate
|
org.eclipse.jdt.core.compiler.source=21
|
||||||
org.eclipse.jdt.core.compiler.compliance=17
|
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,9 @@ 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
|
||||||
|
id 'eclipse'
|
||||||
}
|
}
|
||||||
|
|
||||||
repositories {
|
repositories {
|
||||||
|
|
@ -30,10 +33,35 @@ sourceSets {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
eclipse {
|
||||||
java {
|
|
||||||
toolchain {
|
project {
|
||||||
languageVersion = JavaLanguageVersion.of(17)
|
file {
|
||||||
|
// closure executed after .project content is loaded from existing file
|
||||||
|
// and before gradle build information is merged
|
||||||
|
beforeMerged { project ->
|
||||||
|
project.natures.clear()
|
||||||
|
project.buildCommands.clear()
|
||||||
|
}
|
||||||
|
|
||||||
|
project.natures += 'org.eclipse.buildship.core.gradleprojectnature'
|
||||||
|
project.buildCommand 'org.eclipse.buildship.core.gradleprojectbuilder'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
classpath {
|
||||||
|
downloadJavadoc = true
|
||||||
|
downloadSources = true
|
||||||
|
}
|
||||||
|
|
||||||
|
jdt {
|
||||||
|
file {
|
||||||
|
withProperties { properties ->
|
||||||
|
def formatterPrefs = new Properties()
|
||||||
|
rootProject.file("gradle/org.eclipse.jdt.core.formatter.prefs")
|
||||||
|
.withInputStream { formatterPrefs.load(it) }
|
||||||
|
properties.putAll(formatterPrefs)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,8 @@ spec:
|
||||||
- name: v1
|
- name: v1
|
||||||
served: true
|
served: true
|
||||||
storage: true
|
storage: true
|
||||||
|
subresources:
|
||||||
|
status: {}
|
||||||
schema:
|
schema:
|
||||||
openAPIV3Schema:
|
openAPIV3Schema:
|
||||||
type: object
|
type: object
|
||||||
|
|
@ -1372,10 +1374,28 @@ spec:
|
||||||
- vm
|
- vm
|
||||||
status:
|
status:
|
||||||
type: object
|
type: object
|
||||||
|
default: {}
|
||||||
properties:
|
properties:
|
||||||
|
cpus:
|
||||||
|
description: >-
|
||||||
|
Number of CPUs currently in use.
|
||||||
|
type: integer
|
||||||
|
default: 0
|
||||||
|
ram:
|
||||||
|
description: >-
|
||||||
|
Amount of memory in use.
|
||||||
|
type: string
|
||||||
|
default: "0"
|
||||||
conditions:
|
conditions:
|
||||||
description: >-
|
description: >-
|
||||||
List of component conditions observed
|
List of component conditions observed
|
||||||
|
default:
|
||||||
|
- type: Running
|
||||||
|
status: "False"
|
||||||
|
observedGeneration: 1
|
||||||
|
lastTransitionTime: "1970-01-01T00:00:00Z"
|
||||||
|
reason: Creation
|
||||||
|
message: "Creation of CR"
|
||||||
type: array
|
type: array
|
||||||
items:
|
items:
|
||||||
type: object
|
type: object
|
||||||
|
|
@ -1383,6 +1403,12 @@ spec:
|
||||||
Information about the condition of a component. See
|
Information about the condition of a component. See
|
||||||
https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#spec-and-status
|
https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#spec-and-status
|
||||||
and https://github.com/kubernetes/apimachinery/blob/release-1.23/pkg/apis/meta/v1/types.go#L1432-L1492
|
and https://github.com/kubernetes/apimachinery/blob/release-1.23/pkg/apis/meta/v1/types.go#L1432-L1492
|
||||||
|
required:
|
||||||
|
- type
|
||||||
|
- status
|
||||||
|
- lastTransitionTime
|
||||||
|
- reason
|
||||||
|
- message
|
||||||
properties:
|
properties:
|
||||||
type:
|
type:
|
||||||
type: string
|
type: string
|
||||||
|
|
@ -1428,12 +1454,6 @@ spec:
|
||||||
Message is a human readable message indicating
|
Message is a human readable message indicating
|
||||||
details about the transition. This may be an empty string.
|
details about the transition. This may be an empty string.
|
||||||
default: ""
|
default: ""
|
||||||
required:
|
|
||||||
- type
|
|
||||||
- status
|
|
||||||
- lastTransitionTime
|
|
||||||
- reason
|
|
||||||
- message
|
|
||||||
# either Namespaced or Cluster
|
# either Namespaced or Cluster
|
||||||
scope: Namespaced
|
scope: Namespaced
|
||||||
names:
|
names:
|
||||||
|
|
|
||||||
|
|
@ -8,3 +8,6 @@ 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
|
||||||
|
- vmrunner-role.yaml
|
||||||
|
- vmrunner-service-account.yaml
|
||||||
|
- vmrunner-role-binding.yaml
|
||||||
|
|
|
||||||
13
deploy/vmrunner-role-binding.yaml
Normal file
13
deploy/vmrunner-role-binding.yaml
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
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
|
||||||
20
deploy/vmrunner-role.yaml
Normal file
20
deploy/vmrunner-role.yaml
Normal file
|
|
@ -0,0 +1,20 @@
|
||||||
|
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
|
||||||
|
- apiGroups:
|
||||||
|
- vmoperator.jdrupes.org
|
||||||
|
resources:
|
||||||
|
- vms/status
|
||||||
|
verbs:
|
||||||
|
- patch
|
||||||
6
deploy/vmrunner-service-account.yaml
Normal file
6
deploy/vmrunner-service-account.yaml
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
kind: ServiceAccount
|
||||||
|
apiVersion: v1
|
||||||
|
metadata:
|
||||||
|
name: vm-runner
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/name: vm-operator
|
||||||
|
|
@ -16,8 +16,6 @@ dependencies {
|
||||||
implementation project(':org.jdrupes.vmoperator.util')
|
implementation project(':org.jdrupes.vmoperator.util')
|
||||||
|
|
||||||
implementation 'commons-cli:commons-cli:1.5.0'
|
implementation 'commons-cli:commons-cli:1.5.0'
|
||||||
implementation 'org.freemarker:freemarker:[2.3.32,2.4)'
|
|
||||||
implementation 'io.kubernetes:client-java:[18.0.0,19)'
|
|
||||||
|
|
||||||
runtimeOnly 'com.electronwill.night-config:yaml:[3.6.7,3.7)'
|
runtimeOnly 'com.electronwill.night-config:yaml:[3.6.7,3.7)'
|
||||||
runtimeOnly 'org.slf4j:slf4j-jdk14:[2.0.7,3)'
|
runtimeOnly 'org.slf4j:slf4j-jdk14:[2.0.7,3)'
|
||||||
|
|
|
||||||
|
|
@ -140,6 +140,7 @@ spec:
|
||||||
<#if cr.spec.affinity??>
|
<#if cr.spec.affinity??>
|
||||||
affinity: ${ cr.spec.affinity.toString() }
|
affinity: ${ cr.spec.affinity.toString() }
|
||||||
</#if>
|
</#if>
|
||||||
|
serviceAccountName: vm-runner
|
||||||
volumeClaimTemplates:
|
volumeClaimTemplates:
|
||||||
- metadata:
|
- metadata:
|
||||||
namespace: ${ cr.metadata.namespace.asString }
|
namespace: ${ cr.metadata.namespace.asString }
|
||||||
|
|
|
||||||
|
|
@ -32,6 +32,9 @@ import java.io.IOException;
|
||||||
import java.io.StringWriter;
|
import java.io.StringWriter;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
import static org.jdrupes.vmoperator.manager.Constants.APP_NAME;
|
||||||
|
import static org.jdrupes.vmoperator.manager.Constants.VM_OP_NAME;
|
||||||
|
import org.jdrupes.vmoperator.util.K8s;
|
||||||
import org.yaml.snakeyaml.LoaderOptions;
|
import org.yaml.snakeyaml.LoaderOptions;
|
||||||
import org.yaml.snakeyaml.Yaml;
|
import org.yaml.snakeyaml.Yaml;
|
||||||
import org.yaml.snakeyaml.constructor.SafeConstructor;
|
import org.yaml.snakeyaml.constructor.SafeConstructor;
|
||||||
|
|
@ -98,8 +101,8 @@ import org.yaml.snakeyaml.constructor.SafeConstructor;
|
||||||
DynamicKubernetesObject newCm) {
|
DynamicKubernetesObject newCm) {
|
||||||
ListOptions listOpts = new ListOptions();
|
ListOptions listOpts = new ListOptions();
|
||||||
listOpts.setLabelSelector(
|
listOpts.setLabelSelector(
|
||||||
"app.kubernetes.io/managed-by=" + Constants.VM_OP_NAME + ","
|
"app.kubernetes.io/managed-by=" + VM_OP_NAME + ","
|
||||||
+ "app.kubernetes.io/name=" + Constants.APP_NAME);
|
+ "app.kubernetes.io/name=" + APP_NAME);
|
||||||
// Get pod, selected by label
|
// Get pod, selected by label
|
||||||
var podApi = new DynamicKubernetesApi("", "v1", "pods", client);
|
var podApi = new DynamicKubernetesApi("", "v1", "pods", client);
|
||||||
var pods = podApi
|
var pods = podApi
|
||||||
|
|
|
||||||
|
|
@ -21,16 +21,7 @@ package org.jdrupes.vmoperator.manager;
|
||||||
/**
|
/**
|
||||||
* Some constants.
|
* Some constants.
|
||||||
*/
|
*/
|
||||||
public class Constants {
|
public class Constants extends org.jdrupes.vmoperator.util.Constants {
|
||||||
|
|
||||||
/** The Constant VM_OP_NAME. */
|
|
||||||
public static final String VM_OP_NAME = "vm-operator";
|
|
||||||
|
|
||||||
/** The Constant VM_OP_GROUP. */
|
|
||||||
public static final String VM_OP_GROUP = "vmoperator.jdrupes.org";
|
|
||||||
|
|
||||||
/** The Constant VM_OP_KIND_VM. */
|
|
||||||
public static final String VM_OP_KIND_VM = "VirtualMachine";
|
|
||||||
|
|
||||||
/** The Constant APP_NAME. */
|
/** The Constant APP_NAME. */
|
||||||
public static final String APP_NAME = "vm-runner";
|
public static final String APP_NAME = "vm-runner";
|
||||||
|
|
|
||||||
|
|
@ -21,9 +21,11 @@ package org.jdrupes.vmoperator.manager;
|
||||||
import io.kubernetes.client.openapi.ApiException;
|
import io.kubernetes.client.openapi.ApiException;
|
||||||
import io.kubernetes.client.openapi.Configuration;
|
import io.kubernetes.client.openapi.Configuration;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.util.logging.Level;
|
||||||
import org.jgrapes.core.Channel;
|
import org.jgrapes.core.Channel;
|
||||||
import org.jgrapes.core.Component;
|
import org.jgrapes.core.Component;
|
||||||
import org.jgrapes.core.annotation.Handler;
|
import org.jgrapes.core.annotation.Handler;
|
||||||
|
import org.jgrapes.core.events.HandlingError;
|
||||||
import org.jgrapes.core.events.Start;
|
import org.jgrapes.core.events.Start;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -74,6 +76,20 @@ public class Controller extends Component {
|
||||||
attach(new Reconciler(channel()));
|
attach(new Reconciler(channel()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Special handling of {@link ApiException} thrown by handlers.
|
||||||
|
*
|
||||||
|
* @param event the event
|
||||||
|
*/
|
||||||
|
@Handler(channels = Channel.class)
|
||||||
|
public void onHandlingError(HandlingError event) {
|
||||||
|
if (event.throwable() instanceof ApiException exc) {
|
||||||
|
logger.log(Level.WARNING, exc,
|
||||||
|
() -> "Problem accessing kubernetes: " + exc.getResponseBody());
|
||||||
|
event.stop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handle the start event. Has higher priority because it configures
|
* Handle the start event. Has higher priority because it configures
|
||||||
* the default Kubernetes client.
|
* the default Kubernetes client.
|
||||||
|
|
|
||||||
|
|
@ -33,6 +33,8 @@ import java.util.Collections;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
import org.jdrupes.vmoperator.util.GsonPtr;
|
||||||
|
import org.jdrupes.vmoperator.util.K8s;
|
||||||
import org.yaml.snakeyaml.LoaderOptions;
|
import org.yaml.snakeyaml.LoaderOptions;
|
||||||
import org.yaml.snakeyaml.Yaml;
|
import org.yaml.snakeyaml.Yaml;
|
||||||
import org.yaml.snakeyaml.constructor.SafeConstructor;
|
import org.yaml.snakeyaml.constructor.SafeConstructor;
|
||||||
|
|
|
||||||
|
|
@ -32,9 +32,11 @@ import org.apache.commons.cli.Option;
|
||||||
import org.apache.commons.cli.Options;
|
import org.apache.commons.cli.Options;
|
||||||
import static org.jdrupes.vmoperator.manager.Constants.VM_OP_NAME;
|
import static org.jdrupes.vmoperator.manager.Constants.VM_OP_NAME;
|
||||||
import org.jdrupes.vmoperator.util.FsdUtils;
|
import org.jdrupes.vmoperator.util.FsdUtils;
|
||||||
|
import org.jgrapes.core.Channel;
|
||||||
import org.jgrapes.core.Component;
|
import org.jgrapes.core.Component;
|
||||||
import org.jgrapes.core.Components;
|
import org.jgrapes.core.Components;
|
||||||
import org.jgrapes.core.annotation.Handler;
|
import org.jgrapes.core.annotation.Handler;
|
||||||
|
import org.jgrapes.core.events.HandlingError;
|
||||||
import org.jgrapes.core.events.Stop;
|
import org.jgrapes.core.events.Stop;
|
||||||
import org.jgrapes.io.NioDispatcher;
|
import org.jgrapes.io.NioDispatcher;
|
||||||
import org.jgrapes.util.FileSystemWatcher;
|
import org.jgrapes.util.FileSystemWatcher;
|
||||||
|
|
@ -95,6 +97,20 @@ public class Manager extends Component {
|
||||||
fire(new WatchFile(config.toPath()));
|
fire(new WatchFile(config.toPath()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Log the exception when a handling error is reported.
|
||||||
|
*
|
||||||
|
* @param event the event
|
||||||
|
*/
|
||||||
|
@Handler(channels = Channel.class, priority = -10_000)
|
||||||
|
@SuppressWarnings("PMD.GuardLogStatement")
|
||||||
|
public void onHandlingError(HandlingError event) {
|
||||||
|
logger.log(Level.WARNING, event.throwable(),
|
||||||
|
() -> "Problem invoking handler with " + event.event() + ": "
|
||||||
|
+ event.message());
|
||||||
|
event.stop();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* On stop.
|
* On stop.
|
||||||
*
|
*
|
||||||
|
|
|
||||||
|
|
@ -47,6 +47,8 @@ import static org.jdrupes.vmoperator.manager.Constants.VM_OP_GROUP;
|
||||||
import org.jdrupes.vmoperator.manager.VmDefChanged.Type;
|
import org.jdrupes.vmoperator.manager.VmDefChanged.Type;
|
||||||
import org.jdrupes.vmoperator.util.Convertions;
|
import org.jdrupes.vmoperator.util.Convertions;
|
||||||
import org.jdrupes.vmoperator.util.ExtendedObjectWrapper;
|
import org.jdrupes.vmoperator.util.ExtendedObjectWrapper;
|
||||||
|
import org.jdrupes.vmoperator.util.GsonPtr;
|
||||||
|
import org.jdrupes.vmoperator.util.K8s;
|
||||||
import org.jgrapes.core.Channel;
|
import org.jgrapes.core.Channel;
|
||||||
import org.jgrapes.core.Component;
|
import org.jgrapes.core.Component;
|
||||||
import org.jgrapes.core.annotation.Handler;
|
import org.jgrapes.core.annotation.Handler;
|
||||||
|
|
|
||||||
|
|
@ -29,6 +29,8 @@ import java.io.IOException;
|
||||||
import java.io.StringWriter;
|
import java.io.StringWriter;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
import org.jdrupes.vmoperator.util.GsonPtr;
|
||||||
|
import org.jdrupes.vmoperator.util.K8s;
|
||||||
import org.yaml.snakeyaml.LoaderOptions;
|
import org.yaml.snakeyaml.LoaderOptions;
|
||||||
import org.yaml.snakeyaml.Yaml;
|
import org.yaml.snakeyaml.Yaml;
|
||||||
import org.yaml.snakeyaml.constructor.SafeConstructor;
|
import org.yaml.snakeyaml.constructor.SafeConstructor;
|
||||||
|
|
|
||||||
|
|
@ -44,8 +44,10 @@ import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
|
import static org.jdrupes.vmoperator.manager.Constants.APP_NAME;
|
||||||
import static org.jdrupes.vmoperator.manager.Constants.VM_OP_GROUP;
|
import static org.jdrupes.vmoperator.manager.Constants.VM_OP_GROUP;
|
||||||
import static org.jdrupes.vmoperator.manager.Constants.VM_OP_KIND_VM;
|
import static org.jdrupes.vmoperator.manager.Constants.VM_OP_KIND_VM;
|
||||||
|
import static org.jdrupes.vmoperator.manager.Constants.VM_OP_NAME;
|
||||||
import org.jdrupes.vmoperator.manager.VmDefChanged.Type;
|
import org.jdrupes.vmoperator.manager.VmDefChanged.Type;
|
||||||
import org.jgrapes.core.Channel;
|
import org.jgrapes.core.Channel;
|
||||||
import org.jgrapes.core.Component;
|
import org.jgrapes.core.Component;
|
||||||
|
|
@ -132,7 +134,7 @@ public class VmWatcher extends Component {
|
||||||
for (var version : vmOpApiVersions) {
|
for (var version : vmOpApiVersions) {
|
||||||
coa.getAPIResources(VM_OP_GROUP, version)
|
coa.getAPIResources(VM_OP_GROUP, version)
|
||||||
.getResources().stream()
|
.getResources().stream()
|
||||||
.filter(r -> Constants.VM_OP_KIND_VM.equals(r.getKind()))
|
.filter(r -> VM_OP_KIND_VM.equals(r.getKind()))
|
||||||
.findFirst()
|
.findFirst()
|
||||||
.ifPresent(crd -> watchVmDefs(crd, version));
|
.ifPresent(crd -> watchVmDefs(crd, version));
|
||||||
}
|
}
|
||||||
|
|
@ -148,15 +150,15 @@ public class VmWatcher extends Component {
|
||||||
// Get all known CR instances.
|
// Get all known CR instances.
|
||||||
coa.getAPIResources(VM_OP_GROUP, version)
|
coa.getAPIResources(VM_OP_GROUP, version)
|
||||||
.getResources().stream()
|
.getResources().stream()
|
||||||
.filter(r -> Constants.VM_OP_KIND_VM.equals(r.getKind()))
|
.filter(r -> VM_OP_KIND_VM.equals(r.getKind()))
|
||||||
.findFirst()
|
.findFirst()
|
||||||
.ifPresent(crd -> known.addAll(getKnown(client, crd, version)));
|
.ifPresent(crd -> known.addAll(getKnown(client, crd, version)));
|
||||||
}
|
}
|
||||||
|
|
||||||
ListOptions opts = new ListOptions();
|
ListOptions opts = new ListOptions();
|
||||||
opts.setLabelSelector(
|
opts.setLabelSelector(
|
||||||
"app.kubernetes.io/managed-by=" + Constants.VM_OP_NAME + ","
|
"app.kubernetes.io/managed-by=" + VM_OP_NAME + ","
|
||||||
+ "app.kubernetes.io/name=" + Constants.APP_NAME);
|
+ "app.kubernetes.io/name=" + APP_NAME);
|
||||||
for (String resource : List.of("apps/v1/statefulsets",
|
for (String resource : List.of("apps/v1/statefulsets",
|
||||||
"v1/configmaps", "v1/secrets")) {
|
"v1/configmaps", "v1/secrets")) {
|
||||||
var resParts = new LinkedList<>(List.of(resource.split("/")));
|
var resParts = new LinkedList<>(List.of(resource.split("/")));
|
||||||
|
|
|
||||||
|
|
@ -16,8 +16,9 @@ dependencies {
|
||||||
implementation project(':org.jdrupes.vmoperator.util')
|
implementation project(':org.jdrupes.vmoperator.util')
|
||||||
|
|
||||||
implementation 'commons-cli:commons-cli:1.5.0'
|
implementation 'commons-cli:commons-cli:1.5.0'
|
||||||
implementation 'org.freemarker:freemarker:[2.3.32,2.4)'
|
|
||||||
implementation 'com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:[2.15.1,3]'
|
implementation 'com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:[2.15.1,3]'
|
||||||
|
|
||||||
|
runtimeOnly 'org.slf4j:slf4j-jdk14:[2.0.7,3)'
|
||||||
}
|
}
|
||||||
|
|
||||||
application {
|
application {
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,11 @@
|
||||||
# the first time. Subsequent starts use the copy unless this option is set.
|
# the first time. Subsequent starts use the copy unless this option is set.
|
||||||
# "updateTemplate": false
|
# "updateTemplate": false
|
||||||
|
|
||||||
|
# The namespace that this runner runs in. Usually obtained from
|
||||||
|
# /var/run/secrets/kubernetes.io/serviceaccount/namespace. Should only
|
||||||
|
# be set when starting the runner during development e.g. from the IDE.
|
||||||
|
# "namespace": ...
|
||||||
|
|
||||||
# Define the VM (required)
|
# Define the VM (required)
|
||||||
"vm":
|
"vm":
|
||||||
# The VM's name (required)
|
# The VM's name (required)
|
||||||
|
|
|
||||||
|
|
@ -19,8 +19,8 @@
|
||||||
package org.jdrupes.vmoperator.runner.qemu;
|
package org.jdrupes.vmoperator.runner.qemu;
|
||||||
|
|
||||||
import com.fasterxml.jackson.databind.node.ObjectNode;
|
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
@ -81,34 +81,28 @@ public class CpuController extends Component {
|
||||||
/**
|
/**
|
||||||
* On monitor result.
|
* On monitor result.
|
||||||
*
|
*
|
||||||
* @param result the result
|
* @param event the result
|
||||||
*/
|
*/
|
||||||
@Handler
|
@Handler
|
||||||
public void onHotpluggableCpuStatus(HotpluggableCpuStatus result) {
|
public void onHotpluggableCpuStatus(HotpluggableCpuStatus event) {
|
||||||
if (!result.successful()) {
|
if (!event.successful()) {
|
||||||
logger.warning(() -> "Failed to get hotpluggable CPU status "
|
logger.warning(() -> "Failed to get hotpluggable CPU status "
|
||||||
+ "(won't adjust number of CPUs.): " + result.errorMessage());
|
+ "(won't adjust number of CPUs.): " + event.errorMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sort
|
|
||||||
List<ObjectNode> used = new ArrayList<>();
|
|
||||||
List<ObjectNode> unused = new ArrayList<>();
|
|
||||||
for (var itr = result.values().iterator(); itr.hasNext();) {
|
|
||||||
ObjectNode cpu = (ObjectNode) itr.next();
|
|
||||||
if (cpu.has("qom-path")) {
|
|
||||||
used.add(cpu);
|
|
||||||
} else {
|
|
||||||
unused.add(cpu);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
currentCpus = used.size();
|
|
||||||
if (desiredCpus == null) {
|
if (desiredCpus == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// Process
|
// Process
|
||||||
int diff = used.size() - desiredCpus;
|
currentCpus = event.usedCpus().size();
|
||||||
diff = addCpus(used, unused, diff);
|
int diff = currentCpus - desiredCpus;
|
||||||
deleteCpus(used, diff);
|
if (diff == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
diff = addCpus(event.usedCpus(), event.unusedCpus(), diff);
|
||||||
|
removeCpus(event.usedCpus(), diff);
|
||||||
|
|
||||||
|
// Report result
|
||||||
|
fire(new MonitorCommand(new QmpQueryHotpluggableCpus()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("PMD.AvoidInstantiatingObjectsInLoops")
|
@SuppressWarnings("PMD.AvoidInstantiatingObjectsInLoops")
|
||||||
|
|
@ -123,22 +117,24 @@ public class CpuController extends Component {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
int nextId = 1;
|
int nextId = 1;
|
||||||
while (diff < 0 && !unused.isEmpty()) {
|
List<ObjectNode> remaining = new LinkedList<>(unused);
|
||||||
|
while (diff < 0 && !remaining.isEmpty()) {
|
||||||
String id;
|
String id;
|
||||||
do {
|
do {
|
||||||
id = "cpu-" + nextId++;
|
id = "cpu-" + nextId++;
|
||||||
} while (usedIds.contains(id));
|
} while (usedIds.contains(id));
|
||||||
fire(new MonitorCommand(new QmpAddCpu(unused.get(0), id)));
|
fire(new MonitorCommand(new QmpAddCpu(remaining.get(0), id)));
|
||||||
unused.remove(0);
|
remaining.remove(0);
|
||||||
diff += 1;
|
diff += 1;
|
||||||
}
|
}
|
||||||
return diff;
|
return diff;
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("PMD.AvoidInstantiatingObjectsInLoops")
|
@SuppressWarnings("PMD.AvoidInstantiatingObjectsInLoops")
|
||||||
private int deleteCpus(List<ObjectNode> used, int diff) {
|
private int removeCpus(List<ObjectNode> used, int diff) {
|
||||||
while (diff > 0 && !used.isEmpty()) {
|
List<ObjectNode> removable = new LinkedList<>(used);
|
||||||
ObjectNode cpu = used.remove(0);
|
while (diff > 0 && !removable.isEmpty()) {
|
||||||
|
ObjectNode cpu = removable.remove(0);
|
||||||
String qomPath = cpu.get("qom-path").asText();
|
String qomPath = cpu.get("qom-path").asText();
|
||||||
if (!qomPath.startsWith("/machine/peripheral/cpu-")) {
|
if (!qomPath.startsWith("/machine/peripheral/cpu-")) {
|
||||||
continue;
|
continue;
|
||||||
|
|
|
||||||
|
|
@ -58,14 +58,17 @@ import org.jdrupes.vmoperator.runner.qemu.events.RunnerStateChange;
|
||||||
import org.jdrupes.vmoperator.runner.qemu.events.RunnerStateChange.State;
|
import org.jdrupes.vmoperator.runner.qemu.events.RunnerStateChange.State;
|
||||||
import org.jdrupes.vmoperator.util.ExtendedObjectWrapper;
|
import org.jdrupes.vmoperator.util.ExtendedObjectWrapper;
|
||||||
import org.jdrupes.vmoperator.util.FsdUtils;
|
import org.jdrupes.vmoperator.util.FsdUtils;
|
||||||
|
import org.jgrapes.core.Channel;
|
||||||
import org.jgrapes.core.Component;
|
import org.jgrapes.core.Component;
|
||||||
import org.jgrapes.core.Components;
|
import org.jgrapes.core.Components;
|
||||||
import org.jgrapes.core.EventPipeline;
|
import org.jgrapes.core.EventPipeline;
|
||||||
import org.jgrapes.core.TypedIdKey;
|
import org.jgrapes.core.TypedIdKey;
|
||||||
import org.jgrapes.core.annotation.Handler;
|
import org.jgrapes.core.annotation.Handler;
|
||||||
|
import org.jgrapes.core.events.HandlingError;
|
||||||
import org.jgrapes.core.events.Start;
|
import org.jgrapes.core.events.Start;
|
||||||
import org.jgrapes.core.events.Started;
|
import org.jgrapes.core.events.Started;
|
||||||
import org.jgrapes.core.events.Stop;
|
import org.jgrapes.core.events.Stop;
|
||||||
|
import org.jgrapes.core.internal.EventProcessor;
|
||||||
import org.jgrapes.io.NioDispatcher;
|
import org.jgrapes.io.NioDispatcher;
|
||||||
import org.jgrapes.io.events.Input;
|
import org.jgrapes.io.events.Input;
|
||||||
import org.jgrapes.io.events.ProcessExited;
|
import org.jgrapes.io.events.ProcessExited;
|
||||||
|
|
@ -90,6 +93,13 @@ import org.jgrapes.util.events.WatchFile;
|
||||||
*
|
*
|
||||||
* 
|
* 
|
||||||
*
|
*
|
||||||
|
* The {@link Runner} associates an {@link EventProcessor} with the
|
||||||
|
* {@link Start} event. This "runner event processor" must be used
|
||||||
|
* for all events related to the application level function. Components
|
||||||
|
* that handle events from other sources (and thus event processors)
|
||||||
|
* must fire any resulting events on the runner event processor in order
|
||||||
|
* to maintain synchronization.
|
||||||
|
*
|
||||||
* @startuml RunnerStates.svg
|
* @startuml RunnerStates.svg
|
||||||
* [*] --> Initializing
|
* [*] --> Initializing
|
||||||
* Initializing -> Initializing: InitialConfiguration/configure Runner
|
* Initializing -> Initializing: InitialConfiguration/configure Runner
|
||||||
|
|
@ -149,8 +159,13 @@ import org.jgrapes.util.events.WatchFile;
|
||||||
* error --> terminate
|
* error --> terminate
|
||||||
* StartingProcess --> terminate: ProcessExited
|
* StartingProcess --> terminate: ProcessExited
|
||||||
*
|
*
|
||||||
|
* state Stopped {
|
||||||
|
* state stopped <<entryPoint>>
|
||||||
*
|
*
|
||||||
* terminated --> [*]
|
* stopped --> [*]
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* terminated --> stopped
|
||||||
*
|
*
|
||||||
* @enduml
|
* @enduml
|
||||||
*
|
*
|
||||||
|
|
@ -211,6 +226,7 @@ public class Runner extends Component {
|
||||||
attach(new ProcessManager(channel()));
|
attach(new ProcessManager(channel()));
|
||||||
attach(new SocketConnector(channel()));
|
attach(new SocketConnector(channel()));
|
||||||
attach(qemuMonitor = new QemuMonitor(channel()));
|
attach(qemuMonitor = new QemuMonitor(channel()));
|
||||||
|
attach(new StatusUpdater(channel()));
|
||||||
|
|
||||||
// Configuration store with file in /etc/opt (default)
|
// Configuration store with file in /etc/opt (default)
|
||||||
File config = new File(cmdLine.getOptionValue('c',
|
File config = new File(cmdLine.getOptionValue('c',
|
||||||
|
|
@ -224,6 +240,20 @@ public class Runner extends Component {
|
||||||
fire(new WatchFile(config.toPath()));
|
fire(new WatchFile(config.toPath()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Log the exception when a handling error is reported.
|
||||||
|
*
|
||||||
|
* @param event the event
|
||||||
|
*/
|
||||||
|
@Handler(channels = Channel.class, priority = -10_000)
|
||||||
|
@SuppressWarnings("PMD.GuardLogStatement")
|
||||||
|
public void onHandlingError(HandlingError event) {
|
||||||
|
logger.log(Level.WARNING, event.throwable(),
|
||||||
|
() -> "Problem invoking handler with " + event.event() + ": "
|
||||||
|
+ event.message());
|
||||||
|
event.stop();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* On configuration update.
|
* On configuration update.
|
||||||
*
|
*
|
||||||
|
|
@ -352,6 +382,11 @@ public class Runner extends Component {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Make sure to use thread specific client
|
||||||
|
// https://github.com/kubernetes-client/java/issues/100
|
||||||
|
io.kubernetes.client.openapi.Configuration.setDefaultApiClient(null);
|
||||||
|
|
||||||
|
// Prepare specific event pipeline to avoid concurrency.
|
||||||
rep = newEventPipeline();
|
rep = newEventPipeline();
|
||||||
event.setAssociated(EventPipeline.class, rep);
|
event.setAssociated(EventPipeline.class, rep);
|
||||||
try {
|
try {
|
||||||
|
|
@ -387,7 +422,8 @@ public class Runner extends Component {
|
||||||
@Handler
|
@Handler
|
||||||
public void onStarted(Started event) {
|
public void onStarted(Started event) {
|
||||||
state = State.STARTING;
|
state = State.STARTING;
|
||||||
fire(new RunnerStateChange(state));
|
rep.fire(new RunnerStateChange(state, "RunnerStarted",
|
||||||
|
"Runner has been started"));
|
||||||
// Start first process
|
// Start first process
|
||||||
if (config.vm.useTpm && swtpmDefinition != null) {
|
if (config.vm.useTpm && swtpmDefinition != null) {
|
||||||
startProcess(swtpmDefinition);
|
startProcess(swtpmDefinition);
|
||||||
|
|
@ -476,7 +512,7 @@ public class Runner extends Component {
|
||||||
*/
|
*/
|
||||||
@Handler
|
@Handler
|
||||||
public void onMonitorReady(MonitorReady event) {
|
public void onMonitorReady(MonitorReady event) {
|
||||||
fire(new RunnerConfigurationUpdate(config, state));
|
rep.fire(new RunnerConfigurationUpdate(config, state));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -489,7 +525,8 @@ public class Runner extends Component {
|
||||||
if (state == State.STARTING) {
|
if (state == State.STARTING) {
|
||||||
fire(new MonitorCommand(new QmpCont()));
|
fire(new MonitorCommand(new QmpCont()));
|
||||||
state = State.RUNNING;
|
state = State.RUNNING;
|
||||||
fire(new RunnerStateChange(state));
|
rep.fire(new RunnerStateChange(state, "VmStarted",
|
||||||
|
"Qemu has been configured and is continuing"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -524,9 +561,22 @@ public class Runner extends Component {
|
||||||
* @param event the event
|
* @param event the event
|
||||||
*/
|
*/
|
||||||
@Handler(priority = 10_000)
|
@Handler(priority = 10_000)
|
||||||
public void onStop(Stop event) {
|
public void onStopFirst(Stop event) {
|
||||||
state = State.TERMINATING;
|
state = State.TERMINATING;
|
||||||
fire(new RunnerStateChange(state));
|
rep.fire(new RunnerStateChange(state, "VmTerminating",
|
||||||
|
"The VM is being shut down"));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* On stop.
|
||||||
|
*
|
||||||
|
* @param event the event
|
||||||
|
*/
|
||||||
|
@Handler(priority = -10_000)
|
||||||
|
public void onStopLast(Stop event) {
|
||||||
|
state = State.STOPPED;
|
||||||
|
rep.fire(new RunnerStateChange(state, "VmStopped",
|
||||||
|
"The VM has been shut down"));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void shutdown() {
|
private void shutdown() {
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,304 @@
|
||||||
|
/*
|
||||||
|
* 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.runner.qemu;
|
||||||
|
|
||||||
|
import com.google.gson.JsonObject;
|
||||||
|
import io.kubernetes.client.custom.Quantity;
|
||||||
|
import io.kubernetes.client.custom.Quantity.Format;
|
||||||
|
import io.kubernetes.client.openapi.ApiException;
|
||||||
|
import io.kubernetes.client.openapi.apis.ApisApi;
|
||||||
|
import io.kubernetes.client.openapi.apis.CustomObjectsApi;
|
||||||
|
import io.kubernetes.client.openapi.models.V1APIGroup;
|
||||||
|
import io.kubernetes.client.openapi.models.V1GroupVersionForDiscovery;
|
||||||
|
import io.kubernetes.client.util.Config;
|
||||||
|
import io.kubernetes.client.util.generic.dynamic.DynamicKubernetesApi;
|
||||||
|
import io.kubernetes.client.util.generic.dynamic.DynamicKubernetesObject;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.time.Instant;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.logging.Level;
|
||||||
|
import org.jdrupes.vmoperator.runner.qemu.events.BalloonChangeEvent;
|
||||||
|
import org.jdrupes.vmoperator.runner.qemu.events.HotpluggableCpuStatus;
|
||||||
|
import org.jdrupes.vmoperator.runner.qemu.events.RunnerConfigurationUpdate;
|
||||||
|
import org.jdrupes.vmoperator.runner.qemu.events.RunnerStateChange;
|
||||||
|
import org.jdrupes.vmoperator.runner.qemu.events.RunnerStateChange.State;
|
||||||
|
import static org.jdrupes.vmoperator.util.Constants.VM_OP_GROUP;
|
||||||
|
import static org.jdrupes.vmoperator.util.Constants.VM_OP_KIND_VM;
|
||||||
|
import org.jdrupes.vmoperator.util.GsonPtr;
|
||||||
|
import org.jgrapes.core.Channel;
|
||||||
|
import org.jgrapes.core.Component;
|
||||||
|
import org.jgrapes.core.annotation.Handler;
|
||||||
|
import org.jgrapes.core.events.HandlingError;
|
||||||
|
import org.jgrapes.core.events.Start;
|
||||||
|
import org.jgrapes.util.events.ConfigurationUpdate;
|
||||||
|
import org.jgrapes.util.events.InitialConfiguration;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates the CR status.
|
||||||
|
*/
|
||||||
|
public class StatusUpdater extends Component {
|
||||||
|
|
||||||
|
private static final Set<State> RUNNING_STATES
|
||||||
|
= Set.of(State.RUNNING, State.TERMINATING);
|
||||||
|
|
||||||
|
private String namespace;
|
||||||
|
private String vmName;
|
||||||
|
private DynamicKubernetesApi vmCrApi;
|
||||||
|
private long observedGeneration;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Instantiates a new status updater.
|
||||||
|
*
|
||||||
|
* @param componentChannel the component channel
|
||||||
|
*/
|
||||||
|
public StatusUpdater(Channel componentChannel) {
|
||||||
|
super(componentChannel);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* On handling error.
|
||||||
|
*
|
||||||
|
* @param event the event
|
||||||
|
*/
|
||||||
|
@Handler(channels = Channel.class)
|
||||||
|
public void onHandlingError(HandlingError event) {
|
||||||
|
if (event.throwable() instanceof ApiException exc) {
|
||||||
|
logger.log(Level.WARNING, exc,
|
||||||
|
() -> "Problem accessing kubernetes: " + exc.getResponseBody());
|
||||||
|
event.stop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* On configuration update.
|
||||||
|
*
|
||||||
|
* @param event the event
|
||||||
|
*/
|
||||||
|
@Handler
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public void onConfigurationUpdate(ConfigurationUpdate event) {
|
||||||
|
event.structured("/Runner").ifPresent(c -> {
|
||||||
|
if (event instanceof InitialConfiguration) {
|
||||||
|
namespace = (String) c.get("namespace");
|
||||||
|
updateNamespace();
|
||||||
|
vmName = Optional.ofNullable((Map<String, String>) c.get("vm"))
|
||||||
|
.map(vm -> vm.get("name")).orElse(null);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateNamespace() {
|
||||||
|
if (namespace == null) {
|
||||||
|
var path = Path
|
||||||
|
.of("/var/run/secrets/kubernetes.io/serviceaccount/namespace");
|
||||||
|
if (Files.isReadable(path)) {
|
||||||
|
try {
|
||||||
|
namespace = Files.lines(path).findFirst().orElse(null);
|
||||||
|
} catch (IOException e) {
|
||||||
|
logger.log(Level.WARNING, e,
|
||||||
|
() -> "Cannot read namespace.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (namespace == null) {
|
||||||
|
logger.warning(() -> "Namespace is unknown, some functions"
|
||||||
|
+ " won't be available.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle the start event.
|
||||||
|
*
|
||||||
|
* @param event the event
|
||||||
|
* @throws IOException
|
||||||
|
* @throws ApiException
|
||||||
|
*/
|
||||||
|
@Handler
|
||||||
|
@SuppressWarnings({ "PMD.DataflowAnomalyAnalysis",
|
||||||
|
"PMD.AvoidInstantiatingObjectsInLoops", "PMD.AvoidDuplicateLiterals" })
|
||||||
|
public void onStart(Start event) throws IOException, ApiException {
|
||||||
|
var client = Config.defaultClient();
|
||||||
|
var apis = new ApisApi(client).getAPIVersions();
|
||||||
|
var crdVersions = apis.getGroups().stream()
|
||||||
|
.filter(g -> g.getName().equals(VM_OP_GROUP)).findFirst()
|
||||||
|
.map(V1APIGroup::getVersions).stream().flatMap(l -> l.stream())
|
||||||
|
.map(V1GroupVersionForDiscovery::getVersion).toList();
|
||||||
|
var coa = new CustomObjectsApi(client);
|
||||||
|
for (var crdVersion : crdVersions) {
|
||||||
|
var crdApiRes = coa.getAPIResources(VM_OP_GROUP,
|
||||||
|
crdVersion).getResources().stream()
|
||||||
|
.filter(r -> VM_OP_KIND_VM.equals(r.getKind())).findFirst();
|
||||||
|
if (crdApiRes.isEmpty()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
var crApi = new DynamicKubernetesApi(VM_OP_GROUP,
|
||||||
|
crdVersion, crdApiRes.get().getName(), client);
|
||||||
|
var vmCr = crApi.get(namespace, vmName).throwsApiException();
|
||||||
|
if (vmCr.isSuccess()) {
|
||||||
|
vmCrApi = crApi;
|
||||||
|
observedGeneration
|
||||||
|
= vmCr.getObject().getMetadata().getGeneration();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (vmCrApi == null) {
|
||||||
|
logger.warning(() -> "Cannot find VM's CR, status will not"
|
||||||
|
+ " be updated.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("PMD.AvoidDuplicateLiterals")
|
||||||
|
private JsonObject currentStatus(DynamicKubernetesObject vmCr) {
|
||||||
|
return vmCr.getRaw().getAsJsonObject("status").deepCopy();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* On runner configuration update.
|
||||||
|
*
|
||||||
|
* @param event the event
|
||||||
|
* @throws ApiException
|
||||||
|
*/
|
||||||
|
@Handler
|
||||||
|
public void onRunnerConfigurationUpdate(RunnerConfigurationUpdate event)
|
||||||
|
throws ApiException {
|
||||||
|
if (vmCrApi == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// A change of the runner configuration is typically caused
|
||||||
|
// by a new version of the CR. So we observe the new CR.
|
||||||
|
var vmCr = vmCrApi.get(namespace, vmName).throwsApiException()
|
||||||
|
.getObject();
|
||||||
|
if (vmCr.getMetadata().getGeneration() == observedGeneration) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
vmCrApi.updateStatus(vmCr, from -> {
|
||||||
|
JsonObject status = currentStatus(from);
|
||||||
|
status.getAsJsonArray("conditions").asList().stream()
|
||||||
|
.map(cond -> (JsonObject) cond).filter(cond -> "Running"
|
||||||
|
.equals(cond.get("type").getAsString()))
|
||||||
|
.forEach(cond -> cond.addProperty("observedGeneration",
|
||||||
|
from.getMetadata().getGeneration()));
|
||||||
|
return status;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* On runner state changed.
|
||||||
|
*
|
||||||
|
* @param event the event
|
||||||
|
* @throws ApiException
|
||||||
|
*/
|
||||||
|
@Handler
|
||||||
|
public void onRunnerStateChanged(RunnerStateChange event)
|
||||||
|
throws ApiException {
|
||||||
|
if (vmCrApi == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var vmCr = vmCrApi.get(namespace, vmName).throwsApiException()
|
||||||
|
.getObject();
|
||||||
|
vmCrApi.updateStatus(vmCr, from -> {
|
||||||
|
JsonObject status = currentStatus(from);
|
||||||
|
status.getAsJsonArray("conditions").asList().stream()
|
||||||
|
.map(cond -> (JsonObject) cond)
|
||||||
|
.forEach(cond -> {
|
||||||
|
if ("Running".equals(cond.get("type").getAsString())) {
|
||||||
|
updateRunningCondition(event, from, cond);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (event.state() == State.STARTING) {
|
||||||
|
status.addProperty("ram", GsonPtr.to(from.getRaw())
|
||||||
|
.getAsString("spec", "vm", "maximumRam").orElse("0"));
|
||||||
|
status.addProperty("cpus", 1);
|
||||||
|
} else if (event.state() == State.STOPPED) {
|
||||||
|
status.addProperty("ram", "0");
|
||||||
|
status.addProperty("cpus", 0);
|
||||||
|
}
|
||||||
|
return status;
|
||||||
|
}).throwsApiException();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateRunningCondition(RunnerStateChange event,
|
||||||
|
DynamicKubernetesObject from, JsonObject cond) {
|
||||||
|
boolean reportedRunning
|
||||||
|
= "True".equals(cond.get("status").getAsString());
|
||||||
|
if (RUNNING_STATES.contains(event.state())
|
||||||
|
&& !reportedRunning) {
|
||||||
|
cond.addProperty("status", "True");
|
||||||
|
cond.addProperty("lastTransitionTime",
|
||||||
|
Instant.now().toString());
|
||||||
|
}
|
||||||
|
if (!RUNNING_STATES.contains(event.state())
|
||||||
|
&& reportedRunning) {
|
||||||
|
cond.addProperty("status", "False");
|
||||||
|
cond.addProperty("lastTransitionTime",
|
||||||
|
Instant.now().toString());
|
||||||
|
}
|
||||||
|
cond.addProperty("reason", event.reason());
|
||||||
|
cond.addProperty("message", event.message());
|
||||||
|
cond.addProperty("observedGeneration",
|
||||||
|
from.getMetadata().getGeneration());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* On ballon change.
|
||||||
|
*
|
||||||
|
* @param event the event
|
||||||
|
* @throws ApiException
|
||||||
|
*/
|
||||||
|
@Handler
|
||||||
|
public void onBallonChange(BalloonChangeEvent event) throws ApiException {
|
||||||
|
if (vmCrApi == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var vmCr = vmCrApi.get(namespace, vmName).throwsApiException()
|
||||||
|
.getObject();
|
||||||
|
vmCrApi.updateStatus(vmCr, from -> {
|
||||||
|
JsonObject status = currentStatus(from);
|
||||||
|
status.addProperty("ram",
|
||||||
|
new Quantity(new BigDecimal(event.size()), Format.BINARY_SI)
|
||||||
|
.toSuffixedString());
|
||||||
|
return status;
|
||||||
|
}).throwsApiException();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* On ballon change.
|
||||||
|
*
|
||||||
|
* @param event the event
|
||||||
|
* @throws ApiException
|
||||||
|
*/
|
||||||
|
@Handler
|
||||||
|
public void onCpuChange(HotpluggableCpuStatus event) throws ApiException {
|
||||||
|
if (vmCrApi == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var vmCr = vmCrApi.get(namespace, vmName).throwsApiException()
|
||||||
|
.getObject();
|
||||||
|
vmCrApi.updateStatus(vmCr, from -> {
|
||||||
|
JsonObject status = currentStatus(from);
|
||||||
|
status.addProperty("cpus", event.usedCpus().size());
|
||||||
|
return status;
|
||||||
|
}).throwsApiException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,47 @@
|
||||||
|
/*
|
||||||
|
* 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.runner.qemu.events;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.JsonNode;
|
||||||
|
import java.math.BigInteger;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Signals a change of the balloon.
|
||||||
|
*/
|
||||||
|
public class BalloonChangeEvent extends MonitorEvent {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Instantiates a new tray moved.
|
||||||
|
*
|
||||||
|
* @param kind the kind
|
||||||
|
* @param data the data
|
||||||
|
*/
|
||||||
|
public BalloonChangeEvent(Kind kind, JsonNode data) {
|
||||||
|
super(kind, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the actual value.
|
||||||
|
*
|
||||||
|
* @return the actual value
|
||||||
|
*/
|
||||||
|
public BigInteger size() {
|
||||||
|
return new BigInteger(data().get("actual").asText());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -19,6 +19,10 @@
|
||||||
package org.jdrupes.vmoperator.runner.qemu.events;
|
package org.jdrupes.vmoperator.runner.qemu.events;
|
||||||
|
|
||||||
import com.fasterxml.jackson.databind.JsonNode;
|
import com.fasterxml.jackson.databind.JsonNode;
|
||||||
|
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
import org.jdrupes.vmoperator.runner.qemu.commands.QmpCommand;
|
import org.jdrupes.vmoperator.runner.qemu.commands.QmpCommand;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -26,6 +30,9 @@ import org.jdrupes.vmoperator.runner.qemu.commands.QmpCommand;
|
||||||
*/
|
*/
|
||||||
public class HotpluggableCpuStatus extends MonitorResult {
|
public class HotpluggableCpuStatus extends MonitorResult {
|
||||||
|
|
||||||
|
private List<ObjectNode> usedCpus = new ArrayList<>();
|
||||||
|
private List<ObjectNode> unusedCpus = new ArrayList<>();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Instantiates a new hotpluggable cpu result.
|
* Instantiates a new hotpluggable cpu result.
|
||||||
*
|
*
|
||||||
|
|
@ -34,6 +41,39 @@ public class HotpluggableCpuStatus extends MonitorResult {
|
||||||
*/
|
*/
|
||||||
public HotpluggableCpuStatus(QmpCommand command, JsonNode response) {
|
public HotpluggableCpuStatus(QmpCommand command, JsonNode response) {
|
||||||
super(command, response);
|
super(command, response);
|
||||||
|
if (!successful()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sort
|
||||||
|
for (var itr = values().iterator(); itr.hasNext();) {
|
||||||
|
ObjectNode cpu = (ObjectNode) itr.next();
|
||||||
|
if (cpu.has("qom-path")) {
|
||||||
|
usedCpus.add(cpu);
|
||||||
|
} else {
|
||||||
|
unusedCpus.add(cpu);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
usedCpus = Collections.unmodifiableList(usedCpus);
|
||||||
|
unusedCpus = Collections.unmodifiableList(unusedCpus);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the used cpus.
|
||||||
|
*
|
||||||
|
* @return the usedCpus
|
||||||
|
*/
|
||||||
|
public List<ObjectNode> usedCpus() {
|
||||||
|
return usedCpus;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the unused cpus.
|
||||||
|
*
|
||||||
|
* @return the unusedCpus
|
||||||
|
*/
|
||||||
|
public List<ObjectNode> unusedCpus() {
|
||||||
|
return unusedCpus;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,7 @@ public class MonitorEvent extends Event<Void> {
|
||||||
* The kind of monitor event.
|
* The kind of monitor event.
|
||||||
*/
|
*/
|
||||||
public enum Kind {
|
public enum Kind {
|
||||||
READY, POWERDOWN, DEVICE_TRAY_MOVED
|
READY, POWERDOWN, DEVICE_TRAY_MOVED, BALLOON_CHANGE
|
||||||
}
|
}
|
||||||
|
|
||||||
private final Kind kind;
|
private final Kind kind;
|
||||||
|
|
@ -55,6 +55,9 @@ public class MonitorEvent extends Event<Void> {
|
||||||
case DEVICE_TRAY_MOVED:
|
case DEVICE_TRAY_MOVED:
|
||||||
return Optional
|
return Optional
|
||||||
.of(new TrayMovedEvent(kind, response.get("data")));
|
.of(new TrayMovedEvent(kind, response.get("data")));
|
||||||
|
case BALLOON_CHANGE:
|
||||||
|
return Optional
|
||||||
|
.of(new BalloonChangeEvent(kind, response.get("data")));
|
||||||
default:
|
default:
|
||||||
return Optional
|
return Optional
|
||||||
.of(new MonitorEvent(kind, response.get("data")));
|
.of(new MonitorEvent(kind, response.get("data")));
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,7 @@
|
||||||
package org.jdrupes.vmoperator.runner.qemu.events;
|
package org.jdrupes.vmoperator.runner.qemu.events;
|
||||||
|
|
||||||
import org.jgrapes.core.Channel;
|
import org.jgrapes.core.Channel;
|
||||||
|
import org.jgrapes.core.Components;
|
||||||
import org.jgrapes.core.Event;
|
import org.jgrapes.core.Event;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -30,19 +31,24 @@ public class RunnerStateChange extends Event<Void> {
|
||||||
* The state.
|
* The state.
|
||||||
*/
|
*/
|
||||||
public enum State {
|
public enum State {
|
||||||
INITIALIZING, STARTING, RUNNING, TERMINATING
|
INITIALIZING, STARTING, RUNNING, TERMINATING, STOPPED
|
||||||
}
|
}
|
||||||
|
|
||||||
private final State state;
|
private final State state;
|
||||||
|
private final String reason;
|
||||||
|
private final String message;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Instantiates a new runner state change.
|
* Instantiates a new runner state change.
|
||||||
*
|
*
|
||||||
* @param channels the channels
|
* @param channels the channels
|
||||||
*/
|
*/
|
||||||
public RunnerStateChange(State state, Channel... channels) {
|
public RunnerStateChange(State state, String reason, String message,
|
||||||
|
Channel... channels) {
|
||||||
super(channels);
|
super(channels);
|
||||||
this.state = state;
|
this.state = state;
|
||||||
|
this.reason = reason;
|
||||||
|
this.message = message;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -53,4 +59,36 @@ public class RunnerStateChange extends Event<Void> {
|
||||||
public State state() {
|
public State state() {
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the reason.
|
||||||
|
*
|
||||||
|
* @return the reason
|
||||||
|
*/
|
||||||
|
public String reason() {
|
||||||
|
return reason;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the message.
|
||||||
|
*
|
||||||
|
* @return the message
|
||||||
|
*/
|
||||||
|
public String message() {
|
||||||
|
return message;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
StringBuilder builder = new StringBuilder();
|
||||||
|
builder.append(Components.objectName(this))
|
||||||
|
.append(" [").append(state).append(": ").append(reason);
|
||||||
|
if (channels() != null) {
|
||||||
|
builder.append(", channels=");
|
||||||
|
builder.append(Channel.toString(channels()));
|
||||||
|
}
|
||||||
|
builder.append(']');
|
||||||
|
return builder.toString();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
#
|
#
|
||||||
#Fri Sep 01 16:56:14 CEST 2023
|
#Thu Sep 14 22:05:28 CEST 2023
|
||||||
org.eclipse.jdt.core.formatter.insert_space_after_ellipsis=insert
|
org.eclipse.jdt.core.formatter.insert_space_after_ellipsis=insert
|
||||||
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_declarations=insert
|
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_declarations=insert
|
||||||
org.eclipse.jdt.core.formatter.insert_new_line_in_empty_annotation_declaration=insert
|
org.eclipse.jdt.core.formatter.insert_new_line_in_empty_annotation_declaration=insert
|
||||||
|
|
@ -22,12 +22,12 @@ org.eclipse.jdt.core.formatter.blank_lines_after_package=1
|
||||||
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_local_declarations=insert
|
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_local_declarations=insert
|
||||||
org.eclipse.jdt.core.formatter.alignment_for_arguments_in_enum_constant=16
|
org.eclipse.jdt.core.formatter.alignment_for_arguments_in_enum_constant=16
|
||||||
org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_parameterized_type_reference=do not insert
|
org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_parameterized_type_reference=do not insert
|
||||||
org.eclipse.jdt.core.formatter.wrap_before_or_operator_multicatch=true
|
|
||||||
org.eclipse.jdt.core.formatter.comment.indent_root_tags=false
|
org.eclipse.jdt.core.formatter.comment.indent_root_tags=false
|
||||||
|
org.eclipse.jdt.core.formatter.wrap_before_or_operator_multicatch=true
|
||||||
org.eclipse.jdt.core.formatter.enabling_tag=@formatter\:on
|
org.eclipse.jdt.core.formatter.enabling_tag=@formatter\:on
|
||||||
org.eclipse.jdt.core.formatter.comment.count_line_length_from_starting_position=false
|
org.eclipse.jdt.core.formatter.comment.count_line_length_from_starting_position=false
|
||||||
org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_method_declaration=16
|
|
||||||
org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_parameter=do not insert
|
org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_parameter=do not insert
|
||||||
|
org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_method_declaration=16
|
||||||
org.eclipse.jdt.core.formatter.keep_then_statement_on_same_line=false
|
org.eclipse.jdt.core.formatter.keep_then_statement_on_same_line=false
|
||||||
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_explicitconstructorcall_arguments=insert
|
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_explicitconstructorcall_arguments=insert
|
||||||
org.eclipse.jdt.core.formatter.insert_space_after_prefix_operator=do not insert
|
org.eclipse.jdt.core.formatter.insert_space_after_prefix_operator=do not insert
|
||||||
|
|
@ -43,8 +43,8 @@ org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_invoc
|
||||||
org.eclipse.jdt.core.formatter.insert_space_after_assignment_operator=insert
|
org.eclipse.jdt.core.formatter.insert_space_after_assignment_operator=insert
|
||||||
org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_type_declaration=insert
|
org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_type_declaration=insert
|
||||||
org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_for=do not insert
|
org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_for=do not insert
|
||||||
org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_local_variable=insert
|
|
||||||
org.eclipse.jdt.core.formatter.comment.preserve_white_space_between_code_and_line_comments=true
|
org.eclipse.jdt.core.formatter.comment.preserve_white_space_between_code_and_line_comments=true
|
||||||
|
org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_local_variable=insert
|
||||||
org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_invocation=do not insert
|
org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_invocation=do not insert
|
||||||
org.eclipse.jdt.core.formatter.alignment_for_union_type_in_multicatch=16
|
org.eclipse.jdt.core.formatter.alignment_for_union_type_in_multicatch=16
|
||||||
org.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_method_body=0
|
org.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_method_body=0
|
||||||
|
|
@ -63,8 +63,8 @@ org.eclipse.jdt.core.formatter.alignment_for_type_parameters=16
|
||||||
org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
|
org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
|
||||||
org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_block_comment=false
|
org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_block_comment=false
|
||||||
org.eclipse.jdt.core.formatter.insert_new_line_at_end_of_file_if_missing=do not insert
|
org.eclipse.jdt.core.formatter.insert_new_line_at_end_of_file_if_missing=do not insert
|
||||||
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_array_initializer=insert
|
|
||||||
org.eclipse.jdt.core.formatter.insert_space_before_unary_operator=do not insert
|
org.eclipse.jdt.core.formatter.insert_space_before_unary_operator=do not insert
|
||||||
|
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_array_initializer=insert
|
||||||
org.eclipse.jdt.core.formatter.format_line_comment_starting_on_first_column=false
|
org.eclipse.jdt.core.formatter.format_line_comment_starting_on_first_column=false
|
||||||
org.eclipse.jdt.core.formatter.parentheses_positions_in_annotation=common_lines
|
org.eclipse.jdt.core.formatter.parentheses_positions_in_annotation=common_lines
|
||||||
org.eclipse.jdt.core.formatter.insert_space_before_ellipsis=do not insert
|
org.eclipse.jdt.core.formatter.insert_space_before_ellipsis=do not insert
|
||||||
|
|
|
||||||
|
|
@ -9,5 +9,6 @@ plugins {
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation 'org.freemarker:freemarker:[2.3.32,2.4)'
|
api 'org.freemarker:freemarker:[2.3.32,2.4)'
|
||||||
|
api 'io.kubernetes:client-java:[18.0.0,19)'
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,34 @@
|
||||||
|
/*
|
||||||
|
* VM-Operator
|
||||||
|
* Copyright (C) 2023 Michael N. Lipp
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as
|
||||||
|
* published by the Free Software Foundation, either version 3 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jdrupes.vmoperator.util;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Some constants.
|
||||||
|
*/
|
||||||
|
public class Constants {
|
||||||
|
|
||||||
|
/** The Constant VM_OP_NAME. */
|
||||||
|
public static final String VM_OP_NAME = "vm-operator";
|
||||||
|
|
||||||
|
/** The Constant VM_OP_GROUP. */
|
||||||
|
public static final String VM_OP_GROUP = "vmoperator.jdrupes.org";
|
||||||
|
|
||||||
|
/** The Constant VM_OP_KIND_VM. */
|
||||||
|
public static final String VM_OP_KIND_VM = "VirtualMachine";
|
||||||
|
}
|
||||||
|
|
@ -16,7 +16,7 @@
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.jdrupes.vmoperator.manager;
|
package org.jdrupes.vmoperator.util;
|
||||||
|
|
||||||
import com.google.gson.JsonArray;
|
import com.google.gson.JsonArray;
|
||||||
import com.google.gson.JsonElement;
|
import com.google.gson.JsonElement;
|
||||||
|
|
@ -16,7 +16,7 @@
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.jdrupes.vmoperator.manager;
|
package org.jdrupes.vmoperator.util;
|
||||||
|
|
||||||
import io.kubernetes.client.common.KubernetesListObject;
|
import io.kubernetes.client.common.KubernetesListObject;
|
||||||
import io.kubernetes.client.common.KubernetesObject;
|
import io.kubernetes.client.common.KubernetesObject;
|
||||||
Loading…
Add table
Add a link
Reference in a new issue