diff --git a/theodolite/crd/crd-benchmark.yaml b/theodolite/crd/crd-benchmark.yaml index 9de29fc031e26b9e4e16517492740b0805fd4af9..65504d6ec23245a1206f8cc22fdb8f186b3cf297 100644 --- a/theodolite/crd/crd-benchmark.yaml +++ b/theodolite/crd/crd-benchmark.yaml @@ -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 diff --git a/theodolite/crd/crd-execution.yaml b/theodolite/crd/crd-execution.yaml index 47d0306f5150a8126f021c40bf3c4a4ce0e1abb1..d9cd41903bb2fdc18bd6640bdbe2eb764b2106ab 100644 --- a/theodolite/crd/crd-execution.yaml +++ b/theodolite/crd/crd-execution.yaml @@ -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 diff --git a/theodolite/examples/operator/example-benchmark.yaml b/theodolite/examples/operator/example-benchmark.yaml index 91d9f8f1f7dfed31d9edcb59947af4e832ca2843..5fc004e3b510dca3365a9d1b053ad3f69416a3db 100644 --- a/theodolite/examples/operator/example-benchmark.yaml +++ b/theodolite/examples/operator/example-benchmark.yaml @@ -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 diff --git a/theodolite/src/main/kotlin/theodolite/benchmark/KubernetesBenchmark.kt b/theodolite/src/main/kotlin/theodolite/benchmark/KubernetesBenchmark.kt index b9a2fc7f18b92664b4d93b11755280a9e18b170d..125fdfd8adbedf85267bb87ac241af371245c2ad 100644 --- a/theodolite/src/main/kotlin/theodolite/benchmark/KubernetesBenchmark.kt +++ b/theodolite/src/main/kotlin/theodolite/benchmark/KubernetesBenchmark.kt @@ -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." } diff --git a/theodolite/src/main/kotlin/theodolite/execution/operator/BenchmarkStateHandler.kt b/theodolite/src/main/kotlin/theodolite/execution/operator/BenchmarkStateHandler.kt new file mode 100644 index 0000000000000000000000000000000000000000..86221a3a84e5ec62ca759ab9622aab83b1341baf --- /dev/null +++ b/theodolite/src/main/kotlin/theodolite/execution/operator/BenchmarkStateHandler.kt @@ -0,0 +1,28 @@ +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 diff --git a/theodolite/src/main/kotlin/theodolite/execution/operator/TheodoliteController.kt b/theodolite/src/main/kotlin/theodolite/execution/operator/TheodoliteController.kt index eb51a445ee05f1811a8780ff64c570f0bbdff4d0..882cd9c597d0d0240a77e08267fad1fa76006768 100644 --- a/theodolite/src/main/kotlin/theodolite/execution/operator/TheodoliteController.kt +++ b/theodolite/src/main/kotlin/theodolite/execution/operator/TheodoliteController.kt @@ -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 diff --git a/theodolite/src/main/kotlin/theodolite/execution/operator/TheodoliteOperator.kt b/theodolite/src/main/kotlin/theodolite/execution/operator/TheodoliteOperator.kt index 2aaba77c03884d94c4d5745db270e84324482878..3a7b37cc0612dd319814e6d99d450a8e3c217f8e 100644 --- a/theodolite/src/main/kotlin/theodolite/execution/operator/TheodoliteOperator.kt +++ b/theodolite/src/main/kotlin/theodolite/execution/operator/TheodoliteOperator.kt @@ -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 diff --git a/theodolite/src/main/kotlin/theodolite/model/crd/BenchmarkCRD.kt b/theodolite/src/main/kotlin/theodolite/model/crd/BenchmarkCRD.kt index 377708af7b7e1a50ae1e33064b2668c364e0685a..8c58db0ebdb07a61881dd532c18004196ade570e 100644 --- a/theodolite/src/main/kotlin/theodolite/model/crd/BenchmarkCRD.kt +++ b/theodolite/src/main/kotlin/theodolite/model/crd/BenchmarkCRD.kt @@ -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 diff --git a/theodolite/src/main/kotlin/theodolite/model/crd/BenchmarkStatus.kt b/theodolite/src/main/kotlin/theodolite/model/crd/BenchmarkStatus.kt new file mode 100644 index 0000000000000000000000000000000000000000..9be6c6707c46a2f9ee131ec13e04d78c3e210b22 --- /dev/null +++ b/theodolite/src/main/kotlin/theodolite/model/crd/BenchmarkStatus.kt @@ -0,0 +1,11 @@ +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 diff --git a/theodolite/src/main/kotlin/theodolite/model/crd/States.kt b/theodolite/src/main/kotlin/theodolite/model/crd/States.kt index 79af297915b6703b209acb0c13913482e54db2be..fe30513a7da6d448f13d17aa2ee4d456d9ee2e05 100644 --- a/theodolite/src/main/kotlin/theodolite/model/crd/States.kt +++ b/theodolite/src/main/kotlin/theodolite/model/crd/States.kt @@ -1,11 +1,17 @@ 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 diff --git a/theodolite/src/test/kotlin/theodolite/execution/operator/ControllerTest.kt b/theodolite/src/test/kotlin/theodolite/execution/operator/ControllerTest.kt index 7350f564a71e2f0cbf640b782f5dbf19cbdc7ecb..79159bfe20c886e21751fd033ff14314ce321f16 100644 --- a/theodolite/src/test/kotlin/theodolite/execution/operator/ControllerTest.kt +++ b/theodolite/src/test/kotlin/theodolite/execution/operator/ControllerTest.kt @@ -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 diff --git a/theodolite/src/test/kotlin/theodolite/execution/operator/ExecutionEventHandlerTest.kt b/theodolite/src/test/kotlin/theodolite/execution/operator/ExecutionEventHandlerTest.kt index 5598f48a2d291db4eab8563dd3325534f49b2eb6..77e2f364edd7c92d6acb9570e50e1cf6106e96d1 100644 --- a/theodolite/src/test/kotlin/theodolite/execution/operator/ExecutionEventHandlerTest.kt +++ b/theodolite/src/test/kotlin/theodolite/execution/operator/ExecutionEventHandlerTest.kt @@ -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)