diff --git a/theodolite/src/main/kotlin/rocks/theodolite/kubernetes/KubernetesBenchmarkDeployment.kt b/theodolite/src/main/kotlin/rocks/theodolite/kubernetes/KubernetesBenchmarkDeployment.kt index 28ac651a043134d061123956cea0cfc6d9535ce6..6559d1be779d590d1109313f3311a8d0ae026df4 100644 --- a/theodolite/src/main/kotlin/rocks/theodolite/kubernetes/KubernetesBenchmarkDeployment.kt +++ b/theodolite/src/main/kotlin/rocks/theodolite/kubernetes/KubernetesBenchmarkDeployment.kt @@ -48,7 +48,8 @@ class KubernetesBenchmarkDeployment( /** * Setup a [KubernetesBenchmark] using the [TopicManager] and the [K8sManager]: * - Create the needed topics. - * - Deploy the needed resources. + * - Deploy the needed [KubernetesResource]s (deployment order: SUT resources, loadgenerator resources; + * No guaranteed order of files inside configmaps). */ override fun setup() { val rolloutManager = RolloutManager(rolloutMode, client) @@ -71,13 +72,14 @@ class KubernetesBenchmarkDeployment( * Tears down a [KubernetesBenchmark]: * - Reset the Kafka Lag Exporter. * - Remove the used topics. - * - Remove the [KubernetesResource]s. + * - Remove the [KubernetesResource]s (removal order: loadgenerator resources, SUT resources; + * No guaranteed order of files inside configmaps). */ override fun teardown() { val podCleaner = ResourceByLabelHandler(client) - loadGenResources.forEach { kubernetesManager.remove(it, false) } + loadGenResources.reversed().forEach { kubernetesManager.remove(it, false) } loadGenAfterActions.forEach { it.exec(client = client) } - appResources.forEach { kubernetesManager.remove(it,false) } + appResources.reversed().forEach { kubernetesManager.remove(it,false) } sutAfterActions.forEach { it.exec(client = client) } if (this.topics.isNotEmpty()) { kafkaController.removeTopics(this.topics.map { topic -> topic.name }) @@ -99,4 +101,4 @@ class KubernetesBenchmarkDeployment( logger.info { "Teardown complete. Wait $afterTeardownDelay seconds to let everything cool down." } Thread.sleep(Duration.ofSeconds(afterTeardownDelay).toMillis()) } -} +} \ No newline at end of file diff --git a/theodolite/src/test/kotlin/rocks/theodolite/kubernetes/BenchmarkResourceTeardownTest.kt b/theodolite/src/test/kotlin/rocks/theodolite/kubernetes/BenchmarkResourceTeardownTest.kt new file mode 100644 index 0000000000000000000000000000000000000000..386b66b2bac5985ff85dfb639a3b9735191f3414 --- /dev/null +++ b/theodolite/src/test/kotlin/rocks/theodolite/kubernetes/BenchmarkResourceTeardownTest.kt @@ -0,0 +1,173 @@ +package rocks.theodolite.kubernetes + +import com.fasterxml.jackson.databind.ObjectMapper +import com.fasterxml.jackson.dataformat.yaml.YAMLFactory +import io.fabric8.kubernetes.api.model.ConfigMap +import io.fabric8.kubernetes.api.model.Service +import io.fabric8.kubernetes.api.model.apps.Deployment +import io.fabric8.kubernetes.client.Watcher +import io.fabric8.kubernetes.client.WatcherException +import io.fabric8.kubernetes.client.server.mock.KubernetesServer +import io.quarkus.test.junit.QuarkusTest +import io.quarkus.test.kubernetes.client.KubernetesTestServer +import io.quarkus.test.kubernetes.client.WithKubernetesTestServer +import mu.KotlinLogging +import org.junit.jupiter.api.AfterEach +import org.junit.jupiter.api.Assertions.assertTrue +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test +import rocks.theodolite.kubernetes.model.KubernetesBenchmark + + +private val logger = KotlinLogging.logger {} + +@QuarkusTest +@WithKubernetesTestServer() +class BenchmarkResourceTeardownTest { + + @KubernetesTestServer + private lateinit var server: KubernetesServer + + private val objectMapper: ObjectMapper = ObjectMapper(YAMLFactory()) + + val addedOrder: MutableList<String> = mutableListOf() + val deletedOrder: MutableList<String> = mutableListOf() + + @BeforeEach + fun setUp() { + server.before() + + server.client.resources(Deployment::class.java).watch(object : Watcher<Deployment> { + override fun eventReceived(p0: Watcher.Action?, p1: Deployment?) { + if (p0 != null && p1 != null) { + if (p0.name == "ADDED") { + addedOrder.add(p1.metadata.name) + } else if (p0.name == "DELETED") { + deletedOrder.add(p1.metadata.name) + } + } + } + + override fun onClose(p0: WatcherException?) { + logger.info { p0 } + } + + }) + server.client.resources(Service::class.java).watch(object : Watcher<Service> { + override fun eventReceived(p0: Watcher.Action?, p1: Service?) { + if (p0 != null && p1 != null) { + if (p0.name == "ADDED") { + addedOrder.add(p1.metadata.name) + } else if (p0.name == "DELETED") { + deletedOrder.add(p1.metadata.name) + } + } + } + + override fun onClose(p0: WatcherException?) { + logger.info { p0 } + } + }) + } + + @AfterEach + fun tearDown() { + server.after() + addedOrder.clear() + deletedOrder.clear() + } + + fun setupResources(benchmarkPath: String): BenchmarkDeployment { + val benchmark: KubernetesBenchmark = + objectMapper.readValue(javaClass.getResourceAsStream(benchmarkPath), KubernetesBenchmark::class.java) + + val sut1ConfigMap = objectMapper.readValue( + javaClass.getResourceAsStream("/resource-teardown-test-files/sut-configmap-1.yaml"), + ConfigMap::class.java + ) + server.client.configMaps().createOrReplace(sut1ConfigMap) + + val sut2ConfigMap = objectMapper.readValue( + javaClass.getResourceAsStream("/resource-teardown-test-files/sut-configmap-2.yaml"), + ConfigMap::class.java + ) + server.client.configMaps().createOrReplace(sut2ConfigMap) + + val loadgenConfigMap = objectMapper.readValue( + javaClass.getResourceAsStream("/resource-teardown-test-files/load-generator-configmap.yaml"), + ConfigMap::class.java + ) + server.client.configMaps().createOrReplace(loadgenConfigMap) + + return KubernetesBenchmarkDeploymentBuilder(benchmark, server.client).buildDeployment( + 0, + emptyList(), + 1, + emptyList(), + emptyList(), + 2, + 2, + false + ) + } + + @Test + fun testFullFilesProperties() { + // Make sure lists are cleared for the current test. + assertTrue(addedOrder.size == 0) + assertTrue(deletedOrder.size == 0) + + // Setup + val benchmarkDeployment = + setupResources("/resource-teardown-test-files/example-benchmark-full-files-property.yaml") + + // Run code of interest + benchmarkDeployment.setup() + benchmarkDeployment.teardown() + + // Assertions + assertTrue(addedOrder.size > 0) + assertTrue(deletedOrder.size > 0) + assertTrue(addedOrder == deletedOrder.reversed()) + } + + @Test + fun testPartialFilesProperties() { + // Make sure lists are cleared for the current test. + assertTrue(addedOrder.size == 0) + assertTrue(deletedOrder.size == 0) + + // Setup + val benchmarkDeployment = + setupResources("/resource-teardown-test-files/example-benchmark-partial-files-property.yaml") + + // Run code of interest + benchmarkDeployment.setup() + benchmarkDeployment.teardown() + + // Assertions + assertTrue(addedOrder.size > 0) + assertTrue(deletedOrder.size > 0) + assertTrue(addedOrder == deletedOrder.reversed()) + } + + @Test + fun testNoFilesProperties() { + // Make sure lists are cleared for the current test. + assertTrue(addedOrder.size == 0) + assertTrue(deletedOrder.size == 0) + + // Setup + val benchmarkDeployment = + setupResources("/resource-teardown-test-files/example-benchmark-no-files-property.yaml") + + // Run code of interest + benchmarkDeployment.setup() + benchmarkDeployment.teardown() + + // Assertions + assertTrue(addedOrder.size > 0) + assertTrue(deletedOrder.size > 0) + assertTrue(addedOrder == deletedOrder.reversed()) + } +} diff --git a/theodolite/src/test/resources/resource-teardown-test-files/example-benchmark-full-files-property.yaml b/theodolite/src/test/resources/resource-teardown-test-files/example-benchmark-full-files-property.yaml new file mode 100644 index 0000000000000000000000000000000000000000..9afd25a72309100d6cea5dfba69a75a0db6cdb5b --- /dev/null +++ b/theodolite/src/test/resources/resource-teardown-test-files/example-benchmark-full-files-property.yaml @@ -0,0 +1,25 @@ +name: example-benchmark-full-files-property +waitForResourcesEnabled: false +sut: + beforeActions: [] + afterActions: [] + resources: + - configMap: + name: "sut-configmap-1" + files: + - "sut-file-1.yaml" + - "sut-file-2.yaml" + - configMap: + name: "sut-configmap-2" + files: + - "sut-file-3.yaml" +loadGenerator: + beforeActions: [] + afterActions: [] + resources: + - configMap: + name: "load-generator-configmap" + files: + - "load-generator-file-1.yaml" + - "load-generator-file-2.yaml" + diff --git a/theodolite/src/test/resources/resource-teardown-test-files/example-benchmark-no-files-property.yaml b/theodolite/src/test/resources/resource-teardown-test-files/example-benchmark-no-files-property.yaml new file mode 100644 index 0000000000000000000000000000000000000000..808bb9331a39dde156bc8b1912bb406282d7de47 --- /dev/null +++ b/theodolite/src/test/resources/resource-teardown-test-files/example-benchmark-no-files-property.yaml @@ -0,0 +1,17 @@ +name: example-benchmark-no-files-property +waitForResourcesEnabled: false +sut: + beforeActions: [] + afterActions: [] + resources: + - configMap: + name: "sut-configmap-1" + - configMap: + name: "sut-configmap-2" +loadGenerator: + beforeActions: [] + afterActions: [] + resources: + - configMap: + name: "load-generator-configmap" + diff --git a/theodolite/src/test/resources/resource-teardown-test-files/example-benchmark-partial-files-property.yaml b/theodolite/src/test/resources/resource-teardown-test-files/example-benchmark-partial-files-property.yaml new file mode 100644 index 0000000000000000000000000000000000000000..09b03c46deca7159534128ba29192682f52af53d --- /dev/null +++ b/theodolite/src/test/resources/resource-teardown-test-files/example-benchmark-partial-files-property.yaml @@ -0,0 +1,21 @@ +name: example-benchmark-partial-files-property +waitForResourcesEnabled: false +sut: + beforeActions: [] + afterActions: [] + resources: + - configMap: + name: "sut-configmap-1" + - configMap: + name: "sut-configmap-2" + files: + - "sut-file-3.yaml" +loadGenerator: + beforeActions: [] + afterActions: [] + resources: + - configMap: + name: "load-generator-configmap" + files: + - "load-generator-file-2.yaml" + diff --git a/theodolite/src/test/resources/resource-teardown-test-files/load-generator-configmap.yaml b/theodolite/src/test/resources/resource-teardown-test-files/load-generator-configmap.yaml new file mode 100644 index 0000000000000000000000000000000000000000..d3392e6fef3d050d5f2b44ab362df1d862ab113f --- /dev/null +++ b/theodolite/src/test/resources/resource-teardown-test-files/load-generator-configmap.yaml @@ -0,0 +1,12 @@ +kind: ConfigMap +metadata: + name: load-generator-configmap +data: + load-generator-file-1.yaml: | + kind: Deployment + metadata: + name: load-generator-file-1 + load-generator-file-2.yaml: | + kind: Service + metadata: + name: load-generator-file-2 diff --git a/theodolite/src/test/resources/resource-teardown-test-files/sut-configmap-1.yaml b/theodolite/src/test/resources/resource-teardown-test-files/sut-configmap-1.yaml new file mode 100644 index 0000000000000000000000000000000000000000..e090c0fa94562e7ddf3fe225ed5be0837018199d --- /dev/null +++ b/theodolite/src/test/resources/resource-teardown-test-files/sut-configmap-1.yaml @@ -0,0 +1,12 @@ +kind: ConfigMap +metadata: + name: sut-configmap-1 +data: + sut-file-1.yaml: | + kind: Deployment + metadata: + name: sut-file-1 + sut-file-2.yaml: | + kind: Service + metadata: + name: sut-file-2 diff --git a/theodolite/src/test/resources/resource-teardown-test-files/sut-configmap-2.yaml b/theodolite/src/test/resources/resource-teardown-test-files/sut-configmap-2.yaml new file mode 100644 index 0000000000000000000000000000000000000000..3c52c47d9f96f45058b4a6017d41b29ad25b8468 --- /dev/null +++ b/theodolite/src/test/resources/resource-teardown-test-files/sut-configmap-2.yaml @@ -0,0 +1,8 @@ +kind: ConfigMap +metadata: + name: sut-configmap-2 +data: + sut-file-3.yaml: | + kind: Deployment + metadata: + name: sut-file-3