From 083ebd8538f4962601ca160a60ca889e0d25b67f Mon Sep 17 00:00:00 2001 From: "stu126940@mail.uni-kiel.de" <stu126940@mail.uni-kiel.de> Date: Thu, 2 Sep 2021 13:49:17 +0200 Subject: [PATCH] add state resourceSet to benchmark crd to indicate whether the benchmak can be executed or not --- theodolite/crd/crd-benchmark.yaml | 11 +++++ theodolite/crd/crd-execution.yaml | 2 +- .../examples/operator/example-benchmark.yaml | 4 +- .../benchmark/KubernetesBenchmark.kt | 2 +- .../operator/BenchmarkStateHandler.kt | 28 +++++++++++++ .../operator/TheodoliteController.kt | 42 +++++++++++++++++-- .../execution/operator/TheodoliteOperator.kt | 18 ++++++-- .../theodolite/model/crd/BenchmarkCRD.kt | 3 +- .../theodolite/model/crd/BenchmarkStatus.kt | 11 +++++ .../kotlin/theodolite/model/crd/States.kt | 8 +++- .../execution/operator/ControllerTest.kt | 3 +- .../operator/ExecutionEventHandlerTest.kt | 3 +- 12 files changed, 122 insertions(+), 13 deletions(-) create mode 100644 theodolite/src/main/kotlin/theodolite/execution/operator/BenchmarkStateHandler.kt create mode 100644 theodolite/src/main/kotlin/theodolite/model/crd/BenchmarkStatus.kt diff --git a/theodolite/crd/crd-benchmark.yaml b/theodolite/crd/crd-benchmark.yaml index 9de29fc03..65504d6ec 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 47d0306f5..d9cd41903 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 91d9f8f1f..5fc004e3b 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 b9a2fc7f1..125fdfd8a 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 000000000..86221a3a8 --- /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 eb51a445e..882cd9c59 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 2aaba77c0..3a7b37cc0 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 377708af7..8c58db0eb 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 000000000..9be6c6707 --- /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 79af29791..fe30513a7 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 7350f564a..79159bfe2 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 5598f48a2..77e2f364e 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) -- GitLab