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 c49810fc5838e63211b3a796e5d54085219418c8..6bcff9aeed2e756f115f27fbf25cc2aa35230d2d 100644 --- a/theodolite-quarkus/src/main/kotlin/theodolite/execution/operator/TheodoliteController.kt +++ b/theodolite-quarkus/src/main/kotlin/theodolite/execution/operator/TheodoliteController.kt @@ -1,6 +1,5 @@ package theodolite.execution.operator -import io.fabric8.kubernetes.client.NamespacedKubernetesClient import io.fabric8.kubernetes.client.dsl.MixedOperation import io.fabric8.kubernetes.client.dsl.Resource import mu.KotlinLogging @@ -17,15 +16,12 @@ private val logger = KotlinLogging.logger {} /** * The controller implementation for Theodolite. * - * @see NamespacedKubernetesClient - * @see CustomResourceDefinitionContext * @see BenchmarkExecution * @see KubernetesBenchmark * @see ConcurrentLinkedDeque */ class TheodoliteController( - private val namespace: String, val path: String, private val executionCRDClient: MixedOperation<ExecutionCRD, BenchmarkExecutionList, Resource<ExecutionCRD>>, private val benchmarkCRDClient: MixedOperation<BenchmarkCRD, KubernetesBenchmarkList, Resource<BenchmarkCRD>>, @@ -116,11 +112,10 @@ class TheodoliteController( */ private fun getBenchmarks(): List<KubernetesBenchmark> { return this.benchmarkCRDClient - .inNamespace(namespace) .list() .items .map { it.spec.name = it.metadata.name; it } - .map { it.spec.path = path; it } + .map { it.spec.path = path; it } // TODO check if we can remove the path field from the KubernetesBenchmark .map { it.spec } } @@ -140,7 +135,6 @@ class TheodoliteController( .map { it.name } return executionCRDClient - .inNamespace(namespace) .list() .items .asSequence() diff --git a/theodolite-quarkus/src/main/kotlin/theodolite/execution/operator/TheodoliteOperator.kt b/theodolite-quarkus/src/main/kotlin/theodolite/execution/operator/TheodoliteOperator.kt index 60e238c27877c52a55fba307b036f2d498a1f76a..0415680f5947b3a7d759ad0dbf67cd27c2c58d24 100644 --- a/theodolite-quarkus/src/main/kotlin/theodolite/execution/operator/TheodoliteOperator.kt +++ b/theodolite-quarkus/src/main/kotlin/theodolite/execution/operator/TheodoliteOperator.kt @@ -4,6 +4,7 @@ import io.fabric8.kubernetes.client.DefaultKubernetesClient import io.fabric8.kubernetes.client.NamespacedKubernetesClient import io.fabric8.kubernetes.client.dsl.MixedOperation import io.fabric8.kubernetes.client.dsl.Resource +import io.fabric8.kubernetes.client.informers.SharedInformerFactory import io.fabric8.kubernetes.internal.KubernetesDeserializer import mu.KotlinLogging import theodolite.model.crd.BenchmarkCRD @@ -27,7 +28,11 @@ private val logger = KotlinLogging.logger {} */ class TheodoliteOperator { private val namespace = System.getenv("NAMESPACE") ?: DEFAULT_NAMESPACE - val client: NamespacedKubernetesClient = DefaultKubernetesClient().inNamespace(namespace) + private val appResource = System.getenv("THEODOLITE_APP_RESOURCES") ?: "./config" + + private val client: NamespacedKubernetesClient = DefaultKubernetesClient().inNamespace(namespace) + private lateinit var controller: TheodoliteController + private lateinit var executionStateHandler: ExecutionStateHandler fun start() { @@ -41,7 +46,7 @@ class TheodoliteOperator { /** * Start the operator. */ - private fun startOperator() { + private fun startOperator() { logger.info { "Using $namespace as namespace." } client.use { KubernetesDeserializer.registerCustomKind( @@ -56,56 +61,75 @@ class TheodoliteOperator { BenchmarkCRD::class.java ) - val executionCRDClient: MixedOperation< - ExecutionCRD, - BenchmarkExecutionList, - Resource<ExecutionCRD>> - = client.customResources( - ExecutionCRD::class.java, - BenchmarkExecutionList::class.java - ) + ClusterSetup( + executionCRDClient = getExecutionClient(client), + benchmarkCRDClient = getBenchmarkClient(client), + client = client + ).clearClusterState() - val benchmarkCRDClient: MixedOperation< - BenchmarkCRD, - KubernetesBenchmarkList, - Resource<BenchmarkCRD>> - = client.customResources( - BenchmarkCRD::class.java, - KubernetesBenchmarkList::class.java - ) + getController( + client = client, + executionStateHandler = getExecutionStateHandler(client = client) + ).run() + getExecutionEventHandler(client).startAllRegisteredInformers() + } + } - val executionStateHandler = ExecutionStateHandler( - client = client) - - val appResource = System.getenv("THEODOLITE_APP_RESOURCES") ?: "./config" - val controller = - TheodoliteController( - namespace = client.namespace, - path = appResource, - benchmarkCRDClient = benchmarkCRDClient, - executionCRDClient = executionCRDClient, - executionStateHandler = executionStateHandler) - - val informerFactory = client.informers() - val informerExecution = informerFactory.sharedIndexInformerForCustomResource( - ExecutionCRD::class.java, - BenchmarkExecutionList::class.java, - RESYNC_PERIOD - ) + fun getExecutionEventHandler(client: NamespacedKubernetesClient): SharedInformerFactory { + val factory = client.informers() + .inNamespace(client.namespace) - informerExecution.addEventHandler(ExecutionHandler( + factory.sharedIndexInformerForCustomResource( + ExecutionCRD::class.java, + RESYNC_PERIOD + ).addEventHandler( + ExecutionHandler( controller = controller, - stateHandler = executionStateHandler)) - - ClusterSetup( - executionCRDClient = executionCRDClient, - benchmarkCRDClient = benchmarkCRDClient, - client = client - ).clearClusterState() + stateHandler = ExecutionStateHandler(client) + ) + ) + return factory + } - informerFactory.startAllRegisteredInformers() - controller.run() + fun getExecutionStateHandler(client: NamespacedKubernetesClient): ExecutionStateHandler { + if (!::executionStateHandler.isInitialized) { + this.executionStateHandler = ExecutionStateHandler(client = client) + } + return executionStateHandler + } + fun getController( + client: NamespacedKubernetesClient, + executionStateHandler: ExecutionStateHandler + ): TheodoliteController { + if (!::controller.isInitialized) { + this.controller = TheodoliteController( + path = this.appResource, + benchmarkCRDClient = getBenchmarkClient(client), + executionCRDClient = getExecutionClient(client), + executionStateHandler = executionStateHandler + ) } + return this.controller + } + + private fun getExecutionClient(client: NamespacedKubernetesClient): MixedOperation< + ExecutionCRD, + BenchmarkExecutionList, + Resource<ExecutionCRD>> { + return client.customResources( + ExecutionCRD::class.java, + BenchmarkExecutionList::class.java + ) + } + + private fun getBenchmarkClient(client: NamespacedKubernetesClient): MixedOperation< + BenchmarkCRD, + KubernetesBenchmarkList, + Resource<BenchmarkCRD>> { + return client.customResources( + BenchmarkCRD::class.java, + KubernetesBenchmarkList::class.java + ) } } diff --git a/theodolite-quarkus/src/test/kotlin/theodolite/execution/operator/ControllerDummy.kt b/theodolite-quarkus/src/test/kotlin/theodolite/execution/operator/ControllerDummy.kt deleted file mode 100644 index eec399218a2920278f98dc7366f4f487262c3178..0000000000000000000000000000000000000000 --- a/theodolite-quarkus/src/test/kotlin/theodolite/execution/operator/ControllerDummy.kt +++ /dev/null @@ -1,67 +0,0 @@ -package theodolite.execution.operator - -import io.fabric8.kubernetes.client.NamespacedKubernetesClient -import io.fabric8.kubernetes.client.dsl.MixedOperation -import io.fabric8.kubernetes.client.dsl.Resource -import io.fabric8.kubernetes.internal.KubernetesDeserializer -import theodolite.model.crd.BenchmarkCRD -import theodolite.model.crd.BenchmarkExecutionList -import theodolite.model.crd.ExecutionCRD -import theodolite.model.crd.KubernetesBenchmarkList - -private const val SCOPE = "Namespaced" -private const val EXECUTION_SINGULAR = "execution" -private const val EXECUTION_PLURAL = "executions" -private const val BENCHMARK_SINGULAR = "benchmark" -private const val BENCHMARK_PLURAL = "benchmarks" -private const val API_VERSION = "v1" -private const val GROUP = "theodolite.com" - -class ControllerDummy(client: NamespacedKubernetesClient) { - - private var controller: TheodoliteController - val executionStateHandler = ExecutionStateHandler( - client = client - ) - - fun getController(): TheodoliteController { - return this.controller - } - - init { - KubernetesDeserializer.registerCustomKind( - "$GROUP/$API_VERSION", - EXECUTION_SINGULAR, - ExecutionCRD::class.java - ) - - KubernetesDeserializer.registerCustomKind( - "$GROUP/$API_VERSION", - BENCHMARK_SINGULAR, - BenchmarkCRD::class.java - ) - - val executionCRDClient: MixedOperation< - ExecutionCRD, - BenchmarkExecutionList, - Resource<ExecutionCRD>> = client.customResources( - ExecutionCRD::class.java, - BenchmarkExecutionList::class.java - ) - - val benchmarkCRDClient = client.customResources( - BenchmarkCRD::class.java, - KubernetesBenchmarkList::class.java - ) - - val appResource = System.getenv("THEODOLITE_APP_RESOURCES") ?: "./config" - this.controller = - TheodoliteController( - namespace = client.namespace, - path = appResource, - benchmarkCRDClient = benchmarkCRDClient, - executionCRDClient = executionCRDClient, - executionStateHandler = executionStateHandler - ) - } -} \ No newline at end of file diff --git a/theodolite-quarkus/src/test/kotlin/theodolite/execution/operator/ControllerTest.kt b/theodolite-quarkus/src/test/kotlin/theodolite/execution/operator/ControllerTest.kt index 9174a4cc78933d4c028b2c2a73e1adb63047868f..7ed868c7adc4afcd7a6a606d22124c92910ecd89 100644 --- a/theodolite-quarkus/src/test/kotlin/theodolite/execution/operator/ControllerTest.kt +++ b/theodolite-quarkus/src/test/kotlin/theodolite/execution/operator/ControllerTest.kt @@ -8,6 +8,7 @@ import io.quarkus.test.junit.QuarkusTest import org.junit.jupiter.api.AfterEach import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.DisplayName import org.junit.jupiter.api.Test import theodolite.benchmark.BenchmarkExecution import theodolite.benchmark.KubernetesBenchmark @@ -30,7 +31,10 @@ class ControllerTest { @BeforeEach fun setUp() { server.before() - this.controller = ControllerDummy(server.client).getController() + this.controller = TheodoliteOperator().getController( + client = server.client, + executionStateHandler = ExecutionStateHandler(server.client) + ) // benchmark val benchmark1 = BenchmarkCRDummy(name = "Test-Benchmark") @@ -66,6 +70,38 @@ class ControllerTest { server.after() } + @Test + @DisplayName("Check namespaced property of benchmarkCRDClient") + fun testBenchmarkClientNamespaced(){ + val method = controller + .javaClass + .getDeclaredMethod("getBenchmarks") + method.isAccessible = true + method.invoke(controller) + + assert(server + .lastRequest + .toString() + .contains("namespaces") + ) + } + + @Test + @DisplayName("Check namespaced property of executionCRDClient") + fun testExecutionClientNamespaced(){ + val method = controller + .javaClass + .getDeclaredMethod("getNextExecution") + method.isAccessible = true + method.invoke(controller) + + assert(server + .lastRequest + .toString() + .contains("namespaces") + ) + } + @Test fun getBenchmarksTest() { val method = controller diff --git a/theodolite-quarkus/src/test/kotlin/theodolite/execution/operator/ExecutionEventHandlerTest.kt b/theodolite-quarkus/src/test/kotlin/theodolite/execution/operator/ExecutionEventHandlerTest.kt index fd192cd3b53db6447a75710d1813e116dc555aeb..d72dd1cd3e70d94dbd49efb866b8ae6334ab0a4c 100644 --- a/theodolite-quarkus/src/test/kotlin/theodolite/execution/operator/ExecutionEventHandlerTest.kt +++ b/theodolite-quarkus/src/test/kotlin/theodolite/execution/operator/ExecutionEventHandlerTest.kt @@ -11,8 +11,6 @@ import org.junit.jupiter.api.DisplayName import org.junit.jupiter.api.Test import theodolite.k8s.K8sManager import theodolite.k8s.K8sResourceLoader -import theodolite.model.crd.BenchmarkExecutionList -import theodolite.model.crd.ExecutionCRD import theodolite.model.crd.States import java.lang.Thread.sleep @@ -30,34 +28,27 @@ class ExecutionEventHandlerTest { lateinit var executionVersion2: KubernetesResource lateinit var stateHandler: ExecutionStateHandler lateinit var manager: K8sManager + lateinit var controller: TheodoliteController @BeforeEach fun setUp() { server.before() - val controllerDummy = ControllerDummy(server.client) - - this.factory = server.client.informers() - val informerExecution = factory - .sharedIndexInformerForCustomResource( - ExecutionCRD::class.java, - BenchmarkExecutionList::class.java, - RESYNC_PERIOD - ) - - informerExecution.addEventHandler( - ExecutionHandler( - controller = controllerDummy.getController(), - stateHandler = controllerDummy.executionStateHandler - ) + val operator = TheodoliteOperator() + this.controller = operator.getController( + client = server.client, + executionStateHandler = ExecutionStateHandler(client = server.client) ) + this.factory = operator.getExecutionEventHandler(server.client) + this.stateHandler = TheodoliteOperator().getExecutionStateHandler(client = server.client) + this.executionVersion1 = K8sResourceLoader(server.client) .loadK8sResource("Execution", testResourcePath + "test-execution.yaml") this.executionVersion2 = K8sResourceLoader(server.client) .loadK8sResource("Execution", testResourcePath + "test-execution-update.yaml") - this.stateHandler = ControllerDummy(server.client).executionStateHandler + this.stateHandler = operator.getExecutionStateHandler(server.client) this.manager = K8sManager((server.client)) } @@ -68,6 +59,20 @@ class ExecutionEventHandlerTest { factory.stopAllRegisteredInformers() } + @Test + @DisplayName("Check namespaced property of informers") + fun testNamespaced() { + manager.deploy(executionVersion1) + factory.startAllRegisteredInformers() + server.lastRequest + // the second request must be namespaced (this is the first `GET` request) + assert(server + .lastRequest + .toString() + .contains("namespaces") + ) + } + @Test @DisplayName("Test onAdd method for executions without execution state") fun testWithoutState() { diff --git a/theodolite-quarkus/src/test/kotlin/theodolite/execution/operator/StateHandlerTest.kt b/theodolite-quarkus/src/test/kotlin/theodolite/execution/operator/StateHandlerTest.kt index 7537be82c4caf221bdeea7d112df8b6af153c876..de74cf9ac87a8aca7db133a04ef3809c5e5087c2 100644 --- a/theodolite-quarkus/src/test/kotlin/theodolite/execution/operator/StateHandlerTest.kt +++ b/theodolite-quarkus/src/test/kotlin/theodolite/execution/operator/StateHandlerTest.kt @@ -7,7 +7,6 @@ import org.junit.jupiter.api.Assertions.assertTrue import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.DisplayName import org.junit.jupiter.api.Test -import theodolite.k8s.K8sContextFactory import theodolite.k8s.K8sManager import theodolite.k8s.K8sResourceLoader import theodolite.model.crd.States @@ -16,13 +15,6 @@ import java.time.Duration class StateHandlerTest { private val testResourcePath = "./src/test/resources/k8s-resource-files/" private val server = KubernetesServer(false, true) - private val context = K8sContextFactory().create( - api = "v1", - scope = "Namespaced", - group = "theodolite.com", - plural = "executions" - ) - @BeforeEach fun setUp() { @@ -38,6 +30,18 @@ class StateHandlerTest { server.after() } + @Test + @DisplayName("check if Statehandler is namespaced") + fun namespacedTest() { + val handler = ExecutionStateHandler(client = server.client) + handler.getExecutionState("example-execution") + assert(server + .lastRequest + .toString() + .contains("namespaces") + ) + } + @Test @DisplayName("Test empty execution state") fun executionWithoutExecutionStatusTest(){