Skip to content
Snippets Groups Projects
Commit ef613352 authored by Sören Henning's avatar Sören Henning
Browse files

Merge branch '164-restructure-patcher' into 'theodolite-kotlin'

Restructuring to create the PatcherDefinition inside the TheodoliteExecutor

See merge request !97
parents d4ead8a5 e7a2a0c5
No related branches found
No related tags found
4 merge requests!159Re-implementation of Theodolite with Kotlin/Quarkus,!157Update Graal Image in CI pipeline,!97Restructuring to create the PatcherDefinition inside the TheodoliteExecutor,!83WIP: Re-implementation of Theodolite with Kotlin/Quarkus
Pipeline #2376 passed
Showing
with 91 additions and 59 deletions
...@@ -4,7 +4,7 @@ import io.fabric8.kubernetes.api.model.KubernetesResource ...@@ -4,7 +4,7 @@ import io.fabric8.kubernetes.api.model.KubernetesResource
import io.fabric8.kubernetes.client.DefaultKubernetesClient import io.fabric8.kubernetes.client.DefaultKubernetesClient
import mu.KotlinLogging import mu.KotlinLogging
import theodolite.k8s.K8sResourceLoader import theodolite.k8s.K8sResourceLoader
import theodolite.patcher.PatcherManager import theodolite.patcher.PatcherFactory
import theodolite.util.* import theodolite.util.*
private val logger = KotlinLogging.logger {} private val logger = KotlinLogging.logger {}
...@@ -43,20 +43,15 @@ class KubernetesBenchmark : Benchmark { ...@@ -43,20 +43,15 @@ class KubernetesBenchmark : Benchmark {
configurationOverrides: List<ConfigurationOverride> configurationOverrides: List<ConfigurationOverride>
): BenchmarkDeployment { ): BenchmarkDeployment {
val resources = loadKubernetesResources(this.appResource + this.loadGenResource) val resources = loadKubernetesResources(this.appResource + this.loadGenResource)
val patcherManager = PatcherManager() val patcherFactory = PatcherFactory()
// patch res and load // patch the load dimension the resources
patcherManager.createAndApplyPatcher(res.getType(), this.resourceTypes, resources, res.get()) load.getType().forEach { patcherDefinition -> patcherFactory.createPatcher(patcherDefinition, resources).patch(load.get().toString()) }
patcherManager.createAndApplyPatcher(load.getType(), this.loadTypes, resources, 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( return KubernetesBenchmarkDeployment(
namespace = namespace, namespace = namespace,
......
...@@ -2,6 +2,7 @@ package theodolite.execution ...@@ -2,6 +2,7 @@ package theodolite.execution
import theodolite.benchmark.BenchmarkExecution import theodolite.benchmark.BenchmarkExecution
import theodolite.benchmark.KubernetesBenchmark import theodolite.benchmark.KubernetesBenchmark
import theodolite.patcher.PatcherDefinitionFactory
import theodolite.strategies.StrategyFactory import theodolite.strategies.StrategyFactory
import theodolite.strategies.searchstrategy.CompositeStrategy import theodolite.strategies.searchstrategy.CompositeStrategy
import theodolite.util.Config import theodolite.util.Config
...@@ -20,6 +21,9 @@ class TheodoliteExecutor( ...@@ -20,6 +21,9 @@ class TheodoliteExecutor(
val strategyFactory = StrategyFactory() val strategyFactory = StrategyFactory()
val executionDuration = Duration.ofSeconds(config.execution.duration) 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 = val executor =
BenchmarkExecutorImpl( BenchmarkExecutorImpl(
kubernetesBenchmark, kubernetesBenchmark,
...@@ -30,9 +34,8 @@ class TheodoliteExecutor( ...@@ -30,9 +34,8 @@ class TheodoliteExecutor(
) )
return Config( return Config(
loads = config.load.loadValues.map { load -> LoadDimension(load, config.load.loadType) }, loads = config.load.loadValues.map { load -> LoadDimension(load, loadDimensionPatcherDefinition) },
resources = config.resources.resourceValues.map resources = config.resources.resourceValues.map { resource -> Resource(resource, resourcePatcherDefinition) },
{ resource -> Resource(resource, config.resources.resourceType) },
compositeStrategy = CompositeStrategy( compositeStrategy = CompositeStrategy(
benchmarkExecutor = executor, benchmarkExecutor = executor,
searchStrategy = strategyFactory.createSearchStrategy(executor, config.execution.strategy), searchStrategy = strategyFactory.createSearchStrategy(executor, config.execution.strategy),
......
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 }
}
}
...@@ -2,13 +2,10 @@ package theodolite.patcher ...@@ -2,13 +2,10 @@ package theodolite.patcher
import io.fabric8.kubernetes.api.model.KubernetesResource import io.fabric8.kubernetes.api.model.KubernetesResource
import theodolite.util.PatcherDefinition import theodolite.util.PatcherDefinition
import theodolite.util.TypeName
class PatcherManager { class PatcherFactory {
private fun createK8sPatcher( fun createPatcher(patcherDefinition: PatcherDefinition,
patcherDefinition: PatcherDefinition, k8sResources: List<Pair<String, KubernetesResource>>) : Patcher {
k8sResources: List<Pair<String, KubernetesResource>>
): Patcher {
val resource = val resource =
k8sResources.filter { it.first == patcherDefinition.resource }.map { resource -> resource.second }[0] k8sResources.filter { it.first == patcherDefinition.resource }.map { resource -> resource.second }[0]
return when (patcherDefinition.type) { return when (patcherDefinition.type) {
...@@ -28,46 +25,4 @@ class PatcherManager { ...@@ -28,46 +25,4 @@ class PatcherManager {
else -> throw IllegalArgumentException("Patcher type ${patcherDefinition.type} not found") 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) }
}
} }
\ No newline at end of file
...@@ -13,7 +13,7 @@ import theodolite.util.Results ...@@ -13,7 +13,7 @@ import theodolite.util.Results
class LowerBoundRestriction(results: Results) : RestrictionStrategy(results) { class LowerBoundRestriction(results: Results) : RestrictionStrategy(results) {
override fun next(load: LoadDimension, resources: List<Resource>): List<Resource> { override fun next(load: LoadDimension, resources: List<Resource>): List<Resource> {
val maxLoad: LoadDimension? = this.results.getMaxBenchmarkedLoad(load) 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) { if (lowerBound == null) {
lowerBound = resources[0] lowerBound = resources[0]
} }
......
package theodolite.util 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 { fun get(): Int {
return this.number return this.number
} }
fun getType(): String { fun getType(): List<PatcherDefinition> {
return this.type return this.type
} }
} }
package theodolite.util 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 { fun get(): Int {
return this.number return this.number
} }
fun getType(): String { fun getType(): List<PatcherDefinition> {
return this.type return this.type
} }
} }
...@@ -11,10 +11,10 @@ class Results { ...@@ -11,10 +11,10 @@ class Results {
return this.results[experiment] return this.results[experiment]
} }
fun getMinRequiredInstances(load: LoadDimension?, resourceTyp: String): Resource? { fun getMinRequiredInstances(load: LoadDimension?): Resource? {
if (this.results.isEmpty()) return Resource(Int.MIN_VALUE, resourceTyp) 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) { for (experiment in results) {
if (experiment.key.first == load && experiment.value) { if (experiment.key.first == load && experiment.value) {
if (requiredInstances == null) { if (requiredInstances == null) {
......
...@@ -26,8 +26,8 @@ class CompositeStrategyTest { ...@@ -26,8 +26,8 @@ class CompositeStrategyTest {
arrayOf(false, false, false, false, false, false, true), arrayOf(false, false, false, false, false, false, true),
arrayOf(false, false, false, false, false, false, false) arrayOf(false, false, false, false, false, false, false)
) )
val mockLoads: List<LoadDimension> = (0..6).map { number -> LoadDimension(number, "NumSensors") } val mockLoads: List<LoadDimension> = (0..6).map { number -> LoadDimension(number, emptyList()) }
val mockResources: List<Resource> = (0..6).map { number -> Resource(number, "Instances") } val mockResources: List<Resource> = (0..6).map { number -> Resource(number, emptyList()) }
val results = Results() val results = Results()
val benchmark = TestBenchmark() val benchmark = TestBenchmark()
val sloChecker: BenchmarkExecution.Slo = BenchmarkExecution.Slo() val sloChecker: BenchmarkExecution.Slo = BenchmarkExecution.Slo()
...@@ -38,7 +38,7 @@ class CompositeStrategyTest { ...@@ -38,7 +38,7 @@ class CompositeStrategyTest {
CompositeStrategy(benchmarkExecutor, linearSearch, setOf(lowerBoundRestriction)) CompositeStrategy(benchmarkExecutor, linearSearch, setOf(lowerBoundRestriction))
val actual: ArrayList<Resource?> = ArrayList() 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) expected.add(null)
for (load in mockLoads) { for (load in mockLoads) {
...@@ -59,8 +59,8 @@ class CompositeStrategyTest { ...@@ -59,8 +59,8 @@ class CompositeStrategyTest {
arrayOf(false, false, false, false, false, false, true), arrayOf(false, false, false, false, false, false, true),
arrayOf(false, false, false, false, false, false, false) arrayOf(false, false, false, false, false, false, false)
) )
val mockLoads: List<LoadDimension> = (0..6).map { number -> LoadDimension(number, "NumSensors") } val mockLoads: List<LoadDimension> = (0..6).map { number -> LoadDimension(number, emptyList()) }
val mockResources: List<Resource> = (0..6).map { number -> Resource(number, "Instances") } val mockResources: List<Resource> = (0..6).map { number -> Resource(number, emptyList()) }
val results = Results() val results = Results()
val benchmark = TestBenchmark() val benchmark = TestBenchmark()
val sloChecker: BenchmarkExecution.Slo = BenchmarkExecution.Slo() val sloChecker: BenchmarkExecution.Slo = BenchmarkExecution.Slo()
...@@ -72,7 +72,7 @@ class CompositeStrategyTest { ...@@ -72,7 +72,7 @@ class CompositeStrategyTest {
CompositeStrategy(benchmarkExecutorImpl, binarySearch, setOf(lowerBoundRestriction)) CompositeStrategy(benchmarkExecutorImpl, binarySearch, setOf(lowerBoundRestriction))
val actual: ArrayList<Resource?> = ArrayList() 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) expected.add(null)
for (load in mockLoads) { for (load in mockLoads) {
...@@ -93,8 +93,8 @@ class CompositeStrategyTest { ...@@ -93,8 +93,8 @@ class CompositeStrategyTest {
arrayOf(false, false, false, false, false, false, true, true), arrayOf(false, false, false, false, false, false, true, true),
arrayOf(false, false, false, false, false, false, false, true) arrayOf(false, false, false, false, false, false, false, true)
) )
val mockLoads: List<LoadDimension> = (0..6).map { number -> LoadDimension(number, "NumSensors") } val mockLoads: List<LoadDimension> = (0..6).map { number -> LoadDimension(number, emptyList()) }
val mockResources: List<Resource> = (0..7).map { number -> Resource(number, "Instances") } val mockResources: List<Resource> = (0..7).map { number -> Resource(number, emptyList()) }
val results = Results() val results = Results()
val benchmark = TestBenchmark() val benchmark = TestBenchmark()
val sloChecker: BenchmarkExecution.Slo = BenchmarkExecution.Slo() val sloChecker: BenchmarkExecution.Slo = BenchmarkExecution.Slo()
...@@ -106,7 +106,7 @@ class CompositeStrategyTest { ...@@ -106,7 +106,7 @@ class CompositeStrategyTest {
val actual: ArrayList<Resource?> = ArrayList() val actual: ArrayList<Resource?> = ArrayList()
val expected: ArrayList<Resource?> = 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) { for (load in mockLoads) {
actual.add(strategy.findSuitableResource(load, mockResources)) actual.add(strategy.findSuitableResource(load, mockResources))
......
...@@ -6,7 +6,7 @@ import io.quarkus.test.junit.QuarkusTest ...@@ -6,7 +6,7 @@ import io.quarkus.test.junit.QuarkusTest
import io.smallrye.common.constraint.Assert.assertTrue import io.smallrye.common.constraint.Assert.assertTrue
import org.junit.jupiter.api.Test import org.junit.jupiter.api.Test
import theodolite.k8s.K8sResourceLoader import theodolite.k8s.K8sResourceLoader
import theodolite.patcher.PatcherManager import theodolite.patcher.PatcherFactory
import theodolite.util.PatcherDefinition import theodolite.util.PatcherDefinition
/** /**
...@@ -23,7 +23,7 @@ import theodolite.util.PatcherDefinition ...@@ -23,7 +23,7 @@ import theodolite.util.PatcherDefinition
class ResourceLimitPatcherTest { class ResourceLimitPatcherTest {
val testPath = "./src/main/resources/testYaml/" val testPath = "./src/main/resources/testYaml/"
val loader = K8sResourceLoader(DefaultKubernetesClient().inNamespace("")) val loader = K8sResourceLoader(DefaultKubernetesClient().inNamespace(""))
val manager = PatcherManager() val patcherFactory = PatcherFactory()
fun applyTest(fileName: String) { fun applyTest(fileName: String) {
val cpuValue = "50m" val cpuValue = "50m"
...@@ -42,20 +42,17 @@ class ResourceLimitPatcherTest { ...@@ -42,20 +42,17 @@ class ResourceLimitPatcherTest {
defMEM.container = "uc-application" defMEM.container = "uc-application"
defMEM.type = "ResourceLimitPatcher" defMEM.type = "ResourceLimitPatcher"
manager.applyPatcher( patcherFactory.createPatcher(
patcherDefinition = listOf(defCPU), patcherDefinition = defCPU,
resources = listOf(Pair("cpu-memory-deployment.yaml", k8sResource)), k8sResources = listOf(Pair("cpu-memory-deployment.yaml", k8sResource))
value = cpuValue ).patch(value = cpuValue)
) patcherFactory.createPatcher(
manager.applyPatcher( patcherDefinition = defMEM,
patcherDefinition = listOf(defMEM), k8sResources = listOf(Pair("cpu-memory-deployment.yaml", k8sResource))
resources = listOf(Pair("cpu-memory-deployment.yaml", k8sResource)), ).patch(value = memValue)
value = memValue
)
k8sResource.spec.template.spec.containers.filter { it.name == defCPU.container } k8sResource.spec.template.spec.containers.filter { it.name == defCPU.container }
.forEach { .forEach {
println(it)
assertTrue(it.resources.limits["cpu"].toString() == cpuValue) assertTrue(it.resources.limits["cpu"].toString() == cpuValue)
assertTrue(it.resources.limits["memory"].toString() == memValue) assertTrue(it.resources.limits["memory"].toString() == memValue)
} }
......
...@@ -6,7 +6,7 @@ import io.quarkus.test.junit.QuarkusTest ...@@ -6,7 +6,7 @@ import io.quarkus.test.junit.QuarkusTest
import io.smallrye.common.constraint.Assert.assertTrue import io.smallrye.common.constraint.Assert.assertTrue
import org.junit.jupiter.api.Test import org.junit.jupiter.api.Test
import theodolite.k8s.K8sResourceLoader import theodolite.k8s.K8sResourceLoader
import theodolite.patcher.PatcherManager import theodolite.patcher.PatcherFactory
import theodolite.util.PatcherDefinition import theodolite.util.PatcherDefinition
/** /**
...@@ -23,7 +23,7 @@ import theodolite.util.PatcherDefinition ...@@ -23,7 +23,7 @@ import theodolite.util.PatcherDefinition
class ResourceRequestPatcherTest { class ResourceRequestPatcherTest {
val testPath = "./src/main/resources/testYaml/" val testPath = "./src/main/resources/testYaml/"
val loader = K8sResourceLoader(DefaultKubernetesClient().inNamespace("")) val loader = K8sResourceLoader(DefaultKubernetesClient().inNamespace(""))
val manager = PatcherManager() val patcherFactory = PatcherFactory()
fun applyTest(fileName: String) { fun applyTest(fileName: String) {
val cpuValue = "50m" val cpuValue = "50m"
...@@ -42,20 +42,17 @@ class ResourceRequestPatcherTest { ...@@ -42,20 +42,17 @@ class ResourceRequestPatcherTest {
defMEM.container = "uc-application" defMEM.container = "uc-application"
defMEM.type = "ResourceRequestPatcher" defMEM.type = "ResourceRequestPatcher"
manager.applyPatcher( patcherFactory.createPatcher(
patcherDefinition = listOf(defCPU), patcherDefinition = defCPU,
resources = listOf(Pair("cpu-memory-deployment.yaml", k8sResource)), k8sResources = listOf(Pair("cpu-memory-deployment.yaml", k8sResource))
value = cpuValue ).patch(value = cpuValue)
) patcherFactory.createPatcher(
manager.applyPatcher( patcherDefinition = defMEM,
patcherDefinition = listOf(defMEM), k8sResources = listOf(Pair("cpu-memory-deployment.yaml", k8sResource))
resources = listOf(Pair("cpu-memory-deployment.yaml", k8sResource)), ).patch(value = memValue)
value = memValue
)
k8sResource.spec.template.spec.containers.filter { it.name == defCPU.container } k8sResource.spec.template.spec.containers.filter { it.name == defCPU.container }
.forEach { .forEach {
println(it)
assertTrue(it.resources.requests["cpu"].toString() == cpuValue) assertTrue(it.resources.requests["cpu"].toString() == cpuValue)
assertTrue(it.resources.requests["memory"].toString() == memValue) assertTrue(it.resources.requests["memory"].toString() == memValue)
} }
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment