Skip to content
Snippets Groups Projects
Commit c5e9950b authored by Benedikt Wetzel's avatar Benedikt Wetzel
Browse files

separate execution and benchmark state and small enhancements

parent 9e71cec2
No related branches found
No related tags found
1 merge request!176Add a Benchmark Status
Showing
with 92 additions and 100 deletions
......@@ -86,7 +86,7 @@ Resource Types:
</tr>
</thead>
<tbody><tr>
<td><b>resourceSets</b></td>
<td><b>resourceSetsState</b></td>
<td>string</td>
<td>
The status of a Benchmark indicates whether all resources are available to start the benchmark or not.<br/>
......
......@@ -193,17 +193,17 @@ spec:
status:
type: object
properties:
resourceSets:
resourceSetsState:
description: The status of a Benchmark indicates whether all resources are available to start the benchmark or not.
type: string
additionalPrinterColumns:
- name: Age
type: date
jsonPath: .metadata.creationTimestamp
- name: ResourceSets
- name: STATUS
type: string
description: The status of a Benchmark indicates whether all resources are available to start the benchmark or not.
jsonPath: .status.resourceSets
jsonPath: .status.resourceSetsState
subresources:
status: {}
scope: Namespaced
\ No newline at end of file
......@@ -11,7 +11,6 @@ import theodolite.util.DeploymentFailedException
@JsonDeserialize
@RegisterForReflection
@JsonInclude(JsonInclude.Include.NON_NULL)
class ResourceSets: KubernetesResource {
@JsonProperty("configMap")
@JsonInclude(JsonInclude.Include.NON_NULL)
......
......@@ -10,19 +10,19 @@ class BenchmarkStateHandler(val client: NamespacedKubernetesClient) :
crdList = KubernetesBenchmarkList::class.java
) {
private fun getBenchmarkResourceState() = { cr: BenchmarkCRD -> cr.status.resourceSets }
private fun getBenchmarkResourceState() = { cr: BenchmarkCRD -> cr.status.resourceSetsState }
fun setResourceSetState(resourceName: String, status: States): Boolean {
setState(resourceName) { cr -> cr.status.resourceSets = status.value; cr }
fun setResourceSetState(resourceName: String, status: BenchmarkStates): Boolean {
setState(resourceName) { cr -> cr.status.resourceSetsState = status.value; cr }
return blockUntilStateIsSet(resourceName, status.value, getBenchmarkResourceState())
}
fun getResourceSetState(resourceName: String): States {
fun getResourceSetState(resourceName: String): ExecutionStates {
val status = this.getState(resourceName, getBenchmarkResourceState())
return if (status.isNullOrBlank()) {
States.NO_STATE
ExecutionStates.NO_STATE
} else {
States.values().first { it.value == status }
ExecutionStates.values().first { it.value == status }
}
}
}
\ No newline at end of file
......@@ -41,7 +41,7 @@ class ClusterSetup(
.list()
.items
.asSequence()
.filter { it.status.executionState == States.RUNNING.value }
.filter { it.status.executionState == ExecutionStates.RUNNING.value }
.forEach { execution ->
val benchmark = benchmarkCRDClient
.inNamespace(client.namespace)
......@@ -54,7 +54,7 @@ class ClusterSetup(
benchmark.spec.name = benchmark.metadata.name
Shutdown(execution.spec, benchmark.spec).start()
} else {
throw IllegalStateException("Execution with state ${States.RUNNING.value} was found, but no corresponding benchmark. " +
throw IllegalStateException("Execution with state ${ExecutionStates.RUNNING.value} was found, but no corresponding benchmark. " +
"Could not initialize cluster.")
}
}
......
......@@ -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)) {
States.NO_STATE -> this.stateHandler.setExecutionState(execution.spec.name, States.PENDING)
States.RUNNING -> {
this.stateHandler.setExecutionState(execution.spec.name, States.RESTART)
ExecutionStates.NO_STATE -> this.stateHandler.setExecutionState(execution.spec.name, ExecutionStates.PENDING)
ExecutionStates.RUNNING -> {
this.stateHandler.setExecutionState(execution.spec.name, ExecutionStates.RESTART)
if (this.controller.isExecutionRunning(execution.spec.name)) {
this.controller.stop(restart = true)
}
......@@ -58,15 +58,15 @@ class ExecutionHandler(
if (gson.toJson(oldExecution.spec) != gson.toJson(newExecution.spec)) {
logger.info { "Receive update event for execution ${oldExecution.metadata.name}" }
when (this.stateHandler.getExecutionState(newExecution.metadata.name)) {
States.RUNNING -> {
this.stateHandler.setExecutionState(newExecution.spec.name, States.RESTART)
ExecutionStates.RUNNING -> {
this.stateHandler.setExecutionState(newExecution.spec.name, ExecutionStates.RESTART)
if (this.controller.isExecutionRunning(newExecution.spec.name)) {
this.controller.stop(restart = true)
}
}
States.RESTART -> {
ExecutionStates.RESTART -> {
} // should this set to pending?
else -> this.stateHandler.setExecutionState(newExecution.spec.name, States.PENDING)
else -> this.stateHandler.setExecutionState(newExecution.spec.name, ExecutionStates.PENDING)
}
}
}
......@@ -79,7 +79,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 == ExecutionStates.RUNNING.value
&& this.controller.isExecutionRunning(execution.metadata.name)
) {
this.controller.stop()
......
......@@ -4,7 +4,7 @@ import io.fabric8.kubernetes.client.NamespacedKubernetesClient
import theodolite.model.crd.BenchmarkExecutionList
import theodolite.model.crd.ExecutionCRD
import theodolite.model.crd.ExecutionStatus
import theodolite.model.crd.States
import theodolite.model.crd.ExecutionStates
import java.lang.Thread.sleep
import java.time.Duration
import java.time.Instant
......@@ -23,17 +23,17 @@ class ExecutionStateHandler(val client: NamespacedKubernetesClient) :
private fun getDurationLambda() = { cr: ExecutionCRD -> cr.status.executionDuration }
fun setExecutionState(resourceName: String, status: States): Boolean {
fun setExecutionState(resourceName: String, status: ExecutionStates): Boolean {
setState(resourceName) { cr -> cr.status.executionState = status.value; cr }
return blockUntilStateIsSet(resourceName, status.value, getExecutionLambda())
}
fun getExecutionState(resourceName: String): States {
fun getExecutionState(resourceName: String): ExecutionStates {
val status = this.getState(resourceName, getExecutionLambda())
return if (status.isNullOrBlank()) {
States.NO_STATE
ExecutionStates.NO_STATE
} else {
States.values().first { it.value == status }
ExecutionStates.values().first { it.value == status }
}
}
......
......@@ -5,11 +5,9 @@ import io.fabric8.kubernetes.client.dsl.Resource
import mu.KotlinLogging
import theodolite.benchmark.BenchmarkExecution
import theodolite.benchmark.KubernetesBenchmark
import theodolite.execution.ExecutionModes
import theodolite.execution.TheodoliteExecutor
import theodolite.model.crd.*
import theodolite.patcher.ConfigOverrideModifier
import theodolite.util.ExecutionFailedException
import theodolite.util.ExecutionStateComparator
import java.lang.Thread.sleep
......@@ -90,20 +88,20 @@ class TheodoliteController(
labelName = CREATED_BY_LABEL_NAME
)
executionStateHandler.setExecutionState(execution.name, States.RUNNING)
executionStateHandler.setExecutionState(execution.name, ExecutionStates.RUNNING)
executionStateHandler.startDurationStateTimer(execution.name)
executor = TheodoliteExecutor(execution, benchmark)
executor.run()
when (executionStateHandler.getExecutionState(execution.name)) {
States.RESTART -> runExecution(execution, benchmark)
States.RUNNING -> {
executionStateHandler.setExecutionState(execution.name, States.FINISHED)
ExecutionStates.RESTART -> runExecution(execution, benchmark)
ExecutionStates.RUNNING -> {
executionStateHandler.setExecutionState(execution.name, ExecutionStates.FINISHED)
logger.info { "Execution of ${execution.name} is finally stopped." }
}
else -> {
executionStateHandler.setExecutionState(execution.name, States.FAILURE)
logger.warn { "Unexpected execution state, set state to ${States.FAILURE.value}" }
executionStateHandler.setExecutionState(execution.name, ExecutionStates.FAILURE)
logger.warn { "Unexpected execution state, set state to ${ExecutionStates.FAILURE.value}" }
}
}
} catch (e: Exception) {
......@@ -114,7 +112,7 @@ class TheodoliteController(
message = "An error occurs while executing: ${e.message}")
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, ExecutionStates.FAILURE)
}
executionStateHandler.stopDurationStateTimer()
}
......@@ -123,7 +121,7 @@ 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, ExecutionStates.RESTART)
}
this.executor.executor.run.set(false)
}
......@@ -147,16 +145,16 @@ 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 [ExecutionStates.PENDING] or [ExecutionStates.RESTART]
* 3. Of the remaining [BenchmarkCRD], those with status [ExecutionStates.RESTART] are preferred,
* then, if there is more than one, the oldest execution is chosen.
*
* @return the next execution or null
*/
private fun getNextExecution(): BenchmarkExecution? {
val comparator = ExecutionStateComparator(States.RESTART)
val comparator = ExecutionStateComparator(ExecutionStates.RESTART)
val availableBenchmarkNames = getBenchmarks()
.filter { it.status.resourceSets == States.AVAILABLE.value }
.filter { it.status.resourceSetsState == BenchmarkStates.READY.value }
.map { it.spec }
.map { it.name }
......@@ -166,8 +164,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 == ExecutionStates.PENDING.value ||
it.status.executionState == ExecutionStates.RESTART.value
}
.filter { availableBenchmarkNames.contains(it.spec.benchmark) }
.sortedWith(comparator.thenBy { it.metadata.creationTimestamp })
......@@ -184,23 +182,23 @@ class TheodoliteController(
.forEach { setState(it.first, it.second ) }
}
private fun setState(resource: BenchmarkCRD, state: States) {
private fun setState(resource: BenchmarkCRD, state: BenchmarkStates) {
benchmarkStateHandler.setResourceSetState(resource.spec.name, state)
}
private fun checkResource(benchmark: KubernetesBenchmark): States {
private fun checkResource(benchmark: KubernetesBenchmark): BenchmarkStates {
return try {
val appResources =
benchmark.loadKubernetesResources(resourceSet = benchmark.appResourceSets)
val loadGenResources =
benchmark.loadKubernetesResources(resourceSet = benchmark.loadGenResourceSets)
if(appResources.isNotEmpty() && loadGenResources.isNotEmpty()) {
States.AVAILABLE
BenchmarkStates.READY
} else {
States.NOT_AVAILABLE
BenchmarkStates.PENDING
}
} catch (e: Exception) {
States.NOT_AVAILABLE
BenchmarkStates.PENDING
}
}
......
package theodolite.model.crd
enum class BenchmarkStates(val value: String) {
PENDING("Pending"),
READY("Ready")
}
\ No newline at end of file
......@@ -6,6 +6,6 @@ import io.fabric8.kubernetes.api.model.Namespaced
@JsonDeserialize
class BenchmarkStatus: KubernetesResource, Namespaced {
var resourceSets = "-"
var resourceSetsState = "-"
}
\ No newline at end of file
package theodolite.model.crd
enum class ExecutionStates(val value: String) {
// Execution states
RUNNING("Running"),
PENDING("Pending"),
FAILURE("Failure"),
FINISHED("Finished"),
RESTART("Restart"),
INTERRUPTED("Interrupted"),
NO_STATE("NoState"),
}
\ No newline at end of file
package theodolite.model.crd
enum class States(val value: String) {
// Execution states
RUNNING("RUNNING"),
PENDING("PENDING"),
FAILURE("FAILURE"),
FINISHED("FINISHED"),
RESTART("RESTART"),
INTERRUPTED("INTERRUPTED"),
NO_STATE("NoState"),
// Benchmark states
AVAILABLE("AVAILABLE"),
NOT_AVAILABLE("NOT_AVAILABLE")
}
\ No newline at end of file
package theodolite.util
import theodolite.model.crd.ExecutionCRD
import theodolite.model.crd.States
import theodolite.model.crd.ExecutionStates
class ExecutionStateComparator(private val preferredState: States): Comparator<ExecutionCRD> {
class ExecutionStateComparator(private val preferredState: ExecutionStates): Comparator<ExecutionCRD> {
/**
* 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 [ExecutionStates.RESTART] are before all other executions.
*/
override fun compare(p0: ExecutionCRD, p1: ExecutionCRD): Int {
return when {
......
......@@ -2,7 +2,6 @@ package theodolite.execution.operator
import com.google.gson.Gson
import com.google.gson.GsonBuilder
import io.fabric8.kubernetes.api.model.Service
import io.fabric8.kubernetes.client.CustomResourceList
import io.fabric8.kubernetes.client.server.mock.KubernetesServer
import io.quarkus.test.junit.QuarkusTest
......@@ -14,9 +13,8 @@ import org.junit.jupiter.api.Test
import theodolite.benchmark.BenchmarkExecution
import theodolite.benchmark.KubernetesBenchmark
import theodolite.model.crd.BenchmarkCRD
import theodolite.model.crd.BenchmarkStates
import theodolite.model.crd.ExecutionCRD
import theodolite.model.crd.ExecutionStatus
import theodolite.model.crd.States
@QuarkusTest
class ControllerTest {
......@@ -42,7 +40,7 @@ class ControllerTest {
// benchmark
val benchmark1 = BenchmarkCRDummy(name = "Test-Benchmark")
benchmark1.getCR().status.resourceSets = States.AVAILABLE.value
benchmark1.getCR().status.resourceSetsState = BenchmarkStates.READY.value
val benchmark2 = BenchmarkCRDummy(name = "Test-Benchmark-123")
benchmarkResourceList.items = listOf(benchmark1.getCR(), benchmark2.getCR())
......
......@@ -3,7 +3,7 @@ package theodolite.execution.operator
import theodolite.benchmark.BenchmarkExecution
import theodolite.model.crd.ExecutionCRD
import theodolite.model.crd.ExecutionStatus
import theodolite.model.crd.States
import theodolite.model.crd.ExecutionStates
class ExecutionCRDummy(name: String, benchmark: String) {
......@@ -51,6 +51,6 @@ class ExecutionCRDummy(name: String, benchmark: String) {
execution.configOverrides = mutableListOf()
execution.name = executionCR.metadata.name
executionState.executionState = States.PENDING.value
executionState.executionState = ExecutionStates.PENDING.value
}
}
\ No newline at end of file
......@@ -11,7 +11,7 @@ import org.junit.jupiter.api.DisplayName
import org.junit.jupiter.api.Test
import theodolite.k8s.K8sManager
import theodolite.k8s.resourceLoader.K8sResourceLoaderFromFile
import theodolite.model.crd.States
import theodolite.model.crd.ExecutionStates
import java.lang.Thread.sleep
......@@ -82,7 +82,7 @@ class ExecutionEventHandlerTest {
factory.startAllRegisteredInformers()
sleep(500)
assertEquals(
States.PENDING,
ExecutionStates.PENDING,
stateHandler.getExecutionState(
resourceName = executionName
)
......@@ -96,12 +96,12 @@ class ExecutionEventHandlerTest {
stateHandler
.setExecutionState(
resourceName = executionName,
status = States.RUNNING
status = ExecutionStates.RUNNING
)
factory.startAllRegisteredInformers()
sleep(500)
assertEquals(
States.RESTART,
ExecutionStates.RESTART,
stateHandler.getExecutionState(
resourceName = executionName
)
......@@ -117,7 +117,7 @@ class ExecutionEventHandlerTest {
sleep(500)
assertEquals(
States.PENDING,
ExecutionStates.PENDING,
stateHandler.getExecutionState(
resourceName = executionName
)
......@@ -125,7 +125,7 @@ class ExecutionEventHandlerTest {
manager.deploy(executionVersion2)
assertEquals(
States.PENDING,
ExecutionStates.PENDING,
stateHandler.getExecutionState(
resourceName = executionName
)
......@@ -141,14 +141,14 @@ class ExecutionEventHandlerTest {
stateHandler.setExecutionState(
resourceName = executionName,
status = States.FINISHED
status = ExecutionStates.FINISHED
)
manager.deploy(executionVersion2)
sleep(500)
assertEquals(
States.PENDING,
ExecutionStates.PENDING,
stateHandler.getExecutionState(
resourceName = executionName
)
......@@ -164,14 +164,14 @@ class ExecutionEventHandlerTest {
stateHandler.setExecutionState(
resourceName = executionName,
status = States.FAILURE
status = ExecutionStates.FAILURE
)
manager.deploy(executionVersion2)
sleep(500)
assertEquals(
States.PENDING,
ExecutionStates.PENDING,
stateHandler.getExecutionState(
resourceName = executionName
)
......@@ -188,14 +188,14 @@ class ExecutionEventHandlerTest {
stateHandler.setExecutionState(
resourceName = executionName,
status = States.RUNNING
status = ExecutionStates.RUNNING
)
manager.deploy(executionVersion2)
sleep(500)
assertEquals(
States.RESTART,
ExecutionStates.RESTART,
stateHandler.getExecutionState(
resourceName = executionName
)
......@@ -211,14 +211,14 @@ class ExecutionEventHandlerTest {
stateHandler.setExecutionState(
resourceName = executionName,
status = States.RESTART
status = ExecutionStates.RESTART
)
manager.deploy(executionVersion2)
sleep(500)
assertEquals(
States.RESTART,
ExecutionStates.RESTART,
stateHandler.getExecutionState(
resourceName = executionName
)
......
......@@ -9,7 +9,7 @@ import org.junit.jupiter.api.DisplayName
import org.junit.jupiter.api.Test
import theodolite.k8s.K8sManager
import theodolite.k8s.resourceLoader.K8sResourceLoaderFromFile
import theodolite.model.crd.States
import theodolite.model.crd.ExecutionStates
import java.time.Duration
class StateHandlerTest {
......@@ -47,7 +47,7 @@ class StateHandlerTest {
@DisplayName("Test empty execution state")
fun executionWithoutExecutionStatusTest() {
val handler = ExecutionStateHandler(client = server.client)
assertEquals(States.NO_STATE, handler.getExecutionState("example-execution"))
assertEquals(ExecutionStates.NO_STATE, handler.getExecutionState("example-execution"))
}
@Test
......@@ -62,8 +62,8 @@ class StateHandlerTest {
fun executionStatusTest() {
val handler = ExecutionStateHandler(client = server.client)
assertTrue(handler.setExecutionState("example-execution", States.INTERRUPTED))
assertEquals(States.INTERRUPTED, handler.getExecutionState("example-execution"))
assertTrue(handler.setExecutionState("example-execution", ExecutionStates.INTERRUPTED))
assertEquals(ExecutionStates.INTERRUPTED, handler.getExecutionState("example-execution"))
}
@Test
......
package theodolite.util
import io.quarkus.test.junit.QuarkusTest
import org.junit.Rule
import org.junit.jupiter.api.Assertions
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test
import theodolite.execution.operator.ExecutionCRDummy
import theodolite.model.crd.ExecutionCRD
import theodolite.model.crd.States
import theodolite.model.crd.ExecutionStates
@QuarkusTest
......@@ -16,11 +12,11 @@ class ExecutionStateComparatorTest {
@Test
fun testCompare() {
val comparator = ExecutionStateComparator(States.RESTART)
val comparator = ExecutionStateComparator(ExecutionStates.RESTART)
val execution1 = ExecutionCRDummy("dummy1", "default-benchmark")
val execution2 = ExecutionCRDummy("dummy2", "default-benchmark")
execution1.getStatus().executionState = States.RESTART.value
execution2.getStatus().executionState = States.PENDING.value
execution1.getStatus().executionState = ExecutionStates.RESTART.value
execution2.getStatus().executionState = ExecutionStates.PENDING.value
val list = listOf(execution2.getCR(), execution1.getCR())
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment