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

add state resourceSet to benchmark crd to indicate whether the benchmak can be executed or not

parent c04b712f
No related branches found
No related tags found
1 merge request!176Add a Benchmark Status
Showing
with 122 additions and 13 deletions
......@@ -25,6 +25,7 @@ spec:
name:
description: This field exists only for technical reasons and should not be set by the user. The value of the field will be overwritten.
type: string
default: ""
appResource:
description: A list of file names that reference Kubernetes resources that are deployed on the cluster for the system under test (SUT).
type: array
......@@ -136,10 +137,20 @@ spec:
description: Determines if this topic should only be deleted after each experiement. For removeOnly topics the name can be a RegEx describing the topic.
type: boolean
default: false
status:
type: object
properties:
resourceSets:
description: todo
type: string
additionalPrinterColumns:
- name: Age
type: date
jsonPath: .metadata.creationTimestamp
- name: ResourceSets
type: string
description: todo
jsonPath: .status.resourceSets
subresources:
status: {}
scope: Namespaced
\ No newline at end of file
......@@ -51,7 +51,7 @@ spec:
description: The type of the resource. It must match one of the resource types specified in the referenced benchmark.
type: string
resourceValues:
descriptoin: List of resource values for the specified resource type.
description: List of resource values for the specified resource type.
type: array
items:
type: integer
......
......@@ -35,4 +35,6 @@ spec:
numPartitions: 40
replicationFactor: 1
- name: "theodolite-.*"
removeOnly: True
\ No newline at end of file
removeOnly: True
numPartitions: 40
replicationFactor: 1
\ No newline at end of file
......@@ -47,7 +47,7 @@ class KubernetesBenchmark : KubernetesResource, Benchmark {
* It first loads them via the [YamlParser] to check for their concrete type and afterwards initializes them using
* the [K8sResourceLoader]
*/
private fun loadKubernetesResources(resources: List<String>): List<Pair<String, KubernetesResource>> {
fun loadKubernetesResources(resources: List<String>): List<Pair<String, KubernetesResource>> {
val path = System.getenv("THEODOLITE_APP_RESOURCES") ?: DEFAULT_THEODOLITE_APP_RESOURCES
logger.info { "Using $path as resource path." }
......
package theodolite.execution.operator
import io.fabric8.kubernetes.client.NamespacedKubernetesClient
import theodolite.model.crd.*
class BenchmarkStateHandler(val client: NamespacedKubernetesClient) :
AbstractStateHandler<BenchmarkCRD, KubernetesBenchmarkList, ExecutionStatus>(
client = client,
crd = BenchmarkCRD::class.java,
crdList = KubernetesBenchmarkList::class.java
) {
private fun getBenchmarkResourceState() = { cr: BenchmarkCRD -> cr.status.resourceSets }
fun setResourceSetState(resourceName: String, status: States): Boolean {
setState(resourceName) { cr -> cr.status.resourceSets = status.value; cr }
return blockUntilStateIsSet(resourceName, status.value, getBenchmarkResourceState())
}
fun getResourceSetState(resourceName: String): States {
val status = this.getState(resourceName, getBenchmarkResourceState())
return if (status.isNullOrBlank()) {
States.NO_STATE
} else {
States.values().first { it.value == status }
}
}
}
\ No newline at end of file
......@@ -28,7 +28,8 @@ const val CREATED_BY_LABEL_VALUE = "theodolite"
class TheodoliteController(
private val executionCRDClient: MixedOperation<ExecutionCRD, BenchmarkExecutionList, Resource<ExecutionCRD>>,
private val benchmarkCRDClient: MixedOperation<BenchmarkCRD, KubernetesBenchmarkList, Resource<BenchmarkCRD>>,
private val executionStateHandler: ExecutionStateHandler
private val executionStateHandler: ExecutionStateHandler,
private val benchmarkStateHandler: BenchmarkStateHandler
) {
lateinit var executor: TheodoliteExecutor
......@@ -40,6 +41,7 @@ class TheodoliteController(
sleep(5000) // wait until all states are correctly set
while (true) {
reconcile()
updateBenchmarkStatus()
sleep(2000)
}
}
......@@ -47,8 +49,10 @@ class TheodoliteController(
private fun reconcile() {
do {
val execution = getNextExecution()
updateBenchmarkStatus()
if (execution != null) {
val benchmark = getBenchmarks()
.map { it.spec }
.firstOrNull { it.name == execution.benchmark }
if (benchmark != null) {
runExecution(execution, benchmark)
......@@ -115,16 +119,17 @@ class TheodoliteController(
/**
* @return all available [BenchmarkCRD]s
*/
private fun getBenchmarks(): List<KubernetesBenchmark> {
private fun getBenchmarks(): List<BenchmarkCRD> {
return this.benchmarkCRDClient
.list()
.items
.map {
it.spec.name = it.metadata.name
it.spec
it
}
}
/**
* Get the [BenchmarkExecution] for the next run. Which [BenchmarkExecution]
* is selected for the next execution depends on three points:
......@@ -139,6 +144,8 @@ class TheodoliteController(
private fun getNextExecution(): BenchmarkExecution? {
val comparator = ExecutionStateComparator(States.RESTART)
val availableBenchmarkNames = getBenchmarks()
.filter { it.status.resourceSets == States.AVAILABLE.value }
.map { it.spec }
.map { it.name }
return executionCRDClient
......@@ -156,6 +163,35 @@ class TheodoliteController(
.firstOrNull()
}
private fun updateBenchmarkStatus() {
this.benchmarkCRDClient
.list()
.items
.map { it.spec.name = it.metadata.name; it }
.map { Pair(it, checkResource(it.spec)) }
.forEach { setState(it.first, it.second ) }
}
private fun setState(resource: BenchmarkCRD, state: States) {
benchmarkStateHandler.setResourceSetState(resource.spec.name, state)
}
private fun checkResource(benchmark: KubernetesBenchmark): States {
return try {
val appResources =
benchmark.loadKubernetesResources(resources = benchmark.appResource)
val loadGenResources =
benchmark.loadKubernetesResources(resources = benchmark.loadGenResource)
if(appResources.isNotEmpty() && loadGenResources.isNotEmpty()) {
States.AVAILABLE
} else {
States.NOT_AVAILABLE
}
} catch (e: Exception) {
States.NOT_AVAILABLE
}
}
fun isExecutionRunning(executionName: String): Boolean {
if (!::executor.isInitialized) return false
return this.executor.getExecution().name == executionName
......
......@@ -32,6 +32,7 @@ class TheodoliteOperator {
private val client: NamespacedKubernetesClient = DefaultKubernetesClient().inNamespace(namespace)
private lateinit var controller: TheodoliteController
private lateinit var executionStateHandler: ExecutionStateHandler
private lateinit var benchmarkStateHandler: BenchmarkStateHandler
fun start() {
......@@ -68,7 +69,9 @@ class TheodoliteOperator {
controller = getController(
client = client,
executionStateHandler = getExecutionStateHandler(client = client)
executionStateHandler = getExecutionStateHandler(client = client),
benchmarkStateHandler = getBenchmarkStateHandler(client = client)
)
getExecutionEventHandler(controller, client).startAllRegisteredInformers()
controller.run()
......@@ -101,15 +104,24 @@ class TheodoliteOperator {
return executionStateHandler
}
fun getBenchmarkStateHandler(client: NamespacedKubernetesClient) : BenchmarkStateHandler {
if (!::benchmarkStateHandler.isInitialized) {
this.benchmarkStateHandler = BenchmarkStateHandler(client = client)
}
return benchmarkStateHandler
}
fun getController(
client: NamespacedKubernetesClient,
executionStateHandler: ExecutionStateHandler
executionStateHandler: ExecutionStateHandler,
benchmarkStateHandler: BenchmarkStateHandler
): TheodoliteController {
if (!::controller.isInitialized) {
this.controller = TheodoliteController(
benchmarkCRDClient = getBenchmarkClient(client),
executionCRDClient = getExecutionClient(client),
executionStateHandler = executionStateHandler
executionStateHandler = executionStateHandler,
benchmarkStateHandler = benchmarkStateHandler
)
}
return this.controller
......
......@@ -14,5 +14,6 @@ import theodolite.benchmark.KubernetesBenchmark
@Group("theodolite.com")
@Kind("benchmark")
class BenchmarkCRD(
var spec: KubernetesBenchmark = KubernetesBenchmark()
var spec: KubernetesBenchmark = KubernetesBenchmark(),
var status: BenchmarkStatus = BenchmarkStatus()
) : CustomResource<KubernetesBenchmark, Void>(), Namespaced, HasMetadata
\ No newline at end of file
package theodolite.model.crd
import com.fasterxml.jackson.databind.annotation.JsonDeserialize
import io.fabric8.kubernetes.api.model.KubernetesResource
import io.fabric8.kubernetes.api.model.Namespaced
@JsonDeserialize
class BenchmarkStatus: KubernetesResource, Namespaced {
var resourceSets = "-"
}
\ 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")
NO_STATE("NoState"),
// Benchmark states
AVAILABLE("AVAILABLE"),
NOT_AVAILABLE("NOT_AVAILABLE")
}
\ No newline at end of file
......@@ -33,7 +33,8 @@ class ControllerTest {
server.before()
this.controller = TheodoliteOperator().getController(
client = server.client,
executionStateHandler = ExecutionStateHandler(server.client)
executionStateHandler = ExecutionStateHandler(server.client),
benchmarkStateHandler = BenchmarkStateHandler(server.client)
)
// benchmark
......
......@@ -36,7 +36,8 @@ class ExecutionEventHandlerTest {
val operator = TheodoliteOperator()
this.controller = operator.getController(
client = server.client,
executionStateHandler = ExecutionStateHandler(client = server.client)
executionStateHandler = ExecutionStateHandler(client = server.client),
benchmarkStateHandler = BenchmarkStateHandler(client = server.client)
)
this.factory = operator.getExecutionEventHandler(this.controller, server.client)
......
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