diff --git a/execution/helm/templates/theodolite/crd-benchmark.yaml b/execution/helm/templates/theodolite/crd-benchmark.yaml index e6fc214df3120066fd7bb32c3501965eb3b4649c..848ecb37213f2810853a47fd45d3869198acd720 100644 --- a/execution/helm/templates/theodolite/crd-benchmark.yaml +++ b/execution/helm/templates/theodolite/crd-benchmark.yaml @@ -9,7 +9,7 @@ spec: kind: benchmark plural: benchmarks shortNames: - - bm + - bench versions: - name: v1 served: true diff --git a/execution/helm/templates/theodolite/crd-execution.yaml b/execution/helm/templates/theodolite/crd-execution.yaml index 6b9dea70a2a4d8d23a79c68247194951f26a8fb2..92835ee1d5a016d0fe6e2db874ae222d7f49f461 100644 --- a/execution/helm/templates/theodolite/crd-execution.yaml +++ b/execution/helm/templates/theodolite/crd-execution.yaml @@ -9,7 +9,7 @@ spec: kind: execution plural: executions shortNames: - - ex + - exec versions: - name: v1 served: true diff --git a/theodolite-quarkus/src/main/kotlin/theodolite/execution/operator/ClusterSetup.kt b/theodolite-quarkus/src/main/kotlin/theodolite/execution/operator/ClusterSetup.kt index f3b4e9131a7184962310fe107586757cfb983097..6c8c48f791543b6d8a7716cf26a80bdb449ee7a7 100644 --- a/theodolite-quarkus/src/main/kotlin/theodolite/execution/operator/ClusterSetup.kt +++ b/theodolite-quarkus/src/main/kotlin/theodolite/execution/operator/ClusterSetup.kt @@ -35,7 +35,7 @@ class ClusterSetup( .list() .items .asSequence() - .filter { it.status.executionState == STATES.Running.value } + .filter { it.status.executionState == States.RUNNING.value } .forEach { execution -> val benchmark = benchmarkCRDClient .inNamespace(client.namespace) @@ -49,7 +49,7 @@ class ClusterSetup( Shutdown(execution.spec, benchmark.spec).start() } else { logger.error { - "Execution with state ${STATES.Running.value} was found, but no corresponding benchmark. " + + "Execution with state ${States.RUNNING.value} was found, but no corresponding benchmark. " + "Could not initialize cluster." } } diff --git a/theodolite-quarkus/src/main/kotlin/theodolite/execution/operator/ExecutionEventHandler.kt b/theodolite-quarkus/src/main/kotlin/theodolite/execution/operator/ExecutionEventHandler.kt index 2469d0f7c016447b8c49135b907135beec74af63..a1617b4988d500baab7b02bf5fa993f7a4ae76a3 100644 --- a/theodolite-quarkus/src/main/kotlin/theodolite/execution/operator/ExecutionEventHandler.kt +++ b/theodolite-quarkus/src/main/kotlin/theodolite/execution/operator/ExecutionEventHandler.kt @@ -33,9 +33,9 @@ class ExecutionHandler( logger.info { "Add execution ${execution.metadata.name}" } execution.spec.name = execution.metadata.name when (this.stateHandler.getExecutionState(execution.metadata.name)) { - null -> this.stateHandler.setExecutionState(execution.spec.name, STATES.Pending) - STATES.Running -> { - this.stateHandler.setExecutionState(execution.spec.name, STATES.Restart) + null -> this.stateHandler.setExecutionState(execution.spec.name, States.PENDING) + States.RUNNING -> { + this.stateHandler.setExecutionState(execution.spec.name, States.RESTART) if(this.controller.isExecutionRunning(execution.spec.name)){ this.controller.stop(restart=true) } @@ -58,14 +58,14 @@ class ExecutionHandler( oldExecution.spec.name = oldExecution.metadata.name if(gson.toJson(oldExecution.spec) != gson.toJson(newExecution.spec)) { when(this.stateHandler.getExecutionState(newExecution.metadata.name)) { - STATES.Running -> { - this.stateHandler.setExecutionState(newExecution.spec.name, STATES.Restart) + States.RUNNING -> { + this.stateHandler.setExecutionState(newExecution.spec.name, States.RESTART) if (this.controller.isExecutionRunning(newExecution.spec.name)){ this.controller.stop(restart=true) } } - STATES.Restart -> {} // should this set to pending? - else -> this.stateHandler.setExecutionState(newExecution.spec.name, STATES.Pending) + States.RESTART -> {} // should this set to pending? + else -> this.stateHandler.setExecutionState(newExecution.spec.name, States.PENDING) } } } @@ -78,7 +78,7 @@ class ExecutionHandler( @Synchronized override fun onDelete(execution: ExecutionCRD, b: Boolean) { logger.info { "Delete execution ${execution.metadata.name}" } - if(execution.status.executionState == STATES.Running.value + if(execution.status.executionState == States.RUNNING.value && this.controller.isExecutionRunning(execution.spec.name)) { this.controller.stop() } diff --git a/theodolite-quarkus/src/main/kotlin/theodolite/execution/operator/ExecutionStateHandler.kt b/theodolite-quarkus/src/main/kotlin/theodolite/execution/operator/ExecutionStateHandler.kt index 0e99e92360ef08c389ada40ea30728f118a05adc..fe1b95f95c74efe77913ea435dd1ac896805b065 100644 --- a/theodolite-quarkus/src/main/kotlin/theodolite/execution/operator/ExecutionStateHandler.kt +++ b/theodolite-quarkus/src/main/kotlin/theodolite/execution/operator/ExecutionStateHandler.kt @@ -5,7 +5,7 @@ import io.fabric8.kubernetes.client.KubernetesClient import io.fabric8.kubernetes.client.dsl.base.CustomResourceDefinitionContext import theodolite.model.crd.BenchmarkExecutionList import theodolite.model.crd.ExecutionCRD -import theodolite.model.crd.STATES +import theodolite.model.crd.States import java.lang.Thread.sleep import java.time.Duration import java.time.Instant @@ -33,14 +33,14 @@ class ExecutionStateHandler(context: CustomResourceDefinitionContext, val client execState } - fun setExecutionState(resourceName: String, status: STATES): Boolean { + fun setExecutionState(resourceName: String, status: States): Boolean { setState(resourceName) {cr -> if(cr is ExecutionCRD) cr.status.executionState = status.value; cr} return blockUntilStateIsSet(resourceName, status.value, getExecutionLambda()) } - fun getExecutionState(resourceName: String) : STATES? { + fun getExecutionState(resourceName: String) : States? { val status = this.getState(resourceName, getExecutionLambda()) - return STATES.values().firstOrNull { it.value == status } + return States.values().firstOrNull { it.value == status } } fun setDurationState(resourceName: String, duration: Duration) { diff --git a/theodolite-quarkus/src/main/kotlin/theodolite/execution/operator/LeaderElector.kt b/theodolite-quarkus/src/main/kotlin/theodolite/execution/operator/LeaderElector.kt new file mode 100644 index 0000000000000000000000000000000000000000..b887f5e9adea5d4feadc4be335b97d09c734aff4 --- /dev/null +++ b/theodolite-quarkus/src/main/kotlin/theodolite/execution/operator/LeaderElector.kt @@ -0,0 +1,44 @@ +package theodolite.execution.operator + +import io.fabric8.kubernetes.client.DefaultKubernetesClient +import io.fabric8.kubernetes.client.NamespacedKubernetesClient +import io.fabric8.kubernetes.client.extended.leaderelection.LeaderCallbacks +import io.fabric8.kubernetes.client.extended.leaderelection.LeaderElectionConfigBuilder +import io.fabric8.kubernetes.client.extended.leaderelection.resourcelock.LeaseLock +import mu.KotlinLogging +import java.time.Duration +import java.util.* +import kotlin.reflect.KFunction0 +import kotlin.reflect.KFunction1 + +private val logger = KotlinLogging.logger {} + +class LeaderElector( + val client: NamespacedKubernetesClient, + val name: String + ) { + + fun getLeadership(leader: KFunction1<NamespacedKubernetesClient, Unit>) { + val lockIdentity: String = UUID.randomUUID().toString() + DefaultKubernetesClient().use { kc -> + kc.leaderElector() + .withConfig( + LeaderElectionConfigBuilder() + .withName("Theodolite") + .withLeaseDuration(Duration.ofSeconds(15L)) + .withLock(LeaseLock(client.namespace, name, lockIdentity)) + .withRenewDeadline(Duration.ofSeconds(10L)) + .withRetryPeriod(Duration.ofSeconds(2L)) + .withLeaderCallbacks(LeaderCallbacks( + leader, + { logger.info { "STOPPED LEADERSHIP " } } + ) { newLeader: String? -> + logger.info { "New leader elected $newLeader" } + }) + .build() + ) + .build().run() + } + } + +} \ No newline at end of file diff --git a/theodolite-quarkus/src/main/kotlin/theodolite/execution/operator/TheodoliteController.kt b/theodolite-quarkus/src/main/kotlin/theodolite/execution/operator/TheodoliteController.kt index 3d76a8620c0112a15d94f0227efcdd5ef9afce1d..1e3929da98be060e2cbebf305dbae2f25519798a 100644 --- a/theodolite-quarkus/src/main/kotlin/theodolite/execution/operator/TheodoliteController.kt +++ b/theodolite-quarkus/src/main/kotlin/theodolite/execution/operator/TheodoliteController.kt @@ -78,23 +78,23 @@ class TheodoliteController( benchmark.appResource + benchmark.loadGenResource, execution) - executionStateHandler.setExecutionState(execution.name, STATES.Running) + executionStateHandler.setExecutionState(execution.name, States.RUNNING) executionStateHandler.startDurationStateTimer(execution.name) try { executor = TheodoliteExecutor(execution, benchmark) executor.run() when (executionStateHandler.getExecutionState(execution.name)) { - STATES.Restart -> runExecution(execution, benchmark) - STATES.Running -> { - executionStateHandler.setExecutionState(execution.name, STATES.Finished) + States.RESTART -> runExecution(execution, benchmark) + States.RUNNING -> { + executionStateHandler.setExecutionState(execution.name, States.FINISHED) logger.info { "Execution of ${execution.name} is finally stopped." } } } } catch (e: Exception) { logger.error { "Failure while executing execution ${execution.name} with benchmark ${benchmark.name}." } logger.error { "Problem is: $e" } - executionStateHandler.setExecutionState(execution.name, STATES.Failure) + executionStateHandler.setExecutionState(execution.name, States.FAILURE) } executionStateHandler.stopDurationStateTimer() } @@ -103,9 +103,9 @@ class TheodoliteController( fun stop(restart: Boolean = false) { if (!::executor.isInitialized) return if (restart) { - executionStateHandler.setExecutionState(this.executor.getExecution().name, STATES.Restart) + executionStateHandler.setExecutionState(this.executor.getExecution().name, States.RESTART) } else { - executionStateHandler.setExecutionState(this.executor.getExecution().name, STATES.Interrupted) + executionStateHandler.setExecutionState(this.executor.getExecution().name, States.INTERRUPTED) logger.warn { "Execution ${executor.getExecution().name} unexpected interrupted" } } this.executor.executor.run.set(false) @@ -129,8 +129,8 @@ class TheodoliteController( * is selected for the next execution depends on three points: * * 1. Only executions are considered for which a matching benchmark is available on the cluster - * 2. The Status of the execution must be [STATES.Pending] or [STATES.Restart] - * 3. Of the remaining [BenchmarkCRD], those with status [STATES.Restart] are preferred, + * 2. The Status of the execution must be [States.PENDING] or [States.RESTART] + * 3. Of the remaining [BenchmarkCRD], those with status [States.RESTART] are preferred, * then, if there is more than one, the oldest execution is chosen. * * @return the next execution or null @@ -146,8 +146,8 @@ class TheodoliteController( .asSequence() .map { it.spec.name = it.metadata.name; it } .filter { - it.status.executionState == STATES.Pending.value || - it.status.executionState == STATES.Restart.value + it.status.executionState == States.PENDING.value || + it.status.executionState == States.RESTART.value } .filter { availableBenchmarkNames.contains(it.spec.benchmark) } .sortedWith(stateComparator().thenBy { it.metadata.creationTimestamp }) @@ -157,12 +157,12 @@ class TheodoliteController( /** * Simple comparator which can be used to order a list of [ExecutionCRD] such that executions with - * status [STATES.Restart] are before all other executions. + * status [States.RESTART] are before all other executions. */ private fun stateComparator() = Comparator<ExecutionCRD> { a, b -> when { (a == null && b == null) -> 0 - (a.status.executionState == STATES.Restart.value) -> -1 + (a.status.executionState == States.RESTART.value) -> -1 else -> 1 } } diff --git a/theodolite-quarkus/src/main/kotlin/theodolite/k8s/K8sManager.kt b/theodolite-quarkus/src/main/kotlin/theodolite/k8s/K8sManager.kt index af9e780d505d7392eff535ee9d3be11780561ce4..c58225bf855c70a5a7057132617418a89b0816a8 100644 --- a/theodolite-quarkus/src/main/kotlin/theodolite/k8s/K8sManager.kt +++ b/theodolite-quarkus/src/main/kotlin/theodolite/k8s/K8sManager.kt @@ -63,12 +63,6 @@ class K8sManager(private val client: NamespacedKubernetesClient) { } } - fun setLabel(resource: KubernetesResource) { - when(resource) { - is Deployment -> this.client - } - } - private fun blockUntilPodsDeleted(podLabel: String) { while (!this.client.pods().withLabel(podLabel).list().items.isNullOrEmpty()) { logger.info { "Wait for pods with label '$podLabel' to be deleted." } diff --git a/theodolite-quarkus/src/main/kotlin/theodolite/model/crd/STATES.kt b/theodolite-quarkus/src/main/kotlin/theodolite/model/crd/STATES.kt deleted file mode 100644 index 3fcd9751c3ba755121b4b467b45e2cb3f4f13bee..0000000000000000000000000000000000000000 --- a/theodolite-quarkus/src/main/kotlin/theodolite/model/crd/STATES.kt +++ /dev/null @@ -1,11 +0,0 @@ -package theodolite.model.crd - -enum class STATES(val value: String) { - Running("RUNNING"), - Pending("PENDING"), - Failure("FAILURE"), - Finished("FINISHED"), - Restart("RESTART"), - Interrupted("INTERRUPTED"), - NoState("NoState") -} \ No newline at end of file diff --git a/theodolite-quarkus/src/main/kotlin/theodolite/model/crd/States.kt b/theodolite-quarkus/src/main/kotlin/theodolite/model/crd/States.kt new file mode 100644 index 0000000000000000000000000000000000000000..79af297915b6703b209acb0c13913482e54db2be --- /dev/null +++ b/theodolite-quarkus/src/main/kotlin/theodolite/model/crd/States.kt @@ -0,0 +1,11 @@ +package theodolite.model.crd + +enum class States(val value: String) { + RUNNING("RUNNING"), + PENDING("PENDING"), + FAILURE("FAILURE"), + FINISHED("FINISHED"), + RESTART("RESTART"), + INTERRUPTED("INTERRUPTED"), + NO_STATE("NoState") +} \ No newline at end of file diff --git a/theodolite-quarkus/src/main/resources/operator/benchmarkCRD.yaml b/theodolite-quarkus/src/main/resources/operator/benchmarkCRD.yaml index 8bfc8d3b645985bde7b843a11afca0a514ade5dc..4e481c51231999e2e7a1e75ecbc018d40db75c91 100644 --- a/theodolite-quarkus/src/main/resources/operator/benchmarkCRD.yaml +++ b/theodolite-quarkus/src/main/resources/operator/benchmarkCRD.yaml @@ -8,7 +8,7 @@ spec: kind: benchmark plural: benchmarks shortNames: - - bm + - bench versions: - name: v1 served: true diff --git a/theodolite-quarkus/src/main/resources/operator/executionCRD.yaml b/theodolite-quarkus/src/main/resources/operator/executionCRD.yaml index cfe54d0c6c2d03e4939cbfa4f8c911b73050f29f..8e1189572ee993c37dd565fc62a66996654766f2 100644 --- a/theodolite-quarkus/src/main/resources/operator/executionCRD.yaml +++ b/theodolite-quarkus/src/main/resources/operator/executionCRD.yaml @@ -8,7 +8,7 @@ spec: kind: execution plural: executions shortNames: - - ex + - exec versions: - name: v1 served: true