From 488ad0e1c072e03a8cb774d1b32f0d95d2848243 Mon Sep 17 00:00:00 2001
From: Marcel Becker <stu117960@mail.uni-kiel.de>
Date: Thu, 3 Mar 2022 16:32:43 +0100
Subject: [PATCH] Incorporated feedback from meeting and implemented TestCase
 for SLOFactory

---
 docs/api-reference/crds.md                    | 23 +-----
 theodolite/crd/crd-execution.yaml             | 12 +---
 .../benchmark/BenchmarkExecution.kt           | 15 ++--
 .../benchmark/KubernetesBenchmark.kt          | 20 ------
 .../main/kotlin/theodolite/benchmark/Slo.kt   | 25 +++++++
 .../theodolite/evaluation/AnalysisExecutor.kt |  4 +-
 .../theodolite/evaluation/SloConfigHandler.kt |  4 +-
 .../theodolite/execution/BenchmarkExecutor.kt |  4 +-
 .../execution/BenchmarkExecutorImpl.kt        |  4 +-
 .../kotlin/theodolite/execution/SloFactory.kt | 15 ++--
 .../execution/TheodoliteExecutor.kt           | 59 ++++++++-------
 .../theodolite/CompositeStrategyTest.kt       |  8 +--
 .../InitialGuessSearchStrategyTest.kt         |  8 +--
 .../theodolite/TestBenchmarkExecutorImpl.kt   |  4 +-
 .../execution/operator/SloFactoryTest.kt      | 72 +++++++++++++++++++
 15 files changed, 156 insertions(+), 121 deletions(-)
 create mode 100644 theodolite/src/main/kotlin/theodolite/benchmark/Slo.kt
 create mode 100644 theodolite/src/test/kotlin/theodolite/execution/operator/SloFactoryTest.kt

diff --git a/docs/api-reference/crds.md b/docs/api-reference/crds.md
index 6bcd35d09..c6600bb05 100644
--- a/docs/api-reference/crds.md
+++ b/docs/api-reference/crds.md
@@ -2089,20 +2089,6 @@ Specifies the scaling resource that is benchmarked.
           The name of the SLO. It must match a SLO specified in the Benchmark.<br/>
         </td>
         <td>true</td>
-      </tr><tr>
-        <td><b>offset</b></td>
-        <td>integer</td>
-        <td>
-          Hours by which the start and end timestamp will be shifted (for different timezones).<br/>
-        </td>
-        <td>false</td>
-      </tr><tr>
-        <td><b>prometheusUrl</b></td>
-        <td>string</td>
-        <td>
-          Connection string for Promehteus.<br/>
-        </td>
-        <td>false</td>
       </tr><tr>
         <td><b>properties</b></td>
         <td>map[string]string</td>
@@ -2111,14 +2097,7 @@ Specifies the scaling resource that is benchmarked.
           <br/>
             <i>Default</i>: map[]<br/>
         </td>
-        <td>false</td>
-      </tr><tr>
-        <td><b>sloType</b></td>
-        <td>string</td>
-        <td>
-          The type of the SLO. It must match 'lag trend'.<br/>
-        </td>
-        <td>false</td>
+        <td>true</td>
       </tr></tbody>
 </table>
 
diff --git a/theodolite/crd/crd-execution.yaml b/theodolite/crd/crd-execution.yaml
index bc73b84c3..a9da4644e 100644
--- a/theodolite/crd/crd-execution.yaml
+++ b/theodolite/crd/crd-execution.yaml
@@ -60,21 +60,11 @@ spec:
                 type: array
                 items:
                   type: object
-                  required: ["name"]
+                  required: ["name", "properties"]
                   properties:
                     name:
                       description: The name of the SLO. It must match a SLO specified in the Benchmark.
                       type: string
-                    # TODO Do we need to keep it here or just move to the Benchmark? Does it make sense to override the type at this point?
-                    sloType:
-                      description: The type of the SLO. It must match 'lag trend'.
-                      type: string
-                    prometheusUrl:
-                      description: Connection string for Promehteus.
-                      type: string
-                    offset:
-                      description: Hours by which the start and end timestamp will be shifted (for different timezones).
-                      type: integer
                     properties:
                         description: (Optional) SLO specific additional arguments.
                         type: object
diff --git a/theodolite/src/main/kotlin/theodolite/benchmark/BenchmarkExecution.kt b/theodolite/src/main/kotlin/theodolite/benchmark/BenchmarkExecution.kt
index 02e9553d2..5ab220bb8 100644
--- a/theodolite/src/main/kotlin/theodolite/benchmark/BenchmarkExecution.kt
+++ b/theodolite/src/main/kotlin/theodolite/benchmark/BenchmarkExecution.kt
@@ -14,7 +14,7 @@ import kotlin.properties.Delegates
  *  - 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.
+ *  - The [slos] further restrict the Benchmark SLOs 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.
@@ -30,7 +30,7 @@ class BenchmarkExecution : KubernetesResource {
     lateinit var benchmark: String
     lateinit var load: LoadDefinition
     lateinit var resources: ResourceDefinition
-    lateinit var slos: List<Slo>
+    lateinit var slos: List<SloConfiguration>
     lateinit var execution: Execution
     lateinit var configOverrides: MutableList<ConfigurationOverride?>
 
@@ -50,19 +50,12 @@ class BenchmarkExecution : KubernetesResource {
     }
 
     /**
-     * Measurable 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.
+     * Further SLO configurations for the SLOs specified in the Benchmark.
      */
     @JsonDeserialize
     @RegisterForReflection
-    class Slo : KubernetesResource {
+    class SloConfiguration : KubernetesResource {
         lateinit var name: String
-        var prometheusUrl: String? = null
-        var offset : Int? = null
         var properties: MutableMap<String, String>? = null
     }
 
diff --git a/theodolite/src/main/kotlin/theodolite/benchmark/KubernetesBenchmark.kt b/theodolite/src/main/kotlin/theodolite/benchmark/KubernetesBenchmark.kt
index d4f9f58fc..ef692c103 100644
--- a/theodolite/src/main/kotlin/theodolite/benchmark/KubernetesBenchmark.kt
+++ b/theodolite/src/main/kotlin/theodolite/benchmark/KubernetesBenchmark.kt
@@ -10,7 +10,6 @@ import theodolite.k8s.K8sManager
 import theodolite.k8s.resourceLoader.K8sResourceLoader
 import theodolite.patcher.PatcherFactory
 import theodolite.util.*
-import kotlin.properties.Delegates
 
 
 private val logger = KotlinLogging.logger {}
@@ -47,25 +46,6 @@ class KubernetesBenchmark : KubernetesResource, Benchmark {
     lateinit var loadGenerator: Resources
     private var namespace = System.getenv("NAMESPACE") ?: DEFAULT_NAMESPACE
 
-    /**
-     * 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 {
-        lateinit var name: String
-        lateinit var sloType: String
-        lateinit var prometheusUrl: String
-        var offset by Delegates.notNull<Int>()
-        lateinit var properties: MutableMap<String, String>
-    }
-
     @Transient
     private var client: NamespacedKubernetesClient = DefaultKubernetesClient().inNamespace(namespace)
 
diff --git a/theodolite/src/main/kotlin/theodolite/benchmark/Slo.kt b/theodolite/src/main/kotlin/theodolite/benchmark/Slo.kt
new file mode 100644
index 000000000..c9aac4a1b
--- /dev/null
+++ b/theodolite/src/main/kotlin/theodolite/benchmark/Slo.kt
@@ -0,0 +1,25 @@
+package theodolite.benchmark
+
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize
+import io.fabric8.kubernetes.api.model.KubernetesResource
+import io.quarkus.runtime.annotations.RegisterForReflection
+import kotlin.properties.Delegates
+
+/**
+ * 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 {
+    lateinit var name: String
+    lateinit var sloType: String
+    lateinit var prometheusUrl: String
+    var offset by Delegates.notNull<Int>()
+    lateinit var properties: MutableMap<String, String>
+}
\ No newline at end of file
diff --git a/theodolite/src/main/kotlin/theodolite/evaluation/AnalysisExecutor.kt b/theodolite/src/main/kotlin/theodolite/evaluation/AnalysisExecutor.kt
index 9ac78b78f..c630b4984 100644
--- a/theodolite/src/main/kotlin/theodolite/evaluation/AnalysisExecutor.kt
+++ b/theodolite/src/main/kotlin/theodolite/evaluation/AnalysisExecutor.kt
@@ -1,6 +1,6 @@
 package theodolite.evaluation
 
-import theodolite.benchmark.KubernetesBenchmark
+import theodolite.benchmark.Slo
 import theodolite.util.EvaluationFailedException
 import theodolite.util.IOHandler
 import theodolite.util.LoadDimension
@@ -16,7 +16,7 @@ import java.util.regex.Pattern
  * @param slo Slo that is used for the analysis.
  */
 class AnalysisExecutor(
-        private val slo: KubernetesBenchmark.Slo,
+        private val slo: Slo,
         private val executionId: Int
 ) {
 
diff --git a/theodolite/src/main/kotlin/theodolite/evaluation/SloConfigHandler.kt b/theodolite/src/main/kotlin/theodolite/evaluation/SloConfigHandler.kt
index b3e13362b..77968d6ce 100644
--- a/theodolite/src/main/kotlin/theodolite/evaluation/SloConfigHandler.kt
+++ b/theodolite/src/main/kotlin/theodolite/evaluation/SloConfigHandler.kt
@@ -1,6 +1,6 @@
 package theodolite.evaluation
 
-import theodolite.benchmark.KubernetesBenchmark
+import theodolite.benchmark.Slo
 import theodolite.util.InvalidPatcherConfigurationException
 import javax.enterprise.context.ApplicationScoped
 
@@ -10,7 +10,7 @@ private const val DROPPED_RECORDS_QUERY = "sum by(job) (kafka_streams_stream_tas
 @ApplicationScoped
 class SloConfigHandler {
     companion object {
-        fun getQueryString(slo: KubernetesBenchmark.Slo): String {
+        fun getQueryString(slo: Slo): String {
             return when (slo.sloType.lowercase()) {
                 SloTypes.GENERIC.value -> slo.properties["promQLQuery"] ?: throw IllegalArgumentException("promQLQuery expected")
                 SloTypes.LAG_TREND.value, SloTypes.LAG_TREND_RATIO.value -> CONSUMER_LAG_QUERY
diff --git a/theodolite/src/main/kotlin/theodolite/execution/BenchmarkExecutor.kt b/theodolite/src/main/kotlin/theodolite/execution/BenchmarkExecutor.kt
index 95b2abeaa..f66d4b76e 100644
--- a/theodolite/src/main/kotlin/theodolite/execution/BenchmarkExecutor.kt
+++ b/theodolite/src/main/kotlin/theodolite/execution/BenchmarkExecutor.kt
@@ -2,7 +2,7 @@ package theodolite.execution
 
 import mu.KotlinLogging
 import theodolite.benchmark.Benchmark
-import theodolite.benchmark.KubernetesBenchmark
+import theodolite.benchmark.Slo
 import theodolite.util.ConfigurationOverride
 import theodolite.util.LoadDimension
 import theodolite.util.Resource
@@ -25,7 +25,7 @@ abstract class BenchmarkExecutor(
     val results: Results,
     val executionDuration: Duration,
     val configurationOverrides: List<ConfigurationOverride?>,
-    val slos: List<KubernetesBenchmark.Slo>,
+    val slos: List<Slo>,
     val repetitions: Int,
     val executionId: Int,
     val loadGenerationDelay: Long,
diff --git a/theodolite/src/main/kotlin/theodolite/execution/BenchmarkExecutorImpl.kt b/theodolite/src/main/kotlin/theodolite/execution/BenchmarkExecutorImpl.kt
index 415fd1976..0b7c56f1b 100644
--- a/theodolite/src/main/kotlin/theodolite/execution/BenchmarkExecutorImpl.kt
+++ b/theodolite/src/main/kotlin/theodolite/execution/BenchmarkExecutorImpl.kt
@@ -3,7 +3,7 @@ package theodolite.execution
 import io.quarkus.runtime.annotations.RegisterForReflection
 import mu.KotlinLogging
 import theodolite.benchmark.Benchmark
-import theodolite.benchmark.KubernetesBenchmark
+import theodolite.benchmark.Slo
 import theodolite.evaluation.AnalysisExecutor
 import theodolite.execution.operator.EventCreator
 import theodolite.util.*
@@ -18,7 +18,7 @@ class BenchmarkExecutorImpl(
         results: Results,
         executionDuration: Duration,
         configurationOverrides: List<ConfigurationOverride?>,
-        slos: List<KubernetesBenchmark.Slo>,
+        slos: List<Slo>,
         repetitions: Int,
         executionId: Int,
         loadGenerationDelay: Long,
diff --git a/theodolite/src/main/kotlin/theodolite/execution/SloFactory.kt b/theodolite/src/main/kotlin/theodolite/execution/SloFactory.kt
index 966e19bdc..b990828fa 100644
--- a/theodolite/src/main/kotlin/theodolite/execution/SloFactory.kt
+++ b/theodolite/src/main/kotlin/theodolite/execution/SloFactory.kt
@@ -2,22 +2,19 @@ package theodolite.execution
 
 import theodolite.benchmark.BenchmarkExecution
 import theodolite.benchmark.KubernetesBenchmark
+import theodolite.benchmark.Slo
 
 class SloFactory {
 
-    fun createSlos(execution: BenchmarkExecution, benchmark: KubernetesBenchmark): List<KubernetesBenchmark.Slo> {
+    fun createSlos(execution: BenchmarkExecution, benchmark: KubernetesBenchmark): List<Slo> {
         var benchmarkSlos = benchmark.slos
         var executionSlos = execution.slos
 
         for(executionSlo in executionSlos) {
-            for(i in 0..benchmarkSlos.size) {
-                if(executionSlo.name == benchmarkSlos[i].name) {
-                    benchmarkSlos[i].offset = executionSlo.offset ?: benchmarkSlos[i].offset
-                    benchmarkSlos[i].prometheusUrl = executionSlo.prometheusUrl ?: benchmarkSlos[i].prometheusUrl
-                    if (executionSlo.properties != null) {
-                        for (executionProperty in executionSlo.properties!!) {
-                            benchmarkSlos[i].properties[executionProperty.key] = executionProperty.value
-                        }
+            for(i in 0 until benchmarkSlos.size) {
+                if(executionSlo.name == benchmarkSlos[i].name && executionSlo.properties != null) {
+                    for (executionProperty in executionSlo.properties!!) {
+                        benchmarkSlos[i].properties[executionProperty.key] = executionProperty.value
                     }
                 }
             }
diff --git a/theodolite/src/main/kotlin/theodolite/execution/TheodoliteExecutor.kt b/theodolite/src/main/kotlin/theodolite/execution/TheodoliteExecutor.kt
index 6f6227fb7..da88d4739 100644
--- a/theodolite/src/main/kotlin/theodolite/execution/TheodoliteExecutor.kt
+++ b/theodolite/src/main/kotlin/theodolite/execution/TheodoliteExecutor.kt
@@ -16,13 +16,13 @@ private val logger = KotlinLogging.logger {}
 /**
  * The Theodolite executor runs all the experiments defined with the given execution and benchmark configuration.
  *
- * @property config Configuration of a execution
+ * @property benchmarkExecution 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
+        private val benchmarkExecution: BenchmarkExecution,
+        private val kubernetesBenchmark: KubernetesBenchmark
 ) {
     /**
      * An executor object, configured with the specified benchmark, evaluation method, experiment duration
@@ -41,56 +41,55 @@ class TheodoliteExecutor(
         val results = Results()
         val strategyFactory = StrategyFactory()
 
-        val executionDuration = Duration.ofSeconds(config.execution.duration)
+        val executionDuration = Duration.ofSeconds(benchmarkExecution.execution.duration)
 
         val resourcePatcherDefinition =
             PatcherDefinitionFactory().createPatcherDefinition(
-                config.resources.resourceType,
+                benchmarkExecution.resources.resourceType,
                 this.kubernetesBenchmark.resourceTypes
             )
 
         val loadDimensionPatcherDefinition =
             PatcherDefinitionFactory().createPatcherDefinition(
-                config.load.loadType,
+                benchmarkExecution.load.loadType,
                 this.kubernetesBenchmark.loadTypes
             )
 
-        val slos = SloFactory().createSlos(this.config, this.kubernetesBenchmark)
+        val slos = SloFactory().createSlos(this.benchmarkExecution, this.kubernetesBenchmark)
 
-        // TODO not config.slos!, maybe change naming here cause its kinda missleading due to the Config.kt file
         executor =
             BenchmarkExecutorImpl(
                 benchmark = kubernetesBenchmark,
                 results = results,
                 executionDuration = executionDuration,
-                configurationOverrides = config.configOverrides,
+                configurationOverrides = benchmarkExecution.configOverrides,
                 slos = slos,
-                repetitions = config.execution.repetitions,
-                executionId = config.executionId,
-                loadGenerationDelay = config.execution.loadGenerationDelay,
-                afterTeardownDelay = config.execution.afterTeardownDelay,
-                executionName = config.name
+                repetitions = benchmarkExecution.execution.repetitions,
+                executionId = benchmarkExecution.executionId,
+                loadGenerationDelay = benchmarkExecution.execution.loadGenerationDelay,
+                afterTeardownDelay = benchmarkExecution.execution.afterTeardownDelay,
+                executionName = benchmarkExecution.name
             )
 
-        if (config.load.loadValues != config.load.loadValues.sorted()) {
-            config.load.loadValues = config.load.loadValues.sorted()
+        if (benchmarkExecution.load.loadValues != benchmarkExecution.load.loadValues.sorted()) {
+            benchmarkExecution.load.loadValues = benchmarkExecution.load.loadValues.sorted()
             logger.info {
                 "Load values are not sorted correctly, Theodolite sorts them in ascending order." +
-                        "New order is: ${config.load.loadValues}"
+                        "New order is: ${benchmarkExecution.load.loadValues}"
             }
         }
 
-        if (config.resources.resourceValues != config.resources.resourceValues.sorted()) {
-            config.resources.resourceValues = config.resources.resourceValues.sorted()
+        if (benchmarkExecution.resources.resourceValues != benchmarkExecution.resources.resourceValues.sorted()) {
+            benchmarkExecution.resources.resourceValues = benchmarkExecution.resources.resourceValues.sorted()
             logger.info {
                 "Load values are not sorted correctly, Theodolite sorts them in ascending order." +
-                        "New order is: ${config.resources.resourceValues}"
+                        "New order is: ${benchmarkExecution.resources.resourceValues}"
             }
         }
 
         return Config(
-            loads = config.load.loadValues.map { load -> LoadDimension(load, loadDimensionPatcherDefinition) },
-            resources = config.resources.resourceValues.map { resource ->
+            loads = benchmarkExecution.load.loadValues.map { load -> LoadDimension(load, loadDimensionPatcherDefinition) },
+            resources = benchmarkExecution.resources.resourceValues.map { resource ->
                 Resource(
                     resource,
                     resourcePatcherDefinition
@@ -98,17 +97,17 @@ class TheodoliteExecutor(
             },
             compositeStrategy = CompositeStrategy(
                 benchmarkExecutor = executor,
-                searchStrategy = strategyFactory.createSearchStrategy(executor, config.execution.strategy),
+                searchStrategy = strategyFactory.createSearchStrategy(executor, benchmarkExecution.execution.strategy),
                 restrictionStrategies = strategyFactory.createRestrictionStrategy(
                     results,
-                    config.execution.restrictions
+                    benchmarkExecution.execution.restrictions
                 )
             )
         )
     }
 
     fun getExecution(): BenchmarkExecution {
-        return this.config
+        return this.benchmarkExecution
     }
 
     /**
@@ -120,11 +119,11 @@ class TheodoliteExecutor(
 
         val ioHandler = IOHandler()
         val resultsFolder = ioHandler.getResultFolderURL()
-        this.config.executionId = getAndIncrementExecutionID(resultsFolder + "expID.txt")
-        ioHandler.writeToJSONFile(this.config, "${resultsFolder}exp${this.config.executionId}-execution-configuration")
+        this.benchmarkExecution.executionId = getAndIncrementExecutionID(resultsFolder + "expID.txt")
+        ioHandler.writeToJSONFile(this.benchmarkExecution, "${resultsFolder}exp${this.benchmarkExecution.executionId}-execution-configuration")
         ioHandler.writeToJSONFile(
             kubernetesBenchmark,
-            "${resultsFolder}exp${this.config.executionId}-benchmark-configuration"
+            "${resultsFolder}exp${this.benchmarkExecution.executionId}-benchmark-configuration"
         )
 
         val config = buildConfig()
@@ -138,11 +137,11 @@ class TheodoliteExecutor(
         } finally {
             ioHandler.writeToJSONFile(
                 config.compositeStrategy.benchmarkExecutor.results,
-                "${resultsFolder}exp${this.config.executionId}-result"
+                "${resultsFolder}exp${this.benchmarkExecution.executionId}-result"
             )
             // Create expXYZ_demand.csv file
             ioHandler.writeToCSVFile(
-                "${resultsFolder}exp${this.config.executionId}_demand",
+                "${resultsFolder}exp${this.benchmarkExecution.executionId}_demand",
                 calculateDemandMetric(config.loads, config.compositeStrategy.benchmarkExecutor.results),
                 listOf("load","resources")
             )
diff --git a/theodolite/src/test/kotlin/theodolite/CompositeStrategyTest.kt b/theodolite/src/test/kotlin/theodolite/CompositeStrategyTest.kt
index 1d8823e28..1d2618945 100644
--- a/theodolite/src/test/kotlin/theodolite/CompositeStrategyTest.kt
+++ b/theodolite/src/test/kotlin/theodolite/CompositeStrategyTest.kt
@@ -3,7 +3,7 @@ package theodolite
 import io.quarkus.test.junit.QuarkusTest
 import org.junit.jupiter.api.Assertions.assertEquals
 import org.junit.jupiter.api.Test
-import theodolite.benchmark.KubernetesBenchmark
+import theodolite.benchmark.Slo
 import theodolite.strategies.restriction.LowerBoundRestriction
 import theodolite.strategies.searchstrategy.BinarySearch
 import theodolite.strategies.searchstrategy.CompositeStrategy
@@ -30,7 +30,7 @@ class CompositeStrategyTest {
         val mockResources: List<Resource> = (0..6).map { number -> Resource(number, emptyList()) }
         val results = Results()
         val benchmark = TestBenchmark()
-        val sloChecker: KubernetesBenchmark.Slo = KubernetesBenchmark.Slo()
+        val sloChecker: Slo = Slo()
         val benchmarkExecutor = TestBenchmarkExecutorImpl(mockResults, benchmark, results, listOf(sloChecker), 0, 0, 5)
         val linearSearch = LinearSearch(benchmarkExecutor)
         val lowerBoundRestriction = LowerBoundRestriction(results)
@@ -63,7 +63,7 @@ class CompositeStrategyTest {
         val mockResources: List<Resource> = (0..6).map { number -> Resource(number, emptyList()) }
         val results = Results()
         val benchmark = TestBenchmark()
-        val sloChecker: KubernetesBenchmark.Slo = KubernetesBenchmark.Slo()
+        val sloChecker: Slo = Slo()
         val benchmarkExecutorImpl =
             TestBenchmarkExecutorImpl(mockResults, benchmark, results, listOf(sloChecker), 0, 0, 0)
         val binarySearch = BinarySearch(benchmarkExecutorImpl)
@@ -97,7 +97,7 @@ class CompositeStrategyTest {
         val mockResources: List<Resource> = (0..7).map { number -> Resource(number, emptyList()) }
         val results = Results()
         val benchmark = TestBenchmark()
-        val sloChecker: KubernetesBenchmark.Slo = KubernetesBenchmark.Slo()
+        val sloChecker: Slo = Slo()
         val benchmarkExecutor = TestBenchmarkExecutorImpl(mockResults, benchmark, results, listOf(sloChecker), 0, 0, 0)
         val binarySearch = BinarySearch(benchmarkExecutor)
         val lowerBoundRestriction = LowerBoundRestriction(results)
diff --git a/theodolite/src/test/kotlin/theodolite/InitialGuessSearchStrategyTest.kt b/theodolite/src/test/kotlin/theodolite/InitialGuessSearchStrategyTest.kt
index 81f210762..3f416db39 100644
--- a/theodolite/src/test/kotlin/theodolite/InitialGuessSearchStrategyTest.kt
+++ b/theodolite/src/test/kotlin/theodolite/InitialGuessSearchStrategyTest.kt
@@ -8,7 +8,7 @@ import theodolite.util.LoadDimension
 import theodolite.util.Resource
 import theodolite.util.Results
 import mu.KotlinLogging
-import theodolite.benchmark.KubernetesBenchmark
+import theodolite.benchmark.Slo
 import theodolite.strategies.searchstrategy.PrevResourceMinGuess
 
 private val logger = KotlinLogging.logger {}
@@ -32,7 +32,7 @@ class InitialGuessSearchStrategyTest {
         val results = Results()
         val benchmark = TestBenchmark()
         val guessStrategy = PrevResourceMinGuess()
-        val sloChecker: KubernetesBenchmark.Slo = KubernetesBenchmark.Slo()
+        val sloChecker: Slo = Slo()
         val benchmarkExecutor = TestBenchmarkExecutorImpl(mockResults, benchmark, results, listOf(sloChecker), 0, 0, 5)
         val strategy = InitialGuessSearchStrategy(benchmarkExecutor,guessStrategy, results)
 
@@ -70,7 +70,7 @@ class InitialGuessSearchStrategyTest {
         val results = Results()
         val benchmark = TestBenchmark()
         val guessStrategy = PrevResourceMinGuess()
-        val sloChecker: KubernetesBenchmark.Slo = KubernetesBenchmark.Slo()
+        val sloChecker: Slo = Slo()
         val benchmarkExecutor = TestBenchmarkExecutorImpl(mockResults, benchmark, results, listOf(sloChecker), 0, 0, 5)
         val strategy = InitialGuessSearchStrategy(benchmarkExecutor,guessStrategy, results)
 
@@ -108,7 +108,7 @@ class InitialGuessSearchStrategyTest {
         val results = Results()
         val benchmark = TestBenchmark()
         val guessStrategy = PrevResourceMinGuess()
-        val sloChecker: KubernetesBenchmark.Slo = KubernetesBenchmark.Slo()
+        val sloChecker: Slo = Slo()
         val benchmarkExecutor = TestBenchmarkExecutorImpl(mockResults, benchmark, results, listOf(sloChecker), 0, 0, 5)
         val strategy = InitialGuessSearchStrategy(benchmarkExecutor, guessStrategy, results)
 
diff --git a/theodolite/src/test/kotlin/theodolite/TestBenchmarkExecutorImpl.kt b/theodolite/src/test/kotlin/theodolite/TestBenchmarkExecutorImpl.kt
index 72fdc4a11..a1d9564c2 100644
--- a/theodolite/src/test/kotlin/theodolite/TestBenchmarkExecutorImpl.kt
+++ b/theodolite/src/test/kotlin/theodolite/TestBenchmarkExecutorImpl.kt
@@ -1,7 +1,7 @@
 package theodolite
 
 import theodolite.benchmark.Benchmark
-import theodolite.benchmark.KubernetesBenchmark
+import theodolite.benchmark.Slo
 import theodolite.execution.BenchmarkExecutor
 import theodolite.util.LoadDimension
 import theodolite.util.Resource
@@ -12,7 +12,7 @@ class TestBenchmarkExecutorImpl(
         private val mockResults: Array<Array<Boolean>>,
         benchmark: Benchmark,
         results: Results,
-        slo: List<KubernetesBenchmark.Slo>,
+        slo: List<Slo>,
         executionId: Int,
         loadGenerationDelay: Long,
         afterTeardownDelay: Long
diff --git a/theodolite/src/test/kotlin/theodolite/execution/operator/SloFactoryTest.kt b/theodolite/src/test/kotlin/theodolite/execution/operator/SloFactoryTest.kt
new file mode 100644
index 000000000..c3dcd1b95
--- /dev/null
+++ b/theodolite/src/test/kotlin/theodolite/execution/operator/SloFactoryTest.kt
@@ -0,0 +1,72 @@
+package theodolite.execution.operator
+
+import io.quarkus.test.junit.QuarkusTest
+import org.junit.jupiter.api.Test
+import theodolite.benchmark.BenchmarkExecution
+import theodolite.benchmark.BenchmarkExecution.SloConfiguration
+import theodolite.benchmark.KubernetesBenchmark
+import theodolite.benchmark.Slo
+import theodolite.execution.SloFactory
+import org.junit.jupiter.api.Assertions.*
+
+@QuarkusTest
+internal class SloFactoryTest {
+
+    @Test
+    fun overwriteSloTest() {
+
+        val benchmark = KubernetesBenchmark()
+        val execution = BenchmarkExecution()
+
+        // Define Benchmark SLOs
+        val slo = Slo()
+        slo.name="test"
+        slo.sloType="lag trend"
+        slo.prometheusUrl="test.de"
+        slo.offset=0
+
+        val benchmarkSloProperties = mutableMapOf<String, String>()
+        benchmarkSloProperties["threshold"] = "2000"
+        benchmarkSloProperties["externalSloUrl"] = "http://localhost:80/evaluate-slope"
+        benchmarkSloProperties["warmup"] = "60"
+
+        slo.properties=benchmarkSloProperties
+
+        benchmark.slos = mutableListOf(slo)
+
+
+        // Define Execution SLOs, benchmark SLO values for these properties should be overwritten
+        val sloConfig = SloConfiguration()
+        sloConfig.name = "test"
+
+        val executionSloProperties = mutableMapOf<String, String>()
+        // overwriting properties 'threshold' and 'warmup' and adding property 'extensionTest'
+        executionSloProperties["threshold"] = "3000"
+        executionSloProperties["warmup"] = "80"
+        executionSloProperties["extensionTest"] = "extended"
+
+        sloConfig.properties = executionSloProperties
+
+        // SLO has 'name' that isn't defined in the benchmark, therefore it will be ignored by the SloFactory
+        val sloConfig2 = SloConfiguration()
+        sloConfig2.name = "test2"
+        sloConfig2.properties = executionSloProperties
+
+        execution.slos = listOf(sloConfig, sloConfig2)
+
+        val sloFactory = SloFactory()
+        val combinedSlos = sloFactory.createSlos(execution,benchmark)
+
+        assertEquals(1, combinedSlos.size)
+        assertEquals("test", combinedSlos[0].name)
+        assertEquals("lag trend", combinedSlos[0].sloType)
+        assertEquals("test.de", combinedSlos[0].prometheusUrl)
+        assertEquals(0, combinedSlos[0].offset)
+
+        assertEquals(4, combinedSlos[0].properties.size)
+        assertEquals("3000", combinedSlos[0].properties["threshold"])
+        assertEquals("http://localhost:80/evaluate-slope", combinedSlos[0].properties["externalSloUrl"])
+        assertEquals("80", combinedSlos[0].properties["warmup"])
+        assertEquals("extended", combinedSlos[0].properties["extensionTest"])
+    }
+}
\ No newline at end of file
-- 
GitLab