diff --git a/docs/api-reference/crds.md b/docs/api-reference/crds.md index fb3f02ac941870dd085d06027d972e6003c7aadb..c5cc7596f4c23408597f48b366f6b97e889e7a66 100644 --- a/docs/api-reference/crds.md +++ b/docs/api-reference/crds.md @@ -146,6 +146,15 @@ Resource Types: <i>Default</i>: <br/> </td> <td>false</td> + </tr><tr> + <td><b>waitForResourcesEnabled</b></td> + <td>boolean</td> + <td> + If true, Theodolite waits to create the resource for the SUT until the infrastructure resources are ready, and analogously, Theodolite waits to create the load-gen resource until the resources of the SUT are ready.<br/> + <br/> + <i>Default</i>: false<br/> + </td> + <td>false</td> </tr></tbody> </table> diff --git a/theodolite/crd/crd-benchmark.yaml b/theodolite/crd/crd-benchmark.yaml index c901e61360c05b2f1cf2b1767a20f624eb262231..d2418ee005e2c0168254a9423b9c383ace2d3ca7 100644 --- a/theodolite/crd/crd-benchmark.yaml +++ b/theodolite/crd/crd-benchmark.yaml @@ -26,6 +26,10 @@ spec: 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: "" + waitForResourcesEnabled: + description: If true, Theodolite waits to create the resource for the SUT until the infrastructure resources are ready, and analogously, Theodolite waits to create the load-gen resource until the resources of the SUT are ready. + type: boolean + default: false infrastructure: description: (Optional) A list of file names that reference Kubernetes resources that are deployed on the cluster to create the required infrastructure. type: object diff --git a/theodolite/src/main/kotlin/theodolite/benchmark/KubernetesBenchmark.kt b/theodolite/src/main/kotlin/theodolite/benchmark/KubernetesBenchmark.kt index 6857b9bf8918593dbe5085f40eb28fd8bd809d85..6e8048547fb857d4c659e32885dc46ea1b1c9ae4 100644 --- a/theodolite/src/main/kotlin/theodolite/benchmark/KubernetesBenchmark.kt +++ b/theodolite/src/main/kotlin/theodolite/benchmark/KubernetesBenchmark.kt @@ -10,6 +10,7 @@ import mu.KotlinLogging import theodolite.k8s.K8sManager import theodolite.patcher.PatcherFactory import theodolite.util.* +import kotlin.properties.Delegates private val logger = KotlinLogging.logger {} @@ -37,6 +38,7 @@ private var DEFAULT_THEODOLITE_APP_RESOURCES = "./benchmark-resources" @RegisterForReflection class KubernetesBenchmark : KubernetesResource, Benchmark { lateinit var name: String + var waitForResourcesEnabled = false lateinit var resourceTypes: List<TypeName> lateinit var loadTypes: List<TypeName> var kafkaConfig: KafkaConfig? = null @@ -64,10 +66,8 @@ class KubernetesBenchmark : KubernetesResource, Benchmark { override fun setupInfrastructure() { this.infrastructure.beforeActions.forEach { it.exec(client = client) } - val kubernetesManager = K8sManager(this.client) - loadResources(this.infrastructure.resources) - .map { it.second } - .forEach { kubernetesManager.deploy(it) } + RolloutManager(waitForResourcesEnabled, this.client) + .rollout(loadResources(this.infrastructure.resources).map { it.second }) } override fun teardownInfrastructure() { @@ -129,7 +129,9 @@ class KubernetesBenchmark : KubernetesResource, Benchmark { afterTeardownDelay = afterTeardownDelay, kafkaConfig = if (kafkaConfig != null) mapOf("bootstrap.servers" to kafkaConfig.bootstrapServer) else mapOf(), topics = kafkaConfig?.topics ?: listOf(), - client = this.client + client = this.client, + rolloutMode = waitForResourcesEnabled + ) } diff --git a/theodolite/src/main/kotlin/theodolite/benchmark/KubernetesBenchmarkDeployment.kt b/theodolite/src/main/kotlin/theodolite/benchmark/KubernetesBenchmarkDeployment.kt index b30032c524b1e421301e0e9d1ffe83772b43d900..1d7b22233c084625cf16ca7194c76c14601bbaad 100644 --- a/theodolite/src/main/kotlin/theodolite/benchmark/KubernetesBenchmarkDeployment.kt +++ b/theodolite/src/main/kotlin/theodolite/benchmark/KubernetesBenchmarkDeployment.kt @@ -28,6 +28,7 @@ class KubernetesBenchmarkDeployment( private val sutAfterActions: List<Action>, private val loadGenBeforeActions: List<Action>, private val loadGenAfterActions: List<Action>, + private val rolloutMode: Boolean, val appResources: List<HasMetadata>, val loadGenResources: List<HasMetadata>, private val loadGenerationDelay: Long, @@ -47,19 +48,20 @@ class KubernetesBenchmarkDeployment( * - Deploy the needed resources. */ override fun setup() { + val rolloutManager = RolloutManager(rolloutMode, client) if (this.topics.isNotEmpty()) { val kafkaTopics = this.topics .filter { !it.removeOnly } .map { NewTopic(it.name, it.numPartitions, it.replicationFactor) } kafkaController.createTopics(kafkaTopics) } + sutBeforeActions.forEach { it.exec(client = client) } - appResources.forEach { kubernetesManager.deploy(it) } + rolloutManager.rollout(appResources) logger.info { "Wait ${this.loadGenerationDelay} seconds before starting the load generator." } Thread.sleep(Duration.ofSeconds(this.loadGenerationDelay).toMillis()) loadGenBeforeActions.forEach { it.exec(client = client) } - loadGenResources.forEach { kubernetesManager.deploy(it) } - + rolloutManager.rollout(loadGenResources) } /** diff --git a/theodolite/src/main/kotlin/theodolite/benchmark/RolloutManager.kt b/theodolite/src/main/kotlin/theodolite/benchmark/RolloutManager.kt new file mode 100644 index 0000000000000000000000000000000000000000..f282fb27971218754a0e35801342efc83837b64b --- /dev/null +++ b/theodolite/src/main/kotlin/theodolite/benchmark/RolloutManager.kt @@ -0,0 +1,42 @@ +package theodolite.benchmark + +import io.fabric8.kubernetes.api.model.HasMetadata +import io.fabric8.kubernetes.api.model.Pod +import io.fabric8.kubernetes.api.model.apps.DaemonSet +import io.fabric8.kubernetes.api.model.apps.Deployment +import io.fabric8.kubernetes.api.model.apps.ReplicaSet +import io.fabric8.kubernetes.api.model.apps.StatefulSet +import io.fabric8.kubernetes.api.model.batch.v1.Job +import io.fabric8.kubernetes.client.NamespacedKubernetesClient +import theodolite.k8s.K8sManager + +private var SLEEP_TIME_MS = 500L + + +class RolloutManager(private val blockUntilResourcesReady: Boolean, private val client: NamespacedKubernetesClient) { + + fun rollout(resources: List<HasMetadata>) { + resources + .forEach { K8sManager(client).deploy(it) } + + if (blockUntilResourcesReady) { + resources + .forEach { + when (it) { + is Deployment -> waitFor { client.apps().deployments().withName(it.metadata.name).isReady } + is StatefulSet -> waitFor { client.apps().statefulSets().withName(it.metadata.name).isReady } + is DaemonSet -> waitFor { client.apps().daemonSets().withName(it.metadata.name).isReady } + is ReplicaSet -> waitFor { client.apps().replicaSets().withName(it.metadata.name).isReady } + is Job -> waitFor { client.batch().v1().cronjobs().withName(it.metadata.name).isReady } + } + } + } + } + + private fun waitFor(isResourceReady: () -> Boolean) { + while (!isResourceReady()) { + Thread.sleep(SLEEP_TIME_MS) + } + } + +} \ No newline at end of file diff --git a/theodolite/src/test/kotlin/theodolite/execution/operator/BenchmarkCRDummy.kt b/theodolite/src/test/kotlin/theodolite/execution/operator/BenchmarkCRDummy.kt index cbddbfbfc5d6f838677c6d04b0a0c79f59d8bc66..d6841429166d1549e84ad27887fbf0cba86b174d 100644 --- a/theodolite/src/test/kotlin/theodolite/execution/operator/BenchmarkCRDummy.kt +++ b/theodolite/src/test/kotlin/theodolite/execution/operator/BenchmarkCRDummy.kt @@ -20,11 +20,12 @@ class BenchmarkCRDummy(name: String) { kafkaConfig.bootstrapServer = "" kafkaConfig.topics = emptyList() + benchmarkCR.spec = benchmark benchmarkCR.metadata.name = name benchmarkCR.kind = "Benchmark" benchmarkCR.apiVersion = "v1" - + benchmark.waitForResourcesEnabled = false benchmark.infrastructure = Resources() benchmark.sut = Resources() diff --git a/theodolite/src/test/resources/k8s-resource-files/test-benchmark.yaml b/theodolite/src/test/resources/k8s-resource-files/test-benchmark.yaml index ea9ee8471d3da1dc6011348bd978696bd0fa6f36..102a6a249ab06301396eaf375e7bd2590b334b22 100644 --- a/theodolite/src/test/resources/k8s-resource-files/test-benchmark.yaml +++ b/theodolite/src/test/resources/k8s-resource-files/test-benchmark.yaml @@ -3,6 +3,7 @@ kind: benchmark metadata: name: example-benchmark spec: + waitForResourcesEnabled: false sut: resources: - configMap: