diff --git a/theodolite-quarkus/src/main/kotlin/theodolite/benchmark/KubernetesBenchmark.kt b/theodolite-quarkus/src/main/kotlin/theodolite/benchmark/KubernetesBenchmark.kt index 1e2d9a2e675bee3817c493e5afc89333ca508ed8..0110e1d7cdbbe150fc6d76bc303770b989f5d739 100644 --- a/theodolite-quarkus/src/main/kotlin/theodolite/benchmark/KubernetesBenchmark.kt +++ b/theodolite-quarkus/src/main/kotlin/theodolite/benchmark/KubernetesBenchmark.kt @@ -4,7 +4,7 @@ import io.fabric8.kubernetes.api.model.KubernetesResource import io.fabric8.kubernetes.client.DefaultKubernetesClient import mu.KotlinLogging import theodolite.k8s.K8sResourceLoader -import theodolite.patcher.PatcherManager +import theodolite.patcher.PatcherFactory import theodolite.util.* private val logger = KotlinLogging.logger {} @@ -43,20 +43,15 @@ class KubernetesBenchmark : Benchmark { configurationOverrides: List<ConfigurationOverride> ): BenchmarkDeployment { val resources = loadKubernetesResources(this.appResource + this.loadGenResource) - val patcherManager = PatcherManager() + val patcherFactory = PatcherFactory() - // patch res and load - patcherManager.createAndApplyPatcher(res.getType(), this.resourceTypes, resources, res.get()) - patcherManager.createAndApplyPatcher(load.getType(), this.loadTypes, resources, load.get().toString()) + // patch the load dimension the resources + load.getType().forEach { patcherDefinition -> patcherFactory.createPatcher(patcherDefinition, resources).patch(load.get().toString()) } + res.getType().forEach{ patcherDefinition -> patcherFactory.createPatcher(patcherDefinition, resources).patch(res.get().toString()) } + + // Patch the given overrides + configurationOverrides.forEach { override -> patcherFactory.createPatcher(override.patcher, resources).patch(override.value) } - // patch overrides - configurationOverrides.forEach { override -> - patcherManager.applyPatcher( - listOf(override.patcher), - resources, - override.value - ) - } return KubernetesBenchmarkDeployment( namespace = namespace, diff --git a/theodolite-quarkus/src/main/kotlin/theodolite/execution/TheodoliteExecutor.kt b/theodolite-quarkus/src/main/kotlin/theodolite/execution/TheodoliteExecutor.kt index d1d66af6afdf2f207742b86c89e0771cd2467012..89a3fb4fe5e0f81aa12aa566c9dbb2630c9b9bfe 100644 --- a/theodolite-quarkus/src/main/kotlin/theodolite/execution/TheodoliteExecutor.kt +++ b/theodolite-quarkus/src/main/kotlin/theodolite/execution/TheodoliteExecutor.kt @@ -2,6 +2,7 @@ package theodolite.execution import theodolite.benchmark.BenchmarkExecution import theodolite.benchmark.KubernetesBenchmark +import theodolite.patcher.PatcherDefinitionFactory import theodolite.strategies.StrategyFactory import theodolite.strategies.searchstrategy.CompositeStrategy import theodolite.util.Config @@ -20,6 +21,9 @@ class TheodoliteExecutor( val strategyFactory = StrategyFactory() val executionDuration = Duration.ofSeconds(config.execution.duration) + val resourcePatcherDefinition = PatcherDefinitionFactory().createPatcherDefinition(config.resources.resourceType, this.kubernetesBenchmark.resourceTypes) + val loadDimensionPatcherDefinition = PatcherDefinitionFactory().createPatcherDefinition(config.load.loadType, this.kubernetesBenchmark.loadTypes) + val executor = BenchmarkExecutorImpl( kubernetesBenchmark, @@ -30,9 +34,8 @@ class TheodoliteExecutor( ) return Config( - loads = config.load.loadValues.map { load -> LoadDimension(load, config.load.loadType) }, - resources = config.resources.resourceValues.map - { resource -> Resource(resource, config.resources.resourceType) }, + loads = config.load.loadValues.map { load -> LoadDimension(load, loadDimensionPatcherDefinition) }, + resources = config.resources.resourceValues.map { resource -> Resource(resource, resourcePatcherDefinition) }, compositeStrategy = CompositeStrategy( benchmarkExecutor = executor, searchStrategy = strategyFactory.createSearchStrategy(executor, config.execution.strategy), diff --git a/theodolite-quarkus/src/main/kotlin/theodolite/patcher/PatcherDefinitionFactory.kt b/theodolite-quarkus/src/main/kotlin/theodolite/patcher/PatcherDefinitionFactory.kt new file mode 100644 index 0000000000000000000000000000000000000000..096d19e7c54ce3ac308ca59edee7861a7041dde0 --- /dev/null +++ b/theodolite-quarkus/src/main/kotlin/theodolite/patcher/PatcherDefinitionFactory.kt @@ -0,0 +1,12 @@ +package theodolite.patcher + +import theodolite.util.PatcherDefinition +import theodolite.util.TypeName + +class PatcherDefinitionFactory { + 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 new file mode 100644 index 0000000000000000000000000000000000000000..1a3e56bfe7aaf01526dc8ce4ed2c106383e1f8b7 --- /dev/null +++ b/theodolite-quarkus/src/main/kotlin/theodolite/patcher/PatcherFactory.kt @@ -0,0 +1,28 @@ +package theodolite.patcher + +import io.fabric8.kubernetes.api.model.KubernetesResource +import theodolite.util.PatcherDefinition + +class PatcherFactory { + 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) + "EnvVarPatcher" -> EnvVarPatcher(resource, patcherDefinition.container, patcherDefinition.variableName) + "NodeSelectorPatcher" -> NodeSelectorPatcher(resource, patcherDefinition.variableName) + "ResourceLimitPatcher" -> ResourceLimitPatcher( + resource, + patcherDefinition.container, + patcherDefinition.variableName + ) + "ResourceRequestPatcher" -> ResourceRequestPatcher( + resource, + patcherDefinition.container, + patcherDefinition.variableName + ) + 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/PatcherManager.kt b/theodolite-quarkus/src/main/kotlin/theodolite/patcher/PatcherManager.kt deleted file mode 100644 index 5557eb4b98d5da3bbc8b8d82227de29335c5da67..0000000000000000000000000000000000000000 --- a/theodolite-quarkus/src/main/kotlin/theodolite/patcher/PatcherManager.kt +++ /dev/null @@ -1,73 +0,0 @@ -package theodolite.patcher - -import io.fabric8.kubernetes.api.model.KubernetesResource -import theodolite.util.PatcherDefinition -import theodolite.util.TypeName - -class PatcherManager { - private fun createK8sPatcher( - 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) - "EnvVarPatcher" -> EnvVarPatcher(resource, patcherDefinition.container, patcherDefinition.variableName) - "NodeSelectorPatcher" -> NodeSelectorPatcher(resource, patcherDefinition.variableName) - "ResourceLimitPatcher" -> ResourceLimitPatcher( - resource, - patcherDefinition.container, - patcherDefinition.variableName - ) - "ResourceRequestPatcher" -> ResourceRequestPatcher( - resource, - patcherDefinition.container, - patcherDefinition.variableName - ) - else -> throw IllegalArgumentException("Patcher type ${patcherDefinition.type} not found") - } - } - - private fun getPatcherDef(requiredType: String, patcherTypes: List<TypeName>): List<PatcherDefinition> { - return patcherTypes - .filter { type -> type.typeName == requiredType } - .flatMap { type -> type.patchers } - } - - /** - * This function first creates a patcher definition and - * then patches the list of resources based on this patcher definition - * - * @param type Patcher type, for example "EnvVarPatcher" - * @param patcherTypes List of patcher types definitions, for example for resources and threads - * @param resources List of K8s resources, a patcher takes the resources that are needed - * @param value The value to patch - */ - fun createAndApplyPatcher( - type: String, - patcherTypes: List<TypeName>, - resources: List<Pair<String, KubernetesResource>>, - value: Any - ) { - this.getPatcherDef(type, patcherTypes) - .forEach { patcherDef -> - createK8sPatcher(patcherDef, resources).patch(value) - } - } - - /** - * Patch a resource based on the given patcher definition, a list of resources and a value to patch - * - * @param patcherDefinition The patcher definition - * @param resources List of patcher types definitions, for example for resources and threads - * @param value The value to patch - */ - fun applyPatcher( - patcherDefinition: List<PatcherDefinition>, - resources: List<Pair<String, KubernetesResource>>, - value: Any - ) { - patcherDefinition.forEach { def -> this.createK8sPatcher(def, resources).patch(value) } - } -} 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 dfd6bc8052b8ca44ac8a9220fbf1e3c8df43b93d..6fed9b5d808405b42ad374346862f050ce192141 100644 --- a/theodolite-quarkus/src/main/kotlin/theodolite/strategies/restriction/LowerBoundRestriction.kt +++ b/theodolite-quarkus/src/main/kotlin/theodolite/strategies/restriction/LowerBoundRestriction.kt @@ -13,7 +13,7 @@ import theodolite.util.Results class LowerBoundRestriction(results: Results) : RestrictionStrategy(results) { override fun next(load: LoadDimension, resources: List<Resource>): List<Resource> { val maxLoad: LoadDimension? = this.results.getMaxBenchmarkedLoad(load) - var lowerBound: Resource? = this.results.getMinRequiredInstances(maxLoad, resources[0].getType()) + var lowerBound: Resource? = this.results.getMinRequiredInstances(maxLoad) if (lowerBound == null) { lowerBound = resources[0] } diff --git a/theodolite-quarkus/src/main/kotlin/theodolite/util/LoadDimension.kt b/theodolite-quarkus/src/main/kotlin/theodolite/util/LoadDimension.kt index 29d47460bc49ec44e9a46a129e3dab3246f305b6..43cb861b2d6bbbe457a61d6f98f42487aad1d216 100644 --- a/theodolite-quarkus/src/main/kotlin/theodolite/util/LoadDimension.kt +++ b/theodolite-quarkus/src/main/kotlin/theodolite/util/LoadDimension.kt @@ -1,11 +1,11 @@ package theodolite.util -data class LoadDimension(private val number: Int, private val type: String) { +data class LoadDimension(private val number: Int, private val type: List<PatcherDefinition>) { fun get(): Int { return this.number } - fun getType(): String { + fun getType(): List<PatcherDefinition> { return this.type } } diff --git a/theodolite-quarkus/src/main/kotlin/theodolite/util/Resource.kt b/theodolite-quarkus/src/main/kotlin/theodolite/util/Resource.kt index cb172e0b8de4cff5fc08828a177f3dd9d58bbb53..094e89ebb0d4566499068331ca2fc890f3335597 100644 --- a/theodolite-quarkus/src/main/kotlin/theodolite/util/Resource.kt +++ b/theodolite-quarkus/src/main/kotlin/theodolite/util/Resource.kt @@ -1,11 +1,11 @@ package theodolite.util -data class Resource(private val number: Int, private val type: String) { +data class Resource(private val number: Int, private val type: List<PatcherDefinition>) { fun get(): Int { return this.number } - fun getType(): String { + 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 91bde71792fdca383fc9511658bab39aa58d12ce..c827e8303f5c08f4f612476a1069ecefc0a7308b 100644 --- a/theodolite-quarkus/src/main/kotlin/theodolite/util/Results.kt +++ b/theodolite-quarkus/src/main/kotlin/theodolite/util/Results.kt @@ -11,10 +11,10 @@ class Results { return this.results[experiment] } - fun getMinRequiredInstances(load: LoadDimension?, resourceTyp: String): Resource? { - if (this.results.isEmpty()) return Resource(Int.MIN_VALUE, resourceTyp) + fun getMinRequiredInstances(load: LoadDimension?): Resource? { + if (this.results.isEmpty()) return Resource(Int.MIN_VALUE, emptyList()) - var requiredInstances: Resource? = Resource(Int.MAX_VALUE, resourceTyp) + var requiredInstances: Resource? = Resource(Int.MAX_VALUE, emptyList()) for (experiment in results) { if (experiment.key.first == load && experiment.value) { if (requiredInstances == null) { diff --git a/theodolite-quarkus/src/test/kotlin/theodolite/CompositeStrategyTest.kt b/theodolite-quarkus/src/test/kotlin/theodolite/CompositeStrategyTest.kt index f269768cbcebfb778ee41f367ee5cc9b6fee1f19..67c9857220a0d419183644ffaf8c6a6e16a6ce9b 100644 --- a/theodolite-quarkus/src/test/kotlin/theodolite/CompositeStrategyTest.kt +++ b/theodolite-quarkus/src/test/kotlin/theodolite/CompositeStrategyTest.kt @@ -26,8 +26,8 @@ class CompositeStrategyTest { arrayOf(false, false, false, false, false, false, true), arrayOf(false, false, false, false, false, false, false) ) - val mockLoads: List<LoadDimension> = (0..6).map { number -> LoadDimension(number, "NumSensors") } - val mockResources: List<Resource> = (0..6).map { number -> Resource(number, "Instances") } + val mockLoads: List<LoadDimension> = (0..6).map { number -> LoadDimension(number, emptyList()) } + val mockResources: List<Resource> = (0..6).map { number -> Resource(number, emptyList()) } val results = Results() val benchmark = TestBenchmark() val sloChecker: BenchmarkExecution.Slo = BenchmarkExecution.Slo() @@ -38,7 +38,7 @@ class CompositeStrategyTest { CompositeStrategy(benchmarkExecutor, linearSearch, setOf(lowerBoundRestriction)) val actual: ArrayList<Resource?> = ArrayList() - val expected: ArrayList<Resource?> = ArrayList(listOf(0, 2, 2, 3, 4, 6).map { x -> Resource(x, "Instances") }) + val expected: ArrayList<Resource?> = ArrayList(listOf(0, 2, 2, 3, 4, 6).map { x -> Resource(x, emptyList()) }) expected.add(null) for (load in mockLoads) { @@ -59,8 +59,8 @@ class CompositeStrategyTest { arrayOf(false, false, false, false, false, false, true), arrayOf(false, false, false, false, false, false, false) ) - val mockLoads: List<LoadDimension> = (0..6).map { number -> LoadDimension(number, "NumSensors") } - val mockResources: List<Resource> = (0..6).map { number -> Resource(number, "Instances") } + val mockLoads: List<LoadDimension> = (0..6).map { number -> LoadDimension(number, emptyList()) } + val mockResources: List<Resource> = (0..6).map { number -> Resource(number, emptyList()) } val results = Results() val benchmark = TestBenchmark() val sloChecker: BenchmarkExecution.Slo = BenchmarkExecution.Slo() @@ -72,7 +72,7 @@ class CompositeStrategyTest { CompositeStrategy(benchmarkExecutorImpl, binarySearch, setOf(lowerBoundRestriction)) val actual: ArrayList<Resource?> = ArrayList() - val expected: ArrayList<Resource?> = ArrayList(listOf(0, 2, 2, 3, 4, 6).map { x -> Resource(x, "Instances") }) + val expected: ArrayList<Resource?> = ArrayList(listOf(0, 2, 2, 3, 4, 6).map { x -> Resource(x, emptyList()) }) expected.add(null) for (load in mockLoads) { @@ -93,8 +93,8 @@ class CompositeStrategyTest { arrayOf(false, false, false, false, false, false, true, true), arrayOf(false, false, false, false, false, false, false, true) ) - val mockLoads: List<LoadDimension> = (0..6).map { number -> LoadDimension(number, "NumSensors") } - val mockResources: List<Resource> = (0..7).map { number -> Resource(number, "Instances") } + val mockLoads: List<LoadDimension> = (0..6).map { number -> LoadDimension(number, emptyList()) } + val mockResources: List<Resource> = (0..7).map { number -> Resource(number, emptyList()) } val results = Results() val benchmark = TestBenchmark() val sloChecker: BenchmarkExecution.Slo = BenchmarkExecution.Slo() @@ -106,7 +106,7 @@ class CompositeStrategyTest { val actual: ArrayList<Resource?> = ArrayList() val expected: ArrayList<Resource?> = - ArrayList(listOf(0, 2, 2, 3, 4, 6, 7).map { x -> Resource(x, "Instances") }) + ArrayList(listOf(0, 2, 2, 3, 4, 6, 7).map { x -> Resource(x, emptyList()) }) for (load in mockLoads) { actual.add(strategy.findSuitableResource(load, mockResources)) diff --git a/theodolite-quarkus/src/test/kotlin/theodolite/ResourceLimitPatcherTest.kt b/theodolite-quarkus/src/test/kotlin/theodolite/ResourceLimitPatcherTest.kt index 2170a6a54cf12433b18cc621d78a8608f3f71d63..82e4bc5d77f3f35d217c56a377513c0e7d329170 100644 --- a/theodolite-quarkus/src/test/kotlin/theodolite/ResourceLimitPatcherTest.kt +++ b/theodolite-quarkus/src/test/kotlin/theodolite/ResourceLimitPatcherTest.kt @@ -6,7 +6,7 @@ import io.quarkus.test.junit.QuarkusTest import io.smallrye.common.constraint.Assert.assertTrue import org.junit.jupiter.api.Test import theodolite.k8s.K8sResourceLoader -import theodolite.patcher.PatcherManager +import theodolite.patcher.PatcherFactory import theodolite.util.PatcherDefinition /** @@ -23,7 +23,7 @@ import theodolite.util.PatcherDefinition class ResourceLimitPatcherTest { val testPath = "./src/main/resources/testYaml/" val loader = K8sResourceLoader(DefaultKubernetesClient().inNamespace("")) - val manager = PatcherManager() + val patcherFactory = PatcherFactory() fun applyTest(fileName: String) { val cpuValue = "50m" @@ -42,20 +42,17 @@ class ResourceLimitPatcherTest { defMEM.container = "uc-application" defMEM.type = "ResourceLimitPatcher" - manager.applyPatcher( - patcherDefinition = listOf(defCPU), - resources = listOf(Pair("cpu-memory-deployment.yaml", k8sResource)), - value = cpuValue - ) - manager.applyPatcher( - patcherDefinition = listOf(defMEM), - resources = listOf(Pair("cpu-memory-deployment.yaml", k8sResource)), - value = memValue - ) + patcherFactory.createPatcher( + patcherDefinition = defCPU, + k8sResources = listOf(Pair("cpu-memory-deployment.yaml", k8sResource)) + ).patch(value = cpuValue) + patcherFactory.createPatcher( + patcherDefinition = defMEM, + k8sResources = listOf(Pair("cpu-memory-deployment.yaml", k8sResource)) + ).patch(value = memValue) k8sResource.spec.template.spec.containers.filter { it.name == defCPU.container } .forEach { - println(it) assertTrue(it.resources.limits["cpu"].toString() == cpuValue) assertTrue(it.resources.limits["memory"].toString() == memValue) } diff --git a/theodolite-quarkus/src/test/kotlin/theodolite/ResourceRequestPatcherTest.kt b/theodolite-quarkus/src/test/kotlin/theodolite/ResourceRequestPatcherTest.kt index 108142843949913eb6db34bb268eab2e91fda3cf..3cd6b012f09c5471b1b011b5cd03e61a0fab1c4e 100644 --- a/theodolite-quarkus/src/test/kotlin/theodolite/ResourceRequestPatcherTest.kt +++ b/theodolite-quarkus/src/test/kotlin/theodolite/ResourceRequestPatcherTest.kt @@ -6,7 +6,7 @@ import io.quarkus.test.junit.QuarkusTest import io.smallrye.common.constraint.Assert.assertTrue import org.junit.jupiter.api.Test import theodolite.k8s.K8sResourceLoader -import theodolite.patcher.PatcherManager +import theodolite.patcher.PatcherFactory import theodolite.util.PatcherDefinition /** @@ -23,7 +23,7 @@ import theodolite.util.PatcherDefinition class ResourceRequestPatcherTest { val testPath = "./src/main/resources/testYaml/" val loader = K8sResourceLoader(DefaultKubernetesClient().inNamespace("")) - val manager = PatcherManager() + val patcherFactory = PatcherFactory() fun applyTest(fileName: String) { val cpuValue = "50m" @@ -42,20 +42,17 @@ class ResourceRequestPatcherTest { defMEM.container = "uc-application" defMEM.type = "ResourceRequestPatcher" - manager.applyPatcher( - patcherDefinition = listOf(defCPU), - resources = listOf(Pair("cpu-memory-deployment.yaml", k8sResource)), - value = cpuValue - ) - manager.applyPatcher( - patcherDefinition = listOf(defMEM), - resources = listOf(Pair("cpu-memory-deployment.yaml", k8sResource)), - value = memValue - ) + patcherFactory.createPatcher( + patcherDefinition = defCPU, + k8sResources = listOf(Pair("cpu-memory-deployment.yaml", k8sResource)) + ).patch(value = cpuValue) + patcherFactory.createPatcher( + patcherDefinition = defMEM, + k8sResources = listOf(Pair("cpu-memory-deployment.yaml", k8sResource)) + ).patch(value = memValue) k8sResource.spec.template.spec.containers.filter { it.name == defCPU.container } .forEach { - println(it) assertTrue(it.resources.requests["cpu"].toString() == cpuValue) assertTrue(it.resources.requests["memory"].toString() == memValue) }