diff --git a/theodolite-quarkus/README.md b/theodolite-quarkus/README.md index 76d1e9fa54fd3ecc7208016c50abab4f39397404..fc1d1bfe4a9c20a515cf6e69208657f74694d80e 100644 --- a/theodolite-quarkus/README.md +++ b/theodolite-quarkus/README.md @@ -86,7 +86,7 @@ docker run -i --rm theodolite-quarkus-native **Production:** (Docker-Container) -| Variables name | If not set: |Usage | +| Variables name | Default value |Usage | | -----------------------------|:----------------------------------:| ------------:| | `NAMESPACE` | `default` |Determines the namespace of the Theodolite will be executed in. Used in the KubernetesBenchmark| | `THEODOLITE_EXECUTION` | `./config/BenchmarkExecution.yaml`|The complete path to the benchmarkExecution file. Used in the TheodoliteYamlExecutor. | diff --git a/theodolite-quarkus/src/main/kotlin/theodolite/benchmark/Benchmark.kt b/theodolite-quarkus/src/main/kotlin/theodolite/benchmark/Benchmark.kt index df92ed111206bb58f50a589a612d6f598575244b..db7999b205c61d94fa17791a5d549a2620601b6b 100644 --- a/theodolite-quarkus/src/main/kotlin/theodolite/benchmark/Benchmark.kt +++ b/theodolite-quarkus/src/main/kotlin/theodolite/benchmark/Benchmark.kt @@ -5,8 +5,19 @@ import theodolite.util.ConfigurationOverride import theodolite.util.LoadDimension import theodolite.util.Resource +/** + * A Benchmark contains: + * - The [Resource]s that can be scaled for the benchmark. + * - The [LoadDimension]s that can be scaled the benchmark. + * - additional [ConfigurationOverride]s. + */ @RegisterForReflection interface Benchmark { + + /** + * Builds a Deployment that can be deployed. + * @return a BenchmarkDeployment. + */ fun buildDeployment( load: LoadDimension, res: Resource, diff --git a/theodolite-quarkus/src/main/kotlin/theodolite/benchmark/BenchmarkDeployment.kt b/theodolite-quarkus/src/main/kotlin/theodolite/benchmark/BenchmarkDeployment.kt index df01fae2ece683a9bec6ad0d8489987f94249375..92d3f7a012517895fc61531026e4ea4f3e3cfb50 100644 --- a/theodolite-quarkus/src/main/kotlin/theodolite/benchmark/BenchmarkDeployment.kt +++ b/theodolite-quarkus/src/main/kotlin/theodolite/benchmark/BenchmarkDeployment.kt @@ -1,6 +1,20 @@ package theodolite.benchmark +/** + * A BenchmarkDeployment contains the necessary infrastructure to execute a benchmark. + * Therefore it has the capabilities to set up the deployment of a benchmark and to tear it down. + */ interface BenchmarkDeployment { + + /** + * Setup a benchmark. This method is responsible for deploying the resources + * and organize the needed infrastructure. + */ fun setup() + + /** + * Tears down a benchmark. This method is responsible for deleting the deployed + * resources and to reset the used infrastructure. + */ fun teardown() } diff --git a/theodolite-quarkus/src/main/kotlin/theodolite/benchmark/BenchmarkExecution.kt b/theodolite-quarkus/src/main/kotlin/theodolite/benchmark/BenchmarkExecution.kt index 36fd00795e3f075ca1fbd171fa59339df8151f85..2d5d15b3389cf723be3a8ceb0fff8b27bd700419 100644 --- a/theodolite-quarkus/src/main/kotlin/theodolite/benchmark/BenchmarkExecution.kt +++ b/theodolite-quarkus/src/main/kotlin/theodolite/benchmark/BenchmarkExecution.kt @@ -8,6 +8,22 @@ import io.quarkus.runtime.annotations.RegisterForReflection import theodolite.util.ConfigurationOverride import kotlin.properties.Delegates +/** + * This class represents the configuration for an execution of a benchmark. + * An example for this is the BenchmarkExecution.yaml + * A BenchmarkExecution consists of: + * - A [name]. + * - The [benchmark] that should be executed. + * - The [load] that should be checked in the benchmark. + * - The [resources] that should be checked in the benchmark. + * - A list of [slos] that are used for the evaluation of the experiments. + * - An [execution] that encapsulates: the strategy, the duration, and the restrictions + * for the execution of the benchmark. + * - [configOverrides] additional configurations. + * This class is used for parsing(in [theodolite.execution.TheodoliteYamlExecutor]) and + * for the deserializing in the [theodolite.execution.operator.TheodoliteOperator]. + * @constructor construct an empty BenchmarkExecution. + */ @JsonDeserialize @RegisterForReflection class BenchmarkExecution : CustomResource(), Namespaced { @@ -20,6 +36,10 @@ class BenchmarkExecution : CustomResource(), Namespaced { lateinit var execution: Execution lateinit var configOverrides: List<ConfigurationOverride?> + /** + * This execution encapsulates the [strategy], the [duration], the [repetitions], and the [restrictions] + * which are used for the concrete benchmark experiments. + */ @JsonDeserialize @RegisterForReflection class Execution : KubernetesResource { @@ -29,6 +49,15 @@ class BenchmarkExecution : CustomResource(), Namespaced { lateinit var restrictions: List<String> } + /** + * Measurable metric. + * [sloType] determines the type of the metric. + * It is evaluated using the [theodolite.evaluation.ExternalSloChecker] by data measured by Prometheus. + * The evaluation checks if a [threshold] is reached or not. + * [offset] determines the shift in hours by which the start and end timestamps should be shifted. + * The [warmup] determines after which time the metric should be evaluated to avoid starting interferences. + * The [warmup] time unit depends on the Slo: for the lag trend it is in seconds. + */ @JsonDeserialize @RegisterForReflection class Slo : KubernetesResource { @@ -40,7 +69,10 @@ class BenchmarkExecution : CustomResource(), Namespaced { var warmup by Delegates.notNull<Int>() } - + /** + * Represents a Load that should be created and checked. + * It can be set to [loadValues]. + */ @JsonDeserialize @RegisterForReflection class LoadDefinition : KubernetesResource { @@ -48,6 +80,9 @@ class BenchmarkExecution : CustomResource(), Namespaced { lateinit var loadValues: List<Int> } + /** + * Represents a resource that can be scaled to [resourceValues]. + */ @JsonDeserialize @RegisterForReflection class ResourceDefinition : KubernetesResource { diff --git a/theodolite-quarkus/src/main/kotlin/theodolite/benchmark/KafkaLagExporterRemover.kt b/theodolite-quarkus/src/main/kotlin/theodolite/benchmark/KafkaLagExporterRemover.kt index 2b86c51ae87c7b26609683b68bf6cfc12003efc8..e8179b42d40e40e7ed45a8f5c48fe26f235be334 100644 --- a/theodolite-quarkus/src/main/kotlin/theodolite/benchmark/KafkaLagExporterRemover.kt +++ b/theodolite-quarkus/src/main/kotlin/theodolite/benchmark/KafkaLagExporterRemover.kt @@ -5,8 +5,16 @@ import mu.KotlinLogging private val logger = KotlinLogging.logger {} +/** + * Used to reset the KafkaLagExporter by deleting the pod. + * @param client NamespacedKubernetesClient used for the deletion. + */ class KafkaLagExporterRemover(private val client: NamespacedKubernetesClient) { + /** + * Deletes all pods with the selected label. + * @param [label] of the pod that should be deleted. + */ fun remove(label: String) { this.client.pods().withLabel(label).delete() logger.info { "Pod with label: $label deleted" } diff --git a/theodolite-quarkus/src/main/kotlin/theodolite/benchmark/KubernetesBenchmark.kt b/theodolite-quarkus/src/main/kotlin/theodolite/benchmark/KubernetesBenchmark.kt index a348deb9ff2acf592458635712b99c8d23fb0577..71b65a28fd074e4554c283ee94a8db028d652d46 100644 --- a/theodolite-quarkus/src/main/kotlin/theodolite/benchmark/KubernetesBenchmark.kt +++ b/theodolite-quarkus/src/main/kotlin/theodolite/benchmark/KubernetesBenchmark.kt @@ -11,8 +11,25 @@ import theodolite.patcher.PatcherFactory import theodolite.util.* private val logger = KotlinLogging.logger {} + private var DEFAULT_NAMESPACE = "default" +/** + * Represents a benchmark in Kubernetes. An example for this is the BenchmarkType.yaml + * Contains a of: + * - [name] of the benchmark, + * - [appResource] list of the resources that have to be deployed for the benchmark, + * - [loadGenResource] resource that generates the load, + * - [resourceTypes] types of scaling resources, + * - [loadTypes] types of loads that can be scaled for the benchmark, + * - [kafkaConfig] for the [TopicManager], + * - [namespace] for the client, + * - [path] under which the resource yamls can be found. + * + * This class is used for the parsing(in the [theodolite.execution.TheodoliteYamlExecutor]) and + * for the deserializing in the [theodolite.execution.operator.TheodoliteOperator]. + * @constructor construct an empty Benchmark. + */ @RegisterForReflection class KubernetesBenchmark : Benchmark, CustomResource(), Namespaced { lateinit var name: String @@ -24,6 +41,11 @@ class KubernetesBenchmark : Benchmark, CustomResource(), Namespaced { private val namespace = System.getenv("NAMESPACE") ?: DEFAULT_NAMESPACE var path = System.getenv("THEODOLITE_APP_RESOURCES") ?: "./config" + /** + * Loads [KubernetesResource]s. + * 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>> { val parser = YamlParser() val loader = K8sResourceLoader(DefaultKubernetesClient().inNamespace(namespace)) @@ -36,6 +58,15 @@ class KubernetesBenchmark : Benchmark, CustomResource(), Namespaced { } } + /** + * Builds a deployment. + * First loads all required resources and then patches them to the concrete load and resources for the experiment. + * Afterwards patches additional configurations(cluster depending) into the resources. + * @param load concrete load that will be benchmarked in this experiment. + * @param res concrete resoruce that will be scaled for this experiment. + * @param configurationOverrides + * @return a [BenchmarkDeployment] + */ override fun buildDeployment( load: LoadDimension, res: Resource, diff --git a/theodolite-quarkus/src/main/kotlin/theodolite/benchmark/KubernetesBenchmarkDeployment.kt b/theodolite-quarkus/src/main/kotlin/theodolite/benchmark/KubernetesBenchmarkDeployment.kt index 8e4fce681147ffe7c80b37bab1d0dbb094b4036e..3d73dd67ddcb5363e752d9a0a65d5a8bff98b4e9 100644 --- a/theodolite-quarkus/src/main/kotlin/theodolite/benchmark/KubernetesBenchmarkDeployment.kt +++ b/theodolite-quarkus/src/main/kotlin/theodolite/benchmark/KubernetesBenchmarkDeployment.kt @@ -7,6 +7,14 @@ import org.apache.kafka.clients.admin.NewTopic import theodolite.k8s.K8sManager import theodolite.k8s.TopicManager +/** + * Organizes the deployment of benchmarks in Kubernetes. + * + * @param namespace to operate in. + * @param resources List of [KubernetesResource] that are managed. + * @param kafkaConfig for the organization of Kafka topics. + * @param topics List of topics that are created or deleted. + */ @RegisterForReflection class KubernetesBenchmarkDeployment( val namespace: String, @@ -19,6 +27,11 @@ class KubernetesBenchmarkDeployment( private val kubernetesManager = K8sManager(client) private val LABEL = "app.kubernetes.io/name=kafka-lag-exporter" + /** + * Setup a [KubernetesBenchmark] using the [TopicManager] and the [K8sManager]: + * - Create the needed topics. + * - Deploy the needed resources. + */ override fun setup() { kafkaController.createTopics(this.topics) resources.forEach { @@ -26,6 +39,12 @@ class KubernetesBenchmarkDeployment( } } + /** + * Tears down a [KubernetesBenchmark]: + * - Reset the Kafka Lag Exporter. + * - Remove the used topics. + * - Remove the [KubernetesResource]s. + */ override fun teardown() { KafkaLagExporterRemover(client).remove(LABEL) kafkaController.removeTopics(this.topics.map { topic -> topic.name() }) diff --git a/theodolite-quarkus/src/main/kotlin/theodolite/evaluation/AnalysisExecutor.kt b/theodolite-quarkus/src/main/kotlin/theodolite/evaluation/AnalysisExecutor.kt index 0b6c681c9123adace08bc60946a067442ca2baa1..e76f6cf34fdec447fa4d581b94bbb51b972d888a 100644 --- a/theodolite-quarkus/src/main/kotlin/theodolite/evaluation/AnalysisExecutor.kt +++ b/theodolite-quarkus/src/main/kotlin/theodolite/evaluation/AnalysisExecutor.kt @@ -9,6 +9,10 @@ import java.time.Instant private val logger = KotlinLogging.logger {} +/** + * Contains the analysis. Fetches a metric from Prometheus, documents it, and evaluates it. + * @param slo Slo that is used for the analysis. + */ class AnalysisExecutor( private val slo: BenchmarkExecution.Slo, private val executionId: Int @@ -19,6 +23,14 @@ class AnalysisExecutor( offset = Duration.ofHours(slo.offset.toLong()) ) + /** + * Analyses an experiment via prometheus data. + * First fetches data from prometheus, then documents them and afterwards evaluate it via a [slo]. + * @param load of the experiment. + * @param res of the experiment. + * @param executionDuration of the experiment. + * @return true if the experiment succeeded. + */ fun analyze(load: LoadDimension, res: Resource, executionDuration: Duration): Boolean { var result = false @@ -31,7 +43,7 @@ class AnalysisExecutor( CsvExporter().toCsv(name = "$executionId-${load.get()}-${res.get()}-${slo.sloType}", prom = prometheusData) val sloChecker = SloCheckerFactory().create( - slotype = slo.sloType, + sloType = slo.sloType, externalSlopeURL = slo.externalSloUrl, threshold = slo.threshold, warmup = slo.warmup diff --git a/theodolite-quarkus/src/main/kotlin/theodolite/evaluation/CsvExporter.kt b/theodolite-quarkus/src/main/kotlin/theodolite/evaluation/CsvExporter.kt index e2f536af6dba838b2b4027d6cfafb032ebd3d04d..68862851523934c533cf3af41f0a786ba2b5a73f 100644 --- a/theodolite-quarkus/src/main/kotlin/theodolite/evaluation/CsvExporter.kt +++ b/theodolite-quarkus/src/main/kotlin/theodolite/evaluation/CsvExporter.kt @@ -8,10 +8,16 @@ import java.util.* private val logger = KotlinLogging.logger {} +/** + * Used to document the data received from prometheus for additional offline analysis. + */ class CsvExporter { /** - * Uses the PrintWriter to transform a PrometheusResponse to Csv + * Uses the [PrintWriter] to transform a [PrometheusResponse] to a CSV file. + * @param name of the file. + * @param prom Response that is documented. + * */ fun toCsv(name: String, prom: PrometheusResponse) { val responseArray = promResponseToList(prom) @@ -27,7 +33,7 @@ class CsvExporter { } /** - * Converts a PrometheusResponse into a List of List of Strings + * Converts a [PrometheusResponse] into a [List] of [List]s of [String]s */ private fun promResponseToList(prom: PrometheusResponse): List<List<String>> { val name = prom.data?.result?.get(0)?.metric?.group.toString() diff --git a/theodolite-quarkus/src/main/kotlin/theodolite/evaluation/ExternalSloChecker.kt b/theodolite-quarkus/src/main/kotlin/theodolite/evaluation/ExternalSloChecker.kt index e65116c0a6b562c0e05714d09ab5a9b528249a05..fd901abc470a1f4b739b26e30276366e6bc69739 100644 --- a/theodolite-quarkus/src/main/kotlin/theodolite/evaluation/ExternalSloChecker.kt +++ b/theodolite-quarkus/src/main/kotlin/theodolite/evaluation/ExternalSloChecker.kt @@ -7,6 +7,12 @@ import theodolite.util.PrometheusResponse import java.net.ConnectException import java.time.Instant +/** + * [SloChecker] that uses an external source for the concrete evaluation. + * @param externalSlopeURL The url under which the external evaluation can be reached. + * @param threshold threshold that should not be exceeded to evaluate to true. + * @param warmup time that is not taken into consideration for the evaluation. + */ class ExternalSloChecker( private val externalSlopeURL: String, private val threshold: Int, @@ -19,6 +25,17 @@ class ExternalSloChecker( private val logger = KotlinLogging.logger {} + /** + * Evaluates an experiment using an external service. + * Will try to reach the external service until success or [RETRIES] times. + * Each request will timeout after [TIMEOUT]. + * + * @param start point of the experiment. + * @param end point of the experiment. + * @param fetchedData that should be evaluated + * @return true if the experiment was successful(the threshold was not exceeded. + * @throws ConnectException if the external service could not be reached. + */ override fun evaluate(start: Instant, end: Instant, fetchedData: PrometheusResponse): Boolean { var counter = 0 val data = diff --git a/theodolite-quarkus/src/main/kotlin/theodolite/evaluation/MetricFetcher.kt b/theodolite-quarkus/src/main/kotlin/theodolite/evaluation/MetricFetcher.kt index 19a8bbe9ba0bdd8a694eb37b9db42de6fdf3d620..bbfbf8c3269e442188f92a9b057fcc264acbbe78 100644 --- a/theodolite-quarkus/src/main/kotlin/theodolite/evaluation/MetricFetcher.kt +++ b/theodolite-quarkus/src/main/kotlin/theodolite/evaluation/MetricFetcher.kt @@ -11,10 +11,26 @@ import java.time.Instant private val logger = KotlinLogging.logger {} +/** + * Used to fetch metrics from Prometheus. + * @param prometheusURL URL to the Prometheus server. + * @param offset Duration of time that the start and end points of the queries + * should be shifted. (for different timezones, etc..) + */ class MetricFetcher(private val prometheusURL: String, private val offset: Duration) { private val RETRIES = 2 private val TIMEOUT = 60.0 + /** + * Tries to fetch a metric by a query to a Prometheus server. + * Retries to fetch the metric [RETRIES] times. + * Connects to the server via [prometheusURL]. + * + * @param start start point of the query. + * @param end end point of the query. + * @param query query for the prometheus server. + * @throws ConnectException - if the prometheus server timed out/was not reached. + */ fun fetchMetric(start: Instant, end: Instant, query: String): PrometheusResponse { val offsetStart = start.minus(offset) @@ -46,6 +62,11 @@ class MetricFetcher(private val prometheusURL: String, private val offset: Durat throw ConnectException("No answer from Prometheus received") } + /** + * Deserializes a response from Prometheus. + * @param values Response from Prometheus. + * @return a [PrometheusResponse] + */ private fun parseValues(values: Response): PrometheusResponse { return Gson().fromJson<PrometheusResponse>( values.jsonObject.toString(), diff --git a/theodolite-quarkus/src/main/kotlin/theodolite/evaluation/SloChecker.kt b/theodolite-quarkus/src/main/kotlin/theodolite/evaluation/SloChecker.kt index 66ea1d201f7b48e09c3acb4365436caae637e6fa..94d816d87923f4d8343c6c83dd9747f1cc25ff81 100644 --- a/theodolite-quarkus/src/main/kotlin/theodolite/evaluation/SloChecker.kt +++ b/theodolite-quarkus/src/main/kotlin/theodolite/evaluation/SloChecker.kt @@ -3,6 +3,20 @@ package theodolite.evaluation import theodolite.util.PrometheusResponse import java.time.Instant +/** + * A SloChecker can be used to evaluate data from Promehteus. + * @constructor Creates an empty SloChecker + */ interface SloChecker { + + /** + * Evaluates [fetchedData] and returns if the experiment was successful. + * Returns if the evaluated experiment was successful. + * + * @param start of the experiment + * @param end of the experiment + * @param fetchedData from Prometheus that will be evaluated. + * @return true if experiment was successful. Otherwise false. + */ fun evaluate(start: Instant, end: Instant, fetchedData: PrometheusResponse): Boolean } diff --git a/theodolite-quarkus/src/main/kotlin/theodolite/evaluation/SloCheckerFactory.kt b/theodolite-quarkus/src/main/kotlin/theodolite/evaluation/SloCheckerFactory.kt index 50b7b0aec3c5d48146d4f9423b06fe62f55e3c56..20c421acdfcd76f5d2ebc2ab2c30142bcca3841a 100644 --- a/theodolite-quarkus/src/main/kotlin/theodolite/evaluation/SloCheckerFactory.kt +++ b/theodolite-quarkus/src/main/kotlin/theodolite/evaluation/SloCheckerFactory.kt @@ -1,23 +1,36 @@ package theodolite.evaluation -import java.time.Duration - +/** + * Factory used to potentially create different [SloChecker]s. + * Supports: lag type. + */ class SloCheckerFactory { + /** + * Creates different [SloChecker]s. + * Supports: lag type. + * + * @param sloType Type of the [SloChecker]. + * @param externalSlopeURL Url to the concrete [SloChecker]. + * @param threshold for the [SloChecker]. + * @param warmup for the [SloChecker]. + * + * @return A [SloChecker] + * @throws IllegalArgumentException If [sloType] not supported. + */ fun create( - slotype: String, + sloType: String, externalSlopeURL: String, threshold: Int, warmup: Int ): SloChecker { - - return when (slotype) { + return when (sloType) { "lag trend" -> ExternalSloChecker( externalSlopeURL = externalSlopeURL, threshold = threshold, warmup = warmup ) - else -> throw IllegalArgumentException("Slotype $slotype not found.") + else -> throw IllegalArgumentException("Slotype $sloType not found.") } } } diff --git a/theodolite-quarkus/src/main/kotlin/theodolite/execution/BenchmarkExecutor.kt b/theodolite-quarkus/src/main/kotlin/theodolite/execution/BenchmarkExecutor.kt index a6eae8bbfb1273b90e3d457db3287d6fc1546fbe..e179f7fa9492fc4fbe069330046dfd5d83ff8374 100644 --- a/theodolite-quarkus/src/main/kotlin/theodolite/execution/BenchmarkExecutor.kt +++ b/theodolite-quarkus/src/main/kotlin/theodolite/execution/BenchmarkExecutor.kt @@ -32,11 +32,13 @@ abstract class BenchmarkExecutor( var run: AtomicBoolean = AtomicBoolean(true) /** - * Run a experiment for the given parametrization, evaluate the experiment and save the result. + * Run a experiment for the given parametrization, evaluate the + * experiment and save the result. * * @param load load to be tested. * @param res resources to be tested. - * @return True, if the number of resources are suitable for the given load, false otherwise. + * @return True, if the number of resources are suitable for the + * given load, false otherwise. */ abstract fun runExperiment(load: LoadDimension, res: Resource): Boolean diff --git a/theodolite-quarkus/src/main/kotlin/theodolite/execution/BenchmarkExecutorImpl.kt b/theodolite-quarkus/src/main/kotlin/theodolite/execution/BenchmarkExecutorImpl.kt index 03c66e757abab9de69bc97ff98a67fffdafdf3d1..efbfe4df41d70b1b35ea91667c8e0c85d8b58953 100644 --- a/theodolite-quarkus/src/main/kotlin/theodolite/execution/BenchmarkExecutorImpl.kt +++ b/theodolite-quarkus/src/main/kotlin/theodolite/execution/BenchmarkExecutorImpl.kt @@ -35,12 +35,16 @@ class BenchmarkExecutorImpl( this.run.set(false) } + /** + * Analyse the experiment, if [run] is true, otherwise the experiment was canceled by the user. + */ if (this.run.get()) { result = AnalysisExecutor(slo = slo, executionId = executionId).analyze(load = load, res = res, executionDuration = executionDuration) this.results.setResult(Pair(load, res), result) } benchmarkDeployment.teardown() + return result } } diff --git a/theodolite-quarkus/src/main/kotlin/theodolite/execution/Shutdown.kt b/theodolite-quarkus/src/main/kotlin/theodolite/execution/Shutdown.kt index 92aaef2923ed87e2bb2c3706ac4fc862538a222f..f5c0251c298dea0801dc601c1d2b790de465459e 100644 --- a/theodolite-quarkus/src/main/kotlin/theodolite/execution/Shutdown.kt +++ b/theodolite-quarkus/src/main/kotlin/theodolite/execution/Shutdown.kt @@ -8,9 +8,20 @@ import theodolite.util.Resource private val logger = KotlinLogging.logger {} -class Shutdown(private val benchmarkExecution: BenchmarkExecution, private val benchmark: KubernetesBenchmark) { +/** + * This Shutdown Hook can be used to delete all Kubernetes resources which are related to the given execution and benchmark. + * + * @property benchmarkExecution + * @property benchmark + */ +class Shutdown(private val benchmarkExecution: BenchmarkExecution, private val benchmark: KubernetesBenchmark) : + Thread() { - fun run() { + /** + * Run + * Delete all Kubernetes resources which are related to the execution and the benchmark. + */ + override fun run() { // Build Configuration to teardown logger.info { "Received shutdown signal -> Shutting down" } val deployment = diff --git a/theodolite-quarkus/src/main/kotlin/theodolite/execution/TheodoliteExecutor.kt b/theodolite-quarkus/src/main/kotlin/theodolite/execution/TheodoliteExecutor.kt index 0c3e0d2fd6d1f40df5299c64b214d84323cba853..c2b8cc7d2ebc60b7cbb8328dfe1d7830ec5b5aff 100644 --- a/theodolite-quarkus/src/main/kotlin/theodolite/execution/TheodoliteExecutor.kt +++ b/theodolite-quarkus/src/main/kotlin/theodolite/execution/TheodoliteExecutor.kt @@ -13,12 +13,30 @@ import theodolite.util.Results import java.io.PrintWriter import java.time.Duration +/** + * The Theodolite executor runs all the experiments defined with the given execution and benchmark configuration. + * + * @property config Configuration of a execution + * @property kubernetesBenchmark Configuration of a benchmark + * @constructor Create empty Theodolite executor + */ class TheodoliteExecutor( private val config: BenchmarkExecution, private val kubernetesBenchmark: KubernetesBenchmark ) { + /** + * An executor object, configured with the specified benchmark, evaluation method, experiment duration + * and overrides which are given in the execution. + */ lateinit var executor: BenchmarkExecutor + /** + * Creates all required components to start Theodolite. + * + * @return a [Config], that contains a list of [LoadDimension]s, + * a list of [Resource]s , and the [CompositeStrategy]. + * The [CompositeStrategy] is configured and able to find the minimum required resource for the given load. + */ private fun buildConfig(): Config { val results = Results() val strategyFactory = StrategyFactory() @@ -74,6 +92,10 @@ class TheodoliteExecutor( return this.kubernetesBenchmark } + /** + * Run all experiments which are specified in the corresponding + * execution and benchmark objects. + */ fun run() { storeAsFile(this.config, "${this.config.executionId}-execution-configuration") storeAsFile(kubernetesBenchmark, "${this.config.executionId}-benchmark-configuration") diff --git a/theodolite-quarkus/src/main/kotlin/theodolite/execution/TheodoliteYamlExecutor.kt b/theodolite-quarkus/src/main/kotlin/theodolite/execution/TheodoliteYamlExecutor.kt index 6bddea20c05fb5c0eb6a5a3bd60b3ec2c6b9bd5d..8b2909f7658f4dffcfd961cad8cd00eb013a160c 100644 --- a/theodolite-quarkus/src/main/kotlin/theodolite/execution/TheodoliteYamlExecutor.kt +++ b/theodolite-quarkus/src/main/kotlin/theodolite/execution/TheodoliteYamlExecutor.kt @@ -9,6 +9,22 @@ import kotlin.system.exitProcess private val logger = KotlinLogging.logger {} + +/** + * The Theodolite yaml executor loads the required configurations + * of the executions and the benchmark from yaml files and run the + * corresponding experiments. + * + * The location of the execution, benchmarks and Kubernetes resource + * files can be configured via the following environment variables: + * `THEODOLITE_EXECUTION` + * + * `THEODOLITE_BENCHMARK` + * + * `THEODOLITE_APP_RESOURCES` + * + * @constructor Create empty Theodolite yaml executor + */ class TheodoliteYamlExecutor { private val parser = YamlParser() diff --git a/theodolite-quarkus/src/main/kotlin/theodolite/execution/operator/BenchmarkEventHandler.kt b/theodolite-quarkus/src/main/kotlin/theodolite/execution/operator/BenchmarkEventHandler.kt index 2b4a784315c2961c5782264c44f2c7e4e8f0d2e8..69c53a3792d86d0ad1c3e973b1d53ea5defff8d9 100644 --- a/theodolite-quarkus/src/main/kotlin/theodolite/execution/operator/BenchmarkEventHandler.kt +++ b/theodolite-quarkus/src/main/kotlin/theodolite/execution/operator/BenchmarkEventHandler.kt @@ -5,13 +5,37 @@ import mu.KotlinLogging import theodolite.benchmark.KubernetesBenchmark private val logger = KotlinLogging.logger {} +/** + * Handles adding, updating and deleting KubernetesBenchmarks. + * + * @param controller The TheodoliteController that handles the application state + * + * @see TheodoliteController + * @see KubernetesBenchmark + */ class BenchmarkEventHandler(private val controller: TheodoliteController): ResourceEventHandler<KubernetesBenchmark> { + + /** + * Add a KubernetesBenchmark. + * + * @param benchmark the KubernetesBenchmark to add + * + * @see KubernetesBenchmark + */ override fun onAdd(benchmark: KubernetesBenchmark) { benchmark.name = benchmark.metadata.name logger.info { "Add new benchmark ${benchmark.name}." } this.controller.benchmarks[benchmark.name] = benchmark } + /** + * Update a KubernetesBenchmark. + * + * @param oldBenchmark the KubernetesBenchmark to update + * @param newBenchmark the updated KubernetesBenchmark + * + * @see KubernetesBenchmark + */ override fun onUpdate(oldBenchmark: KubernetesBenchmark, newBenchmark: KubernetesBenchmark) { logger.info { "Update benchmark ${newBenchmark.metadata.name}." } newBenchmark.name = newBenchmark.metadata.name @@ -23,6 +47,13 @@ class BenchmarkEventHandler(private val controller: TheodoliteController): Resou } } + /** + * Delete a KubernetesBenchmark. + * + * @param benchmark the KubernetesBenchmark to delete + * + * @see KubernetesBenchmark + */ override fun onDelete(benchmark: KubernetesBenchmark, b: Boolean) { logger.info { "Delete benchmark ${benchmark.metadata.name}." } this.controller.benchmarks.remove(benchmark.metadata.name) diff --git a/theodolite-quarkus/src/main/kotlin/theodolite/execution/operator/ExecutionEventHandler.kt b/theodolite-quarkus/src/main/kotlin/theodolite/execution/operator/ExecutionEventHandler.kt index 0152cd7bef808d4652cd893fb282e0cb8b18dd5a..971d3428ffde9cf776711bbd68bae68f66597823 100644 --- a/theodolite-quarkus/src/main/kotlin/theodolite/execution/operator/ExecutionEventHandler.kt +++ b/theodolite-quarkus/src/main/kotlin/theodolite/execution/operator/ExecutionEventHandler.kt @@ -7,13 +7,33 @@ import java.lang.NullPointerException private val logger = KotlinLogging.logger {} +/** + * Handles adding, updating and deleting BenchmarkExecutions. + * + * @param controller The TheodoliteController that handles the application state + * + * @see TheodoliteController + * @see BenchmarkExecution + */ class ExecutionHandler(private val controller: TheodoliteController): ResourceEventHandler<BenchmarkExecution> { + + /** + * Add an execution to the end of the queue of the TheodoliteController. + * + * @param execution the execution to add + */ override fun onAdd(execution: BenchmarkExecution) { execution.name = execution.metadata.name logger.info { "Add new execution ${execution.metadata.name} to queue." } this.controller.executionsQueue.add(execution) } + /** + * Update an execution. If this execution is running at the time this function is called, it is stopped and added to + * the beginning of the queue of the TheodoliteController. Otherwise, it is just added to the beginning of the queue. + * + * @param execution the execution to update + */ override fun onUpdate(oldExecution: BenchmarkExecution, newExecution: BenchmarkExecution) { logger.info { "Add updated execution to queue." } newExecution.name = newExecution.metadata.name @@ -29,6 +49,11 @@ class ExecutionHandler(private val controller: TheodoliteController): ResourceEv } } + /** + * Delete an execution from the queue of the TheodoliteController. + * + * @param execution the execution to delete + */ override fun onDelete(execution: BenchmarkExecution, b: Boolean) { try { this.controller.executionsQueue.removeIf { e -> e.name == execution.metadata.name } 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 9f6cd64528874a1dc5f20c6d6c1563b1aa9f003d..532185841a7a8ee000722c1dc513219177f00cae 100644 --- a/theodolite-quarkus/src/main/kotlin/theodolite/execution/operator/TheodoliteController.kt +++ b/theodolite-quarkus/src/main/kotlin/theodolite/execution/operator/TheodoliteController.kt @@ -15,6 +15,20 @@ import java.util.concurrent.atomic.AtomicInteger private val logger = KotlinLogging.logger {} +/** + * The controller implementation for Theodolite. + * + * Maintains a Dequeue, based on ConcurrentLinkedDequeue, of executions to be executed for a benchmark. + * + * @param client The NamespacedKubernetesClient + * @param executionContext The CustomResourceDefinitionContext + * + * @see NamespacedKubernetesClient + * @see CustomResourceDefinitionContext + * @see BenchmarkExecution + * @see KubernetesBenchmark + * @see ConcurrentLinkedDeque + */ class TheodoliteController( val client: NamespacedKubernetesClient, val executionContext: CustomResourceDefinitionContext, @@ -26,6 +40,9 @@ class TheodoliteController( var isUpdated = AtomicBoolean(false) var executionID = AtomicInteger(0) + /** + * Runs the TheodoliteController forever. + */ fun run() { while (true) { try { @@ -46,6 +63,12 @@ class TheodoliteController( } } + /** + * Ensures that the application state corresponds to the defined KubernetesBenchmarks and BenchmarkExecutions. + * + * @see KubernetesBenchmark + * @see BenchmarkExecution + */ @Synchronized private fun reconcile() { while (executionsQueue.isNotEmpty()) { @@ -61,6 +84,12 @@ class TheodoliteController( } } + /** + * Execute a benchmark with a defined KubernetesBenchmark and BenchmarkExecution + * + * @see KubernetesBenchmark + * @see BenchmarkExecution + */ @Synchronized fun runExecution(execution: BenchmarkExecution, benchmark: KubernetesBenchmark) { execution.executionId = executionID.getAndSet(executionID.get() + 1) @@ -82,6 +111,11 @@ class TheodoliteController( logger.info { "Execution of ${execution.name} is finally stopped." } } + /** + * @return true if the TheodoliteExecutor of this controller is initialized. Else returns false. + * + * @see TheodoliteExecutor + */ @Synchronized fun isInitialized(): Boolean { return ::executor.isInitialized 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 c6181d19bcedfdb36e455b540e19bf0a54a2a297..5e15a4a80e67f47a42d605d4af39102927139331 100644 --- a/theodolite-quarkus/src/main/kotlin/theodolite/execution/operator/TheodoliteOperator.kt +++ b/theodolite-quarkus/src/main/kotlin/theodolite/execution/operator/TheodoliteOperator.kt @@ -21,9 +21,17 @@ private const val RESYNC_PERIOD = 10 * 60 * 1000.toLong() private const val GROUP = "theodolite.com" private val logger = KotlinLogging.logger {} +/** + * Implementation of the Operator pattern for K8s. + * + * **See Also:** [Kubernetes Operator Pattern](https://kubernetes.io/docs/concepts/extend-kubernetes/operator/) + */ class TheodoliteOperator { private val namespace = System.getenv("NAMESPACE") ?: DEFAULT_NAMESPACE + /** + * Start the operator. + */ fun start() { logger.info { "Using $namespace as namespace." } val client = DefaultKubernetesClient().inNamespace(namespace) diff --git a/theodolite-quarkus/src/main/kotlin/theodolite/k8s/K8sContextFactory.kt b/theodolite-quarkus/src/main/kotlin/theodolite/k8s/K8sContextFactory.kt index 8fd822c615da4fab37dafb6927032f64db6c4462..c0e07610171b40c6704602ffa86ec15accb14c19 100644 --- a/theodolite-quarkus/src/main/kotlin/theodolite/k8s/K8sContextFactory.kt +++ b/theodolite-quarkus/src/main/kotlin/theodolite/k8s/K8sContextFactory.kt @@ -2,9 +2,26 @@ package theodolite.k8s import io.fabric8.kubernetes.client.dsl.base.CustomResourceDefinitionContext +/** + * Factory for CustomResourceDefinitionContext + * + * @see CustomResourceDefinitionContext + */ class K8sContextFactory { - fun create(api: String, scope: String, group: String, plural: String ) : CustomResourceDefinitionContext{ + /** + * Create a CustomResourceDefinitionContext. + * + * @param api The K8s API version + * @param scope The scope of the CRD + * @param group The group of the CRD + * @param plural The plural name (kind) of the CRD + * + * @return a new CustomResourceDefinitionContext + * + * @see CustomResourceDefinitionContext + */ + fun create(api: String, scope: String, group: String, plural: String ) : CustomResourceDefinitionContext { return CustomResourceDefinitionContext.Builder() .withVersion(api) .withScope(scope) diff --git a/theodolite-quarkus/src/main/kotlin/theodolite/k8s/K8sManager.kt b/theodolite-quarkus/src/main/kotlin/theodolite/k8s/K8sManager.kt index 029f18c30b79bbbf36c3761ced107e23f39682d8..ac2165303f083be066c4398e294e456f1d268dad 100644 --- a/theodolite-quarkus/src/main/kotlin/theodolite/k8s/K8sManager.kt +++ b/theodolite-quarkus/src/main/kotlin/theodolite/k8s/K8sManager.kt @@ -7,8 +7,17 @@ import io.fabric8.kubernetes.api.model.apps.Deployment import io.fabric8.kubernetes.api.model.apps.StatefulSet import io.fabric8.kubernetes.client.NamespacedKubernetesClient - +/** + * This class is used to deploy or remove different Kubernetes resources. + * Supports: Deployments, Services, ConfigMaps, StatefulSets, and CustomResources. + * @param client KubernetesClient used to deploy or remove. + */ class K8sManager(private val client: NamespacedKubernetesClient) { + + /** + * Deploys different k8s resources using the client. + * @throws IllegalArgumentException if KubernetesResource not supported. + */ fun deploy(resource: KubernetesResource) { when (resource) { is Deployment -> @@ -24,6 +33,10 @@ class K8sManager(private val client: NamespacedKubernetesClient) { } } + /** + * Removes different k8s resources using the client. + * @throws IllegalArgumentException if KubernetesResource not supported. + */ fun remove(resource: KubernetesResource) { when (resource) { is Deployment -> diff --git a/theodolite-quarkus/src/main/kotlin/theodolite/k8s/K8sResourceLoader.kt b/theodolite-quarkus/src/main/kotlin/theodolite/k8s/K8sResourceLoader.kt index bf4af6c531064d42a9fcee1b22058850a560427d..324b02b74b2c53eb1292667f037f3fdbcc114b73 100644 --- a/theodolite-quarkus/src/main/kotlin/theodolite/k8s/K8sResourceLoader.kt +++ b/theodolite-quarkus/src/main/kotlin/theodolite/k8s/K8sResourceLoader.kt @@ -10,12 +10,17 @@ import theodolite.util.YamlParser private val logger = KotlinLogging.logger {} +/** + * Used to load different Kubernetes resources. + * Supports: Deployments, Services, ConfigMaps, and CustomResources. + * @param client KubernetesClient used to deploy or remove. + */ class K8sResourceLoader(private val client: NamespacedKubernetesClient) { /** * Parses a Service from a service yaml * @param path of the yaml file - * @return service from fabric8 + * @return Service from fabric8 */ private fun loadService(path: String): Service { return loadGenericResource(path) { x: String -> client.services().load(x).get() } @@ -24,7 +29,7 @@ class K8sResourceLoader(private val client: NamespacedKubernetesClient) { /** * Parses a CustomResource from a yaml * @param path of the yaml file - * @return customResource from fabric8 + * @return CustomResource from fabric8 */ private fun loadServiceMonitor(path: String): ServiceMonitorWrapper { return loadGenericResource(path) { x: String -> ServiceMonitorWrapper(YamlParser().parse(path, HashMap<String, String>()::class.java)!!) } @@ -51,7 +56,8 @@ class K8sResourceLoader(private val client: NamespacedKubernetesClient) { /** * Generic helper function to load a resource. * @param path of the resource - * @param f function that shall be applied to the resource. + * @param f function that is applied to the resource. + * @throws IllegalArgumentException If the resource could not be loaded. */ private fun <T> loadGenericResource(path: String, f: (String) -> T): T { var resource: T? = null @@ -69,6 +75,14 @@ class K8sResourceLoader(private val client: NamespacedKubernetesClient) { return resource } + /** + * Factory function used to load different k8s resources from a path. + * Supported kinds are: Deployments, Services, ServiceMonitors, ConfigMaps and CustomResources. + * Uses CustomResource as default if Kind is not supported. + * @param kind of the resource. CustomResource as default. + * @param path of the resource to be loaded. + * @throws Exception if the resource could not be loaded. + */ fun loadK8sResource(kind: String, path: String): KubernetesResource { return when (kind) { "Deployment" -> loadDeployment(path) diff --git a/theodolite-quarkus/src/main/kotlin/theodolite/k8s/TopicManager.kt b/theodolite-quarkus/src/main/kotlin/theodolite/k8s/TopicManager.kt index 7529a45310f219fc0c5248b4031a020227a86049..2cbe16b5a460f0caf55bf2c99bc84dc0b3b5ac69 100644 --- a/theodolite-quarkus/src/main/kotlin/theodolite/k8s/TopicManager.kt +++ b/theodolite-quarkus/src/main/kotlin/theodolite/k8s/TopicManager.kt @@ -3,19 +3,19 @@ package theodolite.k8s import mu.KotlinLogging import org.apache.kafka.clients.admin.AdminClient import org.apache.kafka.clients.admin.NewTopic -import java.util.* private val logger = KotlinLogging.logger {} /** * Manages the topics related tasks * @param kafkaConfig Kafka Configuration as HashMap + * @constructor Creates a KafkaAdminClient */ class TopicManager(private val kafkaConfig: HashMap<String, Any>) { /** * Creates topics. - * @param newTopics List of all Topic which should be created + * @param newTopics List of all Topic that should be created */ fun createTopics(newTopics: Collection<NewTopic>) { var kafkaAdmin: AdminClient = AdminClient.create(this.kafkaConfig) @@ -30,10 +30,9 @@ class TopicManager(private val kafkaConfig: HashMap<String, Any>) { kafkaAdmin.close() } - /** * Removes topics. - * @param topics + * @param topics List of names with the topics to remove. */ fun removeTopics(topics: List<String>) { var kafkaAdmin: AdminClient = AdminClient.create(this.kafkaConfig) diff --git a/theodolite-quarkus/src/main/kotlin/theodolite/patcher/AbstractPatcher.kt b/theodolite-quarkus/src/main/kotlin/theodolite/patcher/AbstractPatcher.kt index 2757d106868637978443d1562306ae4603a5b7e3..a1a4501c919748389089b9d81e3cf927b0ea2e2a 100644 --- a/theodolite-quarkus/src/main/kotlin/theodolite/patcher/AbstractPatcher.kt +++ b/theodolite-quarkus/src/main/kotlin/theodolite/patcher/AbstractPatcher.kt @@ -2,6 +2,23 @@ package theodolite.patcher import io.fabric8.kubernetes.api.model.KubernetesResource +/** + * A Patcher is able to modify values of a Kubernetes resource, see [Patcher]. + * + * An AbstractPatcher is created with up to three parameters. + * + * @param k8sResource The Kubernetes resource to be patched. + * @param container *(optional)* The name of the container to be patched + * @param variableName *(optional)* The variable name to be patched + * + * + * **For example** to patch the load dimension of a workload generator, the Patcher should be created as follow: + * + * k8sResource: `uc-1-workload-generator.yaml` + * container: `workload` + * variableName: `NUM_SENSORS` + * + */ abstract class AbstractPatcher( k8sResource: KubernetesResource, container: String? = null, diff --git a/theodolite-quarkus/src/main/kotlin/theodolite/patcher/EnvVarPatcher.kt b/theodolite-quarkus/src/main/kotlin/theodolite/patcher/EnvVarPatcher.kt index 5bdf66f183e3ad553e29f8754a4d3ef7ac0ccecd..16bd9aa34127b79c97e8f9d195d4757145a3fa93 100644 --- a/theodolite-quarkus/src/main/kotlin/theodolite/patcher/EnvVarPatcher.kt +++ b/theodolite-quarkus/src/main/kotlin/theodolite/patcher/EnvVarPatcher.kt @@ -6,6 +6,13 @@ import io.fabric8.kubernetes.api.model.EnvVarSource import io.fabric8.kubernetes.api.model.KubernetesResource import io.fabric8.kubernetes.api.model.apps.Deployment +/** + * The EnvVarPatcher allows to modify the value of an environment variable + * + * @property k8sResource Kubernetes resource to be patched. + * @property container Container to be patched. + * @property variableName Name of the environment variable to be patched. + */ class EnvVarPatcher( private val k8sResource: KubernetesResource, private val container: String, diff --git a/theodolite-quarkus/src/main/kotlin/theodolite/patcher/ImagePatcher.kt b/theodolite-quarkus/src/main/kotlin/theodolite/patcher/ImagePatcher.kt index 960ed6d6f1eb67705da77d14d280c246a5fa6fa2..e5e5f6cb67641c71ad0fd31375752cbb03fa62db 100644 --- a/theodolite-quarkus/src/main/kotlin/theodolite/patcher/ImagePatcher.kt +++ b/theodolite-quarkus/src/main/kotlin/theodolite/patcher/ImagePatcher.kt @@ -4,9 +4,15 @@ import io.fabric8.kubernetes.api.model.KubernetesResource import io.fabric8.kubernetes.api.model.apps.Deployment import io.fabric8.kubernetes.api.model.apps.StatefulSet +/** + * The Image patcher allows to change the image of a container. + * + * @param k8sResource Kubernetes resource to be patched. + * @param container Container to be patched. + */ class ImagePatcher(private val k8sResource: KubernetesResource, private val container: String) : AbstractPatcher(k8sResource, container) { - + override fun <String> patch(imagePath: String) { if (k8sResource is Deployment) { k8sResource.spec.template.spec.containers.filter { it.name == container }.forEach { diff --git a/theodolite-quarkus/src/main/kotlin/theodolite/patcher/NodeSelectorPatcher.kt b/theodolite-quarkus/src/main/kotlin/theodolite/patcher/NodeSelectorPatcher.kt index ad0b8d411dd7c3b743177bd1f3e5862d55c7fe65..0a668a908e66577f96ea1268b85a38ad73bb16a7 100644 --- a/theodolite-quarkus/src/main/kotlin/theodolite/patcher/NodeSelectorPatcher.kt +++ b/theodolite-quarkus/src/main/kotlin/theodolite/patcher/NodeSelectorPatcher.kt @@ -3,6 +3,12 @@ package theodolite.patcher import io.fabric8.kubernetes.api.model.KubernetesResource import io.fabric8.kubernetes.api.model.apps.Deployment +/** + * The Node selector patcher make it possible to set the NodeSelector of a Kubernetes deployment. + * + * @param k8sResource Kubernetes resource to be patched. + * @param variableName The `label-key` of the node for which the `label-value` is to be patched. + */ class NodeSelectorPatcher(private val k8sResource: KubernetesResource, private val variableName: String) : AbstractPatcher(k8sResource, variableName) { override fun <String> patch(value: String) { diff --git a/theodolite-quarkus/src/main/kotlin/theodolite/patcher/NumNestedGroupsLoadGeneratorReplicaPatcher.kt b/theodolite-quarkus/src/main/kotlin/theodolite/patcher/NumNestedGroupsLoadGeneratorReplicaPatcher.kt new file mode 100644 index 0000000000000000000000000000000000000000..7cf56f8452949e387a186aa8f8c962e1ee1aad15 --- /dev/null +++ b/theodolite-quarkus/src/main/kotlin/theodolite/patcher/NumNestedGroupsLoadGeneratorReplicaPatcher.kt @@ -0,0 +1,20 @@ +package theodolite.patcher + +import io.fabric8.kubernetes.api.model.KubernetesResource +import io.fabric8.kubernetes.api.model.apps.Deployment +import kotlin.math.pow + +private const val NUM_SENSORS = 4.0 +private const val LOAD_GEN_MAX_RECORDS = 150000 + +class NumNestedGroupsLoadGeneratorReplicaPatcher(private val k8sResource: KubernetesResource) : AbstractPatcher(k8sResource) { + override fun <String> patch(value: String) { + if (k8sResource is Deployment) { + if (value is kotlin.String) { + val approxNumSensors = NUM_SENSORS.pow(Integer.parseInt(value).toDouble()) + val loadGenInstances = (approxNumSensors + LOAD_GEN_MAX_RECORDS -1) / LOAD_GEN_MAX_RECORDS + this.k8sResource.spec.replicas = loadGenInstances.toInt() + } + } + } +} diff --git a/theodolite-quarkus/src/main/kotlin/theodolite/patcher/NumSensorsLoadGeneratorReplicaPatcher.kt b/theodolite-quarkus/src/main/kotlin/theodolite/patcher/NumSensorsLoadGeneratorReplicaPatcher.kt new file mode 100644 index 0000000000000000000000000000000000000000..6f2ebcb8b1eb37801c7f6bb2f28c251a07ae44e8 --- /dev/null +++ b/theodolite-quarkus/src/main/kotlin/theodolite/patcher/NumSensorsLoadGeneratorReplicaPatcher.kt @@ -0,0 +1,17 @@ +package theodolite.patcher + +import io.fabric8.kubernetes.api.model.KubernetesResource +import io.fabric8.kubernetes.api.model.apps.Deployment + +private const val LOAD_GEN_MAX_RECORDS = 150000 + +class NumSensorsLoadGeneratorReplicaPatcher(private val k8sResource: KubernetesResource) : AbstractPatcher(k8sResource) { + override fun <String> patch(value: String) { + if (k8sResource is Deployment) { + if (value is kotlin.String) { + val loadGenInstances = (Integer.parseInt(value) + LOAD_GEN_MAX_RECORDS - 1) / LOAD_GEN_MAX_RECORDS + this.k8sResource.spec.replicas = loadGenInstances + } + } + } +} diff --git a/theodolite-quarkus/src/main/kotlin/theodolite/patcher/Patcher.kt b/theodolite-quarkus/src/main/kotlin/theodolite/patcher/Patcher.kt index 55cde39f5a6d7bb4cacd1e539593fc0538d108af..84b886cb4f06b3e667eb8b8aeaa622e1ee54852e 100644 --- a/theodolite-quarkus/src/main/kotlin/theodolite/patcher/Patcher.kt +++ b/theodolite-quarkus/src/main/kotlin/theodolite/patcher/Patcher.kt @@ -2,7 +2,19 @@ package theodolite.patcher import io.quarkus.runtime.annotations.RegisterForReflection +/** + * A patcher can be used to modify values of Kubernetes resource. + * + * @constructor Create empty Patcher + */ @RegisterForReflection interface Patcher { + /** + * The patch method modifies a value in the definition of a + * Kubernetes resource. + * + * @param T The type of value + * @param value The value to be used. + */ fun <T> patch(value: T) } diff --git a/theodolite-quarkus/src/main/kotlin/theodolite/patcher/PatcherDefinitionFactory.kt b/theodolite-quarkus/src/main/kotlin/theodolite/patcher/PatcherDefinitionFactory.kt index 096d19e7c54ce3ac308ca59edee7861a7041dde0..bcb568f716449cb1981112ab23a81411a0f7c54d 100644 --- a/theodolite-quarkus/src/main/kotlin/theodolite/patcher/PatcherDefinitionFactory.kt +++ b/theodolite-quarkus/src/main/kotlin/theodolite/patcher/PatcherDefinitionFactory.kt @@ -3,8 +3,24 @@ package theodolite.patcher import theodolite.util.PatcherDefinition import theodolite.util.TypeName +/** + * The PatcherDefinition Factory creates a [PatcherDefinition]s. + * + * @constructor Create empty Patcher definition factory. + */ class PatcherDefinitionFactory { - fun createPatcherDefinition(requiredType: String, patcherTypes: List<TypeName>) : List<PatcherDefinition> { + /** + * Creates a list of PatcherDefinitions + * + * @param requiredType indicates the required PatcherDefinitions + * (for example `NumSensors`) + * @param patcherTypes list of TypeNames. A TypeName contains a type + * (for example `NumSensors`) and a list of + * PatcherDefinitions, which are related to this type. + * @return A list of PatcherDefinitions which corresponds to the + * value of the requiredType. + */ + fun createPatcherDefinition(requiredType: String, patcherTypes: List<TypeName>): List<PatcherDefinition> { return patcherTypes .filter { type -> type.typeName == requiredType } .flatMap { type -> type.patchers } diff --git a/theodolite-quarkus/src/main/kotlin/theodolite/patcher/PatcherFactory.kt b/theodolite-quarkus/src/main/kotlin/theodolite/patcher/PatcherFactory.kt index dd391c599ad33fa0f6990ecc86ed1af5430cb695..2ee1f6c7b46322cb0f8de03c37aabe64ccf0ba5a 100644 --- a/theodolite-quarkus/src/main/kotlin/theodolite/patcher/PatcherFactory.kt +++ b/theodolite-quarkus/src/main/kotlin/theodolite/patcher/PatcherFactory.kt @@ -3,13 +3,35 @@ package theodolite.patcher import io.fabric8.kubernetes.api.model.KubernetesResource import theodolite.util.PatcherDefinition +/** + * The Patcher factory creates [Patcher]s + * + * @constructor Creates an empty PatcherFactory. + */ class PatcherFactory { - fun createPatcher(patcherDefinition: PatcherDefinition, - k8sResources: List<Pair<String, KubernetesResource>>) : Patcher { + /** + * Create patcher based on the given [PatcherDefinition] and + * the list of KubernetesResources. + * + * @param patcherDefinition The [PatcherDefinition] for which are + * [Patcher] should be created. + * @param k8sResources List of all available Kubernetes resources. + * This is a list of pairs<String, KubernetesResource>: + * The frist corresponds to the filename where the resource is defined. + * The second corresponds to the concrete [KubernetesResource] that should be patched. + * @return The created [Patcher]. + * @throws IllegalArgumentException if no patcher can be created. + */ + fun createPatcher( + patcherDefinition: PatcherDefinition, + k8sResources: List<Pair<String, KubernetesResource>> + ): Patcher { val resource = k8sResources.filter { it.first == patcherDefinition.resource }.map { resource -> resource.second }[0] return when (patcherDefinition.type) { "ReplicaPatcher" -> ReplicaPatcher(resource) + "NumNestedGroupsLoadGeneratorReplicaPatcher" -> NumNestedGroupsLoadGeneratorReplicaPatcher(resource) + "NumSensorsLoadGeneratorReplicaPatcher" -> NumSensorsLoadGeneratorReplicaPatcher(resource) "EnvVarPatcher" -> EnvVarPatcher(resource, patcherDefinition.container, patcherDefinition.variableName) "NodeSelectorPatcher" -> NodeSelectorPatcher(resource, patcherDefinition.variableName) "ResourceLimitPatcher" -> ResourceLimitPatcher( @@ -26,4 +48,4 @@ class PatcherFactory { else -> throw IllegalArgumentException("Patcher type ${patcherDefinition.type} not found") } } -} \ No newline at end of file +} diff --git a/theodolite-quarkus/src/main/kotlin/theodolite/patcher/ReplicaPatcher.kt b/theodolite-quarkus/src/main/kotlin/theodolite/patcher/ReplicaPatcher.kt index b4b33fabc5a27e3cc52f5cda889f63bc61adbf5f..4cc35f2ed74f9e366c266c3f98f1b3d36d4ba1b8 100644 --- a/theodolite-quarkus/src/main/kotlin/theodolite/patcher/ReplicaPatcher.kt +++ b/theodolite-quarkus/src/main/kotlin/theodolite/patcher/ReplicaPatcher.kt @@ -3,6 +3,11 @@ package theodolite.patcher import io.fabric8.kubernetes.api.model.KubernetesResource import io.fabric8.kubernetes.api.model.apps.Deployment +/** + * The Replica [Patcher] modifies the number of replicas for the given Kubernetes deployment. + * + * @param k8sResource Kubernetes resource to be patched. + */ class ReplicaPatcher(private val k8sResource: KubernetesResource) : AbstractPatcher(k8sResource) { override fun <String> patch(value: String) { if (k8sResource is Deployment) { diff --git a/theodolite-quarkus/src/main/kotlin/theodolite/patcher/ResourceLimitPatcher.kt b/theodolite-quarkus/src/main/kotlin/theodolite/patcher/ResourceLimitPatcher.kt index 533489a44457ba7452f3d0ca1eb88eb6f3ada7d9..eab82effbc084e91ba57c1bea7103b2a3239c922 100644 --- a/theodolite-quarkus/src/main/kotlin/theodolite/patcher/ResourceLimitPatcher.kt +++ b/theodolite-quarkus/src/main/kotlin/theodolite/patcher/ResourceLimitPatcher.kt @@ -8,6 +8,13 @@ import io.fabric8.kubernetes.api.model.apps.Deployment import io.fabric8.kubernetes.api.model.apps.StatefulSet import java.lang.IllegalArgumentException +/** + * The Resource limit [Patcher] set resource limits for deployments and statefulSets. + * + * @param k8sResource Kubernetes resource to be patched. + * @param container Container to be patched. + * @param limitedResource The resource to be limited (e.g. **cpu or memory**) + */ class ResourceLimitPatcher( private val k8sResource: KubernetesResource, private val container: String, diff --git a/theodolite-quarkus/src/main/kotlin/theodolite/patcher/ResourceRequestPatcher.kt b/theodolite-quarkus/src/main/kotlin/theodolite/patcher/ResourceRequestPatcher.kt index 39e8fafb513a60e8a8cd445227160025fe6d8c42..f4ef38edebab67022066394e149716ab9ffbce00 100644 --- a/theodolite-quarkus/src/main/kotlin/theodolite/patcher/ResourceRequestPatcher.kt +++ b/theodolite-quarkus/src/main/kotlin/theodolite/patcher/ResourceRequestPatcher.kt @@ -8,6 +8,13 @@ import io.fabric8.kubernetes.api.model.apps.Deployment import io.fabric8.kubernetes.api.model.apps.StatefulSet import java.lang.IllegalArgumentException +/** + * The Resource request [Patcher] set resource limits for deployments and statefulSets. + * + * @param k8sResource Kubernetes resource to be patched. + * @param container Container to be patched. + * @param requestedResource The resource to be requested (e.g. **cpu or memory**) + */ class ResourceRequestPatcher( private val k8sResource: KubernetesResource, private val container: String, diff --git a/theodolite-quarkus/src/main/kotlin/theodolite/patcher/SchedulerNamePatcher.kt b/theodolite-quarkus/src/main/kotlin/theodolite/patcher/SchedulerNamePatcher.kt index 6d6505192daac3331e5c99b9706ca7413e98ea7d..589bceff78158a422d923169bd35a1e11e2f4caa 100644 --- a/theodolite-quarkus/src/main/kotlin/theodolite/patcher/SchedulerNamePatcher.kt +++ b/theodolite-quarkus/src/main/kotlin/theodolite/patcher/SchedulerNamePatcher.kt @@ -3,7 +3,12 @@ package theodolite.patcher import io.fabric8.kubernetes.api.model.KubernetesResource import io.fabric8.kubernetes.api.model.apps.Deployment -class SchedulerNamePatcher(private val k8sResource: KubernetesResource) : Patcher { +/** + * The Scheduler name [Patcher] make it possible to set the scheduler which should be used to deploy the given deployment. + * + * @param k8sResource Kubernetes resource to be patched. + */ +class SchedulerNamePatcher(private val k8sResource: KubernetesResource): Patcher { override fun <String> patch(value: String) { if (k8sResource is Deployment) { k8sResource.spec.template.spec.schedulerName = value as kotlin.String diff --git a/theodolite-quarkus/src/main/kotlin/theodolite/strategies/StrategyFactory.kt b/theodolite-quarkus/src/main/kotlin/theodolite/strategies/StrategyFactory.kt index 3d0135a8884e581bd8caa61fb5c0632057812150..9bef5587ac9c26d2323af41c5119ac36b95cf807 100644 --- a/theodolite-quarkus/src/main/kotlin/theodolite/strategies/StrategyFactory.kt +++ b/theodolite-quarkus/src/main/kotlin/theodolite/strategies/StrategyFactory.kt @@ -8,8 +8,20 @@ import theodolite.strategies.searchstrategy.LinearSearch import theodolite.strategies.searchstrategy.SearchStrategy import theodolite.util.Results +/** + * Factory for creating [SearchStrategy] and [RestrictionStrategy] Strategies. + */ class StrategyFactory { + /** + * Create a [SearchStrategy]. + * + * @param executor The [theodolite.execution.BenchmarkExecutor] that executes individual experiments. + * @param searchStrategyString Specifies the [SearchStrategy]. Must either be the string 'LinearSearch', + * or 'BinarySearch'. + * + * @throws IllegalArgumentException if the [SearchStrategy] was not one of the allowed options. + */ fun createSearchStrategy(executor: BenchmarkExecutor, searchStrategyString: String): SearchStrategy { return when (searchStrategyString) { "LinearSearch" -> LinearSearch(executor) @@ -18,6 +30,16 @@ class StrategyFactory { } } + /** + * Create a [RestrictionStrategy]. + * + * @param results The [Results] saves the state of the Theodolite benchmark run. + * @param restrictionStrings Specifies the list of [RestrictionStrategy] that are used to restrict the amount + * of [theodolite.util.Resource] for a fixed LoadDimension. Must equal the string + * 'LowerBound'. + * + * @throws IllegalArgumentException if param searchStrategyString was not one of the allowed options. + */ fun createRestrictionStrategy(results: Results, restrictionStrings: List<String>): Set<RestrictionStrategy> { return restrictionStrings .map { restriction -> diff --git a/theodolite-quarkus/src/main/kotlin/theodolite/strategies/restriction/LowerBoundRestriction.kt b/theodolite-quarkus/src/main/kotlin/theodolite/strategies/restriction/LowerBoundRestriction.kt index 6fed9b5d808405b42ad374346862f050ce192141..2911b6ac949a9d523e464c0ea2942063e996d767 100644 --- a/theodolite-quarkus/src/main/kotlin/theodolite/strategies/restriction/LowerBoundRestriction.kt +++ b/theodolite-quarkus/src/main/kotlin/theodolite/strategies/restriction/LowerBoundRestriction.kt @@ -8,10 +8,10 @@ import theodolite.util.Results * The Lower Bound Restriction sets the lower bound of the resources to be examined to the value * needed to successfully execute the next smaller load. * - * @param results Result object used as a basis to restrict the resources. + * @param results [Result] object used as a basis to restrict the resources. */ class LowerBoundRestriction(results: Results) : RestrictionStrategy(results) { - override fun next(load: LoadDimension, resources: List<Resource>): List<Resource> { + override fun apply(load: LoadDimension, resources: List<Resource>): List<Resource> { val maxLoad: LoadDimension? = this.results.getMaxBenchmarkedLoad(load) var lowerBound: Resource? = this.results.getMinRequiredInstances(maxLoad) if (lowerBound == null) { diff --git a/theodolite-quarkus/src/main/kotlin/theodolite/strategies/restriction/RestrictionStrategy.kt b/theodolite-quarkus/src/main/kotlin/theodolite/strategies/restriction/RestrictionStrategy.kt index 75c7e86b57e3b3afb0121eab628f2872458efe74..1ab7302d7898daad729b1c94c32d97138b5cdcf4 100644 --- a/theodolite-quarkus/src/main/kotlin/theodolite/strategies/restriction/RestrictionStrategy.kt +++ b/theodolite-quarkus/src/main/kotlin/theodolite/strategies/restriction/RestrictionStrategy.kt @@ -6,18 +6,20 @@ import theodolite.util.Resource import theodolite.util.Results /** - * A "Restriction Strategy" restricts a list of resources based on the current + * A 'Restriction Strategy' restricts a list of resources based on the current * results of all previously performed benchmarks. + * + * @param results the [Results] object */ @RegisterForReflection abstract class RestrictionStrategy(val results: Results) { /** - * Next Restrict the given resource list for the given load based on the result object. + * Apply the restriction of the given resource list for the given load based on the results object. * - * @param load Load dimension for which a subset of resources are required. - * @param resources List of resources to be restricted. + * @param load [LoadDimension] for which a subset of resources are required. + * @param resources List of [Resource]s to be restricted. * @return Returns a list containing only elements that have not been filtered out by the * restriction (possibly empty). */ - abstract fun next(load: LoadDimension, resources: List<Resource>): List<Resource> + abstract fun apply(load: LoadDimension, resources: List<Resource>): List<Resource> } diff --git a/theodolite-quarkus/src/main/kotlin/theodolite/strategies/searchstrategy/BinarySearch.kt b/theodolite-quarkus/src/main/kotlin/theodolite/strategies/searchstrategy/BinarySearch.kt index 04f25fd9925d83b2a034536f9116c660dae6377d..027444fe36a47878af998abdf18dc7a7562d7afd 100644 --- a/theodolite-quarkus/src/main/kotlin/theodolite/strategies/searchstrategy/BinarySearch.kt +++ b/theodolite-quarkus/src/main/kotlin/theodolite/strategies/searchstrategy/BinarySearch.kt @@ -5,7 +5,7 @@ import theodolite.util.LoadDimension import theodolite.util.Resource /** - * Search for the smallest suitable resource with binary search. + * Binary-search-like implementation for determining the smallest suitable number of instances. * * @param benchmarkExecutor Benchmark executor which runs the individual benchmarks. */ @@ -18,6 +18,14 @@ class BinarySearch(benchmarkExecutor: BenchmarkExecutor) : SearchStrategy(benchm return resources[result] } + /** + * Apply binary search. + * + * @param load the load dimension to perform experiments for + * @param resources the list in which binary search is performed + * @param lower lower bound for binary search (inclusive) + * @param upper upper bound for binary search (inclusive) + */ private fun binarySearch(load: LoadDimension, resources: List<Resource>, lower: Int, upper: Int): Int { if (lower > upper) { throw IllegalArgumentException() diff --git a/theodolite-quarkus/src/main/kotlin/theodolite/strategies/searchstrategy/CompositeStrategy.kt b/theodolite-quarkus/src/main/kotlin/theodolite/strategies/searchstrategy/CompositeStrategy.kt index 01d841c91b2de6fb70208ef4f8da12ab75361818..6ae06d70c9effe0a0a4bbd9abffa665fb08636c9 100644 --- a/theodolite-quarkus/src/main/kotlin/theodolite/strategies/searchstrategy/CompositeStrategy.kt +++ b/theodolite-quarkus/src/main/kotlin/theodolite/strategies/searchstrategy/CompositeStrategy.kt @@ -6,6 +6,13 @@ import theodolite.strategies.restriction.RestrictionStrategy import theodolite.util.LoadDimension import theodolite.util.Resource +/** + * Composite strategy that combines a SearchStrategy and a set of RestrictionStrategy. + * + * @param searchStrategy the [SearchStrategy] that is executed as part of this [CompositeStrategy]. + * @param restrictionStrategies the set of [RestrictionStrategy] that are connected conjuntively to restrict the [Resource] + * @param benchmarkExecutor Benchmark executor which runs the individual benchmarks. + */ @RegisterForReflection class CompositeStrategy( benchmarkExecutor: BenchmarkExecutor, @@ -16,7 +23,7 @@ class CompositeStrategy( override fun findSuitableResource(load: LoadDimension, resources: List<Resource>): Resource? { var restrictedResources = resources.toList() for (strategy in this.restrictionStrategies) { - restrictedResources = restrictedResources.intersect(strategy.next(load, resources)).toList() + restrictedResources = restrictedResources.intersect(strategy.apply(load, resources)).toList() } return this.searchStrategy.findSuitableResource(load, restrictedResources) } diff --git a/theodolite-quarkus/src/main/kotlin/theodolite/strategies/searchstrategy/LinearSearch.kt b/theodolite-quarkus/src/main/kotlin/theodolite/strategies/searchstrategy/LinearSearch.kt index f1e8591a0a619d7c3ce59a40505989714f40972c..08daa082d0eb8f2ecbb71193111a0263ae275fbc 100644 --- a/theodolite-quarkus/src/main/kotlin/theodolite/strategies/searchstrategy/LinearSearch.kt +++ b/theodolite-quarkus/src/main/kotlin/theodolite/strategies/searchstrategy/LinearSearch.kt @@ -4,6 +4,11 @@ import theodolite.execution.BenchmarkExecutor import theodolite.util.LoadDimension import theodolite.util.Resource +/** + * Linear-search-like implementation for determining the smallest suitable number of instances. + * + * @param benchmarkExecutor Benchmark executor which runs the individual benchmarks. + */ class LinearSearch(benchmarkExecutor: BenchmarkExecutor) : SearchStrategy(benchmarkExecutor) { override fun findSuitableResource(load: LoadDimension, resources: List<Resource>): Resource? { diff --git a/theodolite-quarkus/src/main/kotlin/theodolite/strategies/searchstrategy/SearchStrategy.kt b/theodolite-quarkus/src/main/kotlin/theodolite/strategies/searchstrategy/SearchStrategy.kt index 888a58585b08cd02b116ee1d2e275138f5f690e8..4e304b010d4d56f6b5fe734a6b977361f93e57a1 100644 --- a/theodolite-quarkus/src/main/kotlin/theodolite/strategies/searchstrategy/SearchStrategy.kt +++ b/theodolite-quarkus/src/main/kotlin/theodolite/strategies/searchstrategy/SearchStrategy.kt @@ -5,13 +5,19 @@ import theodolite.execution.BenchmarkExecutor import theodolite.util.LoadDimension import theodolite.util.Resource +/** + * Base class for the implementation for SearchStrategies. SearchStrategies determine the smallest suitable number of instances. + * + * @param benchmarkExecutor Benchmark executor which runs the individual benchmarks. + */ @RegisterForReflection abstract class SearchStrategy(val benchmarkExecutor: BenchmarkExecutor) { /** * Find smallest suitable resource from the specified resource list for the given load. * - * @param load Load to be tested. - * @param resources List of all possible resources. + * @param load the [LoadDimension] to be tested. + * @param resources List of all possible [Resource]s. + * * @return suitable resource for the specified load, or null if no suitable resource exists. */ abstract fun findSuitableResource(load: LoadDimension, resources: List<Resource>): Resource? diff --git a/theodolite-quarkus/src/main/kotlin/theodolite/util/Config.kt b/theodolite-quarkus/src/main/kotlin/theodolite/util/Config.kt index 15defa6c7127bc82d6ea2b13abe51076e44bd5a9..afbf784e9d6d72939615e367b54891ecd95a3608 100644 --- a/theodolite-quarkus/src/main/kotlin/theodolite/util/Config.kt +++ b/theodolite-quarkus/src/main/kotlin/theodolite/util/Config.kt @@ -3,6 +3,13 @@ package theodolite.util import io.quarkus.runtime.annotations.RegisterForReflection import theodolite.strategies.searchstrategy.CompositeStrategy +/** + * Config class that represents a configuration of a theodolite run. + * + * @param loads the [LoadDimension] of the execution + * @param resources the [Resource] of the execution + * @param compositeStrategy the [CompositeStrategy] of the execution + */ @RegisterForReflection data class Config( val loads: List<LoadDimension>, diff --git a/theodolite-quarkus/src/main/kotlin/theodolite/util/ConfigurationOverride.kt b/theodolite-quarkus/src/main/kotlin/theodolite/util/ConfigurationOverride.kt index c4801dfbab0beb01cab025200cceab17efb0053d..537b44721bb344c2cd7af71d29dc4fa3da5a7a33 100644 --- a/theodolite-quarkus/src/main/kotlin/theodolite/util/ConfigurationOverride.kt +++ b/theodolite-quarkus/src/main/kotlin/theodolite/util/ConfigurationOverride.kt @@ -3,9 +3,19 @@ package theodolite.util import com.fasterxml.jackson.databind.annotation.JsonDeserialize import io.quarkus.runtime.annotations.RegisterForReflection +/** + * Representation of a configuration override. + */ @JsonDeserialize @RegisterForReflection class ConfigurationOverride { + /** + * Patcher of the configuration override. + */ lateinit var patcher: PatcherDefinition + + /** + * Value of the patched configuration override. + */ lateinit var value: String } diff --git a/theodolite-quarkus/src/main/kotlin/theodolite/util/KafkaConfig.kt b/theodolite-quarkus/src/main/kotlin/theodolite/util/KafkaConfig.kt index cb94c8b7bfac4c3bc043e04a67d673ccaddea3c5..4ba096e37345a9488cb288b21a8aa57ff07ac1ff 100644 --- a/theodolite-quarkus/src/main/kotlin/theodolite/util/KafkaConfig.kt +++ b/theodolite-quarkus/src/main/kotlin/theodolite/util/KafkaConfig.kt @@ -5,20 +5,51 @@ import io.quarkus.runtime.annotations.RegisterForReflection import org.apache.kafka.clients.admin.NewTopic import kotlin.properties.Delegates +/** + * Configuration of Kafka connection. + * + * @see TopicWrapper + */ @RegisterForReflection @JsonDeserialize class KafkaConfig { + /** + * The bootstrap server connection string + */ lateinit var bootstrapServer: String + + /** + * The list of topics + */ lateinit var topics: List<TopicWrapper> + /** + * Get all current Kafka topics. + * + * @return the list of topics. + */ fun getKafkaTopics(): List<NewTopic> { return topics.map { topic -> NewTopic(topic.name, topic.numPartitions, topic.replicationFactor) } } + /** + * Wrapper for a topic definition. + */ @RegisterForReflection class TopicWrapper { + /** + * The topic name + */ lateinit var name: String + + /** + * The number of partitions + */ var numPartitions by Delegates.notNull<Int>() + + /** + * The replication factor of this topic + */ var replicationFactor by Delegates.notNull<Short>() } } diff --git a/theodolite-quarkus/src/main/kotlin/theodolite/util/LoadDimension.kt b/theodolite-quarkus/src/main/kotlin/theodolite/util/LoadDimension.kt index 4f092e5f9dd8fed8f4a6229eecf4f8a26a2e7e76..cf26da979b05f0a2bd82289ce371715ea0d67c93 100644 --- a/theodolite-quarkus/src/main/kotlin/theodolite/util/LoadDimension.kt +++ b/theodolite-quarkus/src/main/kotlin/theodolite/util/LoadDimension.kt @@ -2,13 +2,24 @@ package theodolite.util import io.quarkus.runtime.annotations.RegisterForReflection - +/** + * Representation of the load dimensions for a execution of theodolite. + * + * @param number the value of this [LoadDimension] + * @param type [PatcherDefinition] of this [LoadDimension] + */ @RegisterForReflection data class LoadDimension(private val number: Int, private val type: List<PatcherDefinition>) { + /** + * @return the value of this load dimension. + */ fun get(): Int { return this.number } + /** + * @return the list of [PatcherDefinition] + */ fun getType(): List<PatcherDefinition> { return this.type } diff --git a/theodolite-quarkus/src/main/kotlin/theodolite/util/Parser.kt b/theodolite-quarkus/src/main/kotlin/theodolite/util/Parser.kt index 886fd0b1f7a8a9a2219c74197ebb878f6d87775e..e435b1cbbf18b9f860ceda69f5f7ec66e64c9375 100644 --- a/theodolite-quarkus/src/main/kotlin/theodolite/util/Parser.kt +++ b/theodolite-quarkus/src/main/kotlin/theodolite/util/Parser.kt @@ -1,5 +1,16 @@ package theodolite.util +/** + * Interface for parsers. + * A parser allows the reading of files and creates a corresponding object from them. + */ interface Parser { + /** + * Parse a file. + * + * @param path The path of the file + * @param E The class of the type to parse + * @param T The type to parse + */ fun <T> parse(path: String, E: Class<T>): T? } diff --git a/theodolite-quarkus/src/main/kotlin/theodolite/util/PatcherDefinition.kt b/theodolite-quarkus/src/main/kotlin/theodolite/util/PatcherDefinition.kt index e78048cd7691f8bfd14f663d401384ae6c329d4b..b24f887d6ff6e3096a2e740f541861d76804775b 100644 --- a/theodolite-quarkus/src/main/kotlin/theodolite/util/PatcherDefinition.kt +++ b/theodolite-quarkus/src/main/kotlin/theodolite/util/PatcherDefinition.kt @@ -3,11 +3,29 @@ package theodolite.util import com.fasterxml.jackson.databind.annotation.JsonDeserialize import io.quarkus.runtime.annotations.RegisterForReflection +/** + * Definition of the structure of a [theodolite.patcher.AbstractPatcher] which implements the [theodolite.patcher.Patcher] interface. + */ @JsonDeserialize @RegisterForReflection class PatcherDefinition { + /** + * The type of the patcher + */ lateinit var type: String + + /** + * The resource which the patcher is applied to + */ lateinit var resource: String + + /** + * The container which the patcher is applied to + */ lateinit var container: String + + /** + * The variable name for the patcher + */ lateinit var variableName: String } diff --git a/theodolite-quarkus/src/main/kotlin/theodolite/util/PrometheusResponse.kt b/theodolite-quarkus/src/main/kotlin/theodolite/util/PrometheusResponse.kt index ca26ad117034b160e48dc950e80062100a1c68d8..d1d59c482e64fd14c4744d8fcd606f286da24fb4 100644 --- a/theodolite-quarkus/src/main/kotlin/theodolite/util/PrometheusResponse.kt +++ b/theodolite-quarkus/src/main/kotlin/theodolite/util/PrometheusResponse.kt @@ -2,25 +2,58 @@ package theodolite.util import io.quarkus.runtime.annotations.RegisterForReflection +/** + * This class corresponds to the JSON response format of a Prometheus + * [range-query](https://www.prometheus.io/docs/prometheus/latest/querying/api/#range-queries) + */ @RegisterForReflection data class PrometheusResponse( + /** + * Indicates whether the query was successful. + */ var status: String? = null, + /** + * The data section of the query result contains the information about the resultType and the values itself. + */ var data: PromData? = null ) +/** + * Description of Prometheus data. + * + * Based on [PromResult] + */ @RegisterForReflection data class PromData( + /** + * Type of the result, either "matrix" | "vector" | "scalar" | "string" + */ var resultType: String? = null, + /** + * Result of the range-query. In the case of range-query this corresponds to the [range-vectors result format](https://www.prometheus.io/docs/prometheus/latest/querying/api/#range-vectors) + */ var result: List<PromResult>? = null ) +/** + * PromResult corresponds to the [range-vectors result format](https://www.prometheus.io/docs/prometheus/latest/querying/api/#range-vectors) + */ @RegisterForReflection data class PromResult( + /** + * Label of the metric + */ var metric: PromMetric? = null, + /** + * Values of the metric (e.g. [ [ <unix_time>, "<sample_value>" ], ... ]) + */ var values: List<Any>? = null ) +/** + * Corresponds to the metric field in the range-vector result format of a Prometheus range-query response. + */ @RegisterForReflection data class PromMetric( var group: String? = null -) +) \ No newline at end of file diff --git a/theodolite-quarkus/src/main/kotlin/theodolite/util/Resource.kt b/theodolite-quarkus/src/main/kotlin/theodolite/util/Resource.kt index 59acf175e31b2707b236b421d2055bb14a49ca1a..1d6410aa4288e19817e3ba48bfd1bc0d85d006a2 100644 --- a/theodolite-quarkus/src/main/kotlin/theodolite/util/Resource.kt +++ b/theodolite-quarkus/src/main/kotlin/theodolite/util/Resource.kt @@ -2,13 +2,22 @@ package theodolite.util import io.quarkus.runtime.annotations.RegisterForReflection +/** + * Representation of the resources for an execution of Theodolite. + */ @RegisterForReflection data class Resource(private val number: Int, private val type: List<PatcherDefinition>) { + /** + * @return the value of this resource. + */ fun get(): Int { return this.number } + /** + * @return the list of [PatcherDefinition] + */ fun getType(): List<PatcherDefinition> { return this.type } diff --git a/theodolite-quarkus/src/main/kotlin/theodolite/util/Results.kt b/theodolite-quarkus/src/main/kotlin/theodolite/util/Results.kt index a8a902ff69742bbb617685ac5cd24bf6c419c370..7116d73cf5b54325c8cfa41b1186d58695628874 100644 --- a/theodolite-quarkus/src/main/kotlin/theodolite/util/Results.kt +++ b/theodolite-quarkus/src/main/kotlin/theodolite/util/Results.kt @@ -2,18 +2,47 @@ package theodolite.util import io.quarkus.runtime.annotations.RegisterForReflection +/** + * Central class that saves the state of a execution of Theodolite. For an execution, it is used to save the result of + * individual experiments. Further, it is used by the RestrictionStrategy to + * perform the [theodolite.strategies.restriction.RestrictionStrategy]. + */ @RegisterForReflection class Results { private val results: MutableMap<Pair<LoadDimension, Resource>, Boolean> = mutableMapOf() + /** + * Set the result for an experiment. + * + * @param experiment A pair that identifies the experiment by the [LoadDimension] and [Resource]. + * @param successful the result of the experiment. Successful == true and Unsuccessful == false. + */ fun setResult(experiment: Pair<LoadDimension, Resource>, successful: Boolean) { this.results[experiment] = successful } + /** + * Get the result for an experiment. + * + * @param experiment A pair that identifies the experiment by the [LoadDimension] and [Resource]. + * @return true if the experiment was successful and false otherwise. If the result has not been reported so far, + * null is returned. + * + * @see Resource + */ fun getResult(experiment: Pair<LoadDimension, Resource>): Boolean? { return this.results[experiment] } + /** + * Get the smallest suitable number of instances for a specified [LoadDimension]. + * + * @param load the [LoadDimension] + * + * @return the smallest suitable number of resources. If the experiment was not executed yet, + * a @see Resource with the constant Int.MAX_VALUE as value is returned. If no experiments have been marked as either successful or unsuccessful + * yet, a Resource with the constant value Int.MIN_VALUE is returned. + */ fun getMinRequiredInstances(load: LoadDimension?): Resource? { if (this.results.isEmpty()) return Resource(Int.MIN_VALUE, emptyList()) @@ -30,6 +59,14 @@ class Results { return requiredInstances } + /** + * Get the largest [LoadDimension] that has been reported executed successfully (or unsuccessfully) so far, for a + * [LoadDimension] and is smaller than the given [LoadDimension]. + * + * @param load the [LoadDimension] + * + * @return the largest [LoadDimension] or null, if there is none for this [LoadDimension] + */ fun getMaxBenchmarkedLoad(load: LoadDimension): LoadDimension? { var maxBenchmarkedLoad: LoadDimension? = null for (experiment in results) { diff --git a/theodolite-quarkus/src/main/kotlin/theodolite/util/TypeName.kt b/theodolite-quarkus/src/main/kotlin/theodolite/util/TypeName.kt index 20758466c5d5efbcb999bf0bf9c4edbe63ea1032..f20fc7c9ce6757be75d9317e76c23a68b09914bd 100644 --- a/theodolite-quarkus/src/main/kotlin/theodolite/util/TypeName.kt +++ b/theodolite-quarkus/src/main/kotlin/theodolite/util/TypeName.kt @@ -3,6 +3,9 @@ package theodolite.util import com.fasterxml.jackson.databind.annotation.JsonDeserialize import io.quarkus.runtime.annotations.RegisterForReflection +/** + * The TypeName encapsulates a list of [PatcherDefinition] along with a typeName that specifies for what the [PatcherDefinition] should be used. + */ @RegisterForReflection @JsonDeserialize class TypeName { diff --git a/theodolite-quarkus/src/main/kotlin/theodolite/util/YamlParser.kt b/theodolite-quarkus/src/main/kotlin/theodolite/util/YamlParser.kt index ec91150df6c9999c418660424aa8b74163030e34..ce69894e4145372aef07286ae315d11631a4df3f 100644 --- a/theodolite-quarkus/src/main/kotlin/theodolite/util/YamlParser.kt +++ b/theodolite-quarkus/src/main/kotlin/theodolite/util/YamlParser.kt @@ -6,6 +6,9 @@ import java.io.File import java.io.FileInputStream import java.io.InputStream +/** + * The YamlParser parses a YAML file + */ class YamlParser : Parser { override fun <T> parse(path: String, E: Class<T>): T? { val input: InputStream = FileInputStream(File(path))