diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index b7d75d5470d8073a2021e06428e9170f605ec7b3..4a412569fbd477f02a0b67b83e8814ab98b34031 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -51,7 +51,7 @@ build-benchmarks:
       - "theodolite-benchmarks/build/libs/*.jar"
       - "theodolite-benchmarks/*/build/libs/*.jar"
       - "theodolite-benchmarks/*/build/distributions/*.tar"
-    expire_in: 1 day
+    expire_in: 6 hours
 
 test-benchmarks:
   stage: test
@@ -234,7 +234,7 @@ build-theodolite-jvm:
     paths:
       - "theodolite-quarkus/build/lib/*"
       - "theodolite-quarkus/build/*-runner.jar"
-    expire_in: 1 day
+    expire_in: 6 hours
 
 build-theodolite-native:
   stage: build
@@ -245,7 +245,7 @@ build-theodolite-native:
   artifacts:
     paths:
       - "theodolite-quarkus/build/*-runner"
-    expire_in: 1 day
+    expire_in: 6 hours
 
 test-theodolite:
   stage: test
diff --git a/execution/helm/README.md b/execution/helm/README.md
index 6a2f083559fd0fe7b5674834e99a075e9bea1851..c545804aaec8eb8ed91054f1f7ee97dd293816a4 100644
--- a/execution/helm/README.md
+++ b/execution/helm/README.md
@@ -6,11 +6,9 @@ Install the chart via:
 
 ```sh
 helm dependencies update .
-helm install my-confluent .
+helm install theodolite .
 ```
 
-**Please note: Theodolite currently uses hard-coded URLs, to connect to Kafka and Zookeeper. For that reason, the name of this chart must be `my-confluent`.** We will change this behavior soon.
-
 This chart installs requirements to execute benchmarks with Theodolite.
 
 Dependencies and subcharts:
@@ -27,7 +25,7 @@ Dependencies and subcharts:
 Test the installation:
 
 ```sh
-helm test <release-name>
+helm test theodolite
 ```
 
 Our test files are located [here](templates/../../theodolite-chart/templates/tests). Many subcharts have their own tests, these are also executed and are placed in the respective /templates folders. 
@@ -46,18 +44,18 @@ helm install theodolite . -f preconfigs/one-broker-values.yaml
 
 ## Uninstall this Chart
 
-To uninstall/delete the `my-release` deployment:
+To uninstall/delete the `theodolite` deployment:
 
 ```sh
-helm delete my-release
+helm delete theodolite
 ```
 
 This command does not remove the CRDs which are created by this chart. Remove them manually with:
 
 ```sh
 # CRDs from Theodolite
-kubectl delete crd execution
-kubectl delete crd benchmark
+kubectl delete crd executions.theodolite.com
+kubectl delete crd benchmarks.theodolite.com
 # CRDs from Prometheus operator (see https://github.com/prometheus-community/helm-charts/tree/main/charts/kube-prometheus-stack#uninstall-chart)
 kubectl delete crd alertmanagerconfigs.monitoring.coreos.com
 kubectl delete crd alertmanagers.monitoring.coreos.com
diff --git a/execution/helm/templates/theodolite/role.yaml b/execution/helm/templates/theodolite/role.yaml
index 73e3e5098448c88e6b030f1dc2056caf999da3ce..ee699a5b861c64e355ca2ba44330a7d600756b77 100644
--- a/execution/helm/templates/theodolite/role.yaml
+++ b/execution/helm/templates/theodolite/role.yaml
@@ -42,7 +42,7 @@ rules:
     - delete
     - list
     - create
-  {{- if .Values.operator.enabled -}}
+  {{- if .Values.operator.enabled }}
   - apiGroups:
     - theodolite.com
     resources: 
diff --git a/execution/helm/templates/theodolite/thedolite-operator.yaml b/execution/helm/templates/theodolite/thedolite-operator.yaml
index cb3f193b89c3a198bab853fb85a0db1184394dce..3af9cfffd71059993688d5be77ae110a2e3cdc2d 100644
--- a/execution/helm/templates/theodolite/thedolite-operator.yaml
+++ b/execution/helm/templates/theodolite/thedolite-operator.yaml
@@ -23,9 +23,19 @@ spec:
               value: {{ .Release.Namespace }}
             - name: MODE
               value: operator
+            - name: THEODOLITE_APP_RESOURCES
+              value: "./benchmark-resources"
+          volumeMounts:
+            - name: benchmark-resources
+              mountPath: /work/benchmark-resources
         - name: lag-analysis
           image: ghcr.io/cau-se/theodolite-slo-checker-lag-trend:theodolite-kotlin-latest
           ports:
           - containerPort: 80
-            name: analysis 
+            name: analysis
+      volumes:
+      - name: benchmark-resources
+        configMap:
+          name: benchmark-resources
+          optional: true
 {{- end }}
diff --git a/execution/helm/values.yaml b/execution/helm/values.yaml
index 5e48ad46b01689c974189f471b0016e45d71f958..67dab74c3931ce13a1ab0f7504a946a208b4dfb8 100644
--- a/execution/helm/values.yaml
+++ b/execution/helm/values.yaml
@@ -24,6 +24,9 @@ grafana:
   # Administrator credentials when not using an existing secret (see below)
   adminUser: admin
   adminPassword: admin
+  grafana.ini:
+    users:
+      default_theme: light
   ## Sidecars that collect the configmaps with specified label and stores the included files them into the respective folders
   ## Requires at least Grafana 5 to work and can't be used together with parameters dashboardProviders, datasources and dashboards
   sidecar:
@@ -145,8 +148,8 @@ kafka-lag-exporter:
   enabled: true
   nodeSelector: {}
   clusters:
-    - name: "my-confluent-cp-kafka"
-      bootstrapBrokers: "my-confluent-cp-kafka:9092"
+    - name: "theodolite-cp-kafka"
+      bootstrapBrokers: "theodolite-cp-kafka:9092"
 
   ## The interval between refreshing metrics
   pollIntervalSeconds: 15
diff --git a/execution/uc-application/aggregation-deployment.yaml b/execution/uc-application/aggregation-deployment.yaml
index 07732ca1dd1e6b2b06f098dfb10a53d38e8d5cae..1d3ebdb20dd06433e97e112edef76d7deac17395 100644
--- a/execution/uc-application/aggregation-deployment.yaml
+++ b/execution/uc-application/aggregation-deployment.yaml
@@ -21,9 +21,9 @@ spec:
           name: jmx
         env:
         - name: KAFKA_BOOTSTRAP_SERVERS
-          value: "my-confluent-cp-kafka:9092"
+          value: "theodolite-cp-kafka:9092"
         - name: SCHEMA_REGISTRY_URL
-          value: "http://my-confluent-cp-schema-registry:8081"
+          value: "http://theodolite-cp-schema-registry:8081"
         - name: JAVA_OPTS
           value: "-Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.port=5555"
         - name: COMMIT_INTERVAL_MS # Set as default for the applications
diff --git a/execution/uc-workload-generator/workloadGenerator.yaml b/execution/uc-workload-generator/workloadGenerator.yaml
index 146e285f66d4c0e1a88d613e4ac2d5571234fad6..8f7cc3a8df20752eccb321242bb774c18f4e1d0a 100644
--- a/execution/uc-workload-generator/workloadGenerator.yaml
+++ b/execution/uc-workload-generator/workloadGenerator.yaml
@@ -32,6 +32,6 @@ spec:
         - name: KUBERNETES_DNS_NAME
           value: "titan-ccp-load-generator.$(KUBERNETES_NAMESPACE).svc.cluster.local"
         - name: KAFKA_BOOTSTRAP_SERVERS
-          value: "my-confluent-cp-kafka:9092"
+          value: "theodolite-cp-kafka:9092"
         - name: SCHEMA_REGISTRY_URL
-          value: "http://my-confluent-cp-schema-registry:8081"
+          value: "http://theodolite-cp-schema-registry:8081"
diff --git a/theodolite-benchmarks/buildSrc/src/main/groovy/theodolite.flink.gradle b/theodolite-benchmarks/buildSrc/src/main/groovy/theodolite.flink.gradle
index f9c1d98d9f88a95bdc3fa25e7c1bec2f3c9bddb4..d6d5217667a73a1529d73ac59260bcf47d8cf2e1 100644
--- a/theodolite-benchmarks/buildSrc/src/main/groovy/theodolite.flink.gradle
+++ b/theodolite-benchmarks/buildSrc/src/main/groovy/theodolite.flink.gradle
@@ -6,6 +6,23 @@ plugins {
 
 applicationDefaultJvmArgs = ["-Dlog4j.configuration=log4j.properties"]
 
+
+run.classpath = sourceSets.main.runtimeClasspath
+
+jar {
+    manifest {
+        attributes 'Built-By': System.getProperty('user.name'),
+                   'Build-Jdk': System.getProperty('java.version')
+    }
+}
+
+shadowJar {
+    configurations = [project.configurations.compile]
+    zip64 true
+}
+
+tasks.distZip.enabled = false
+
 ext {
   flinkVersion = '1.12.2'
   scalaBinaryVersion = '2.12'
@@ -48,17 +65,3 @@ dependencies {
     // Use JUnit test framework
     testImplementation 'junit:junit:4.12'
 }
-
-run.classpath = sourceSets.main.runtimeClasspath
-
-jar {
-    manifest {
-        attributes 'Built-By': System.getProperty('user.name'),
-                   'Build-Jdk': System.getProperty('java.version')
-    }
-}
-
-shadowJar {
-    configurations = [project.configurations.compile]
-    zip64 true
-}
diff --git a/theodolite-benchmarks/buildSrc/src/main/groovy/theodolite.kstreams.gradle b/theodolite-benchmarks/buildSrc/src/main/groovy/theodolite.kstreams.gradle
index c6779fbc4348a8d665776e68688858ab3d2f4146..eece7b835ae9d6f39283ea371ce8b0b8194cdaa0 100644
--- a/theodolite-benchmarks/buildSrc/src/main/groovy/theodolite.kstreams.gradle
+++ b/theodolite-benchmarks/buildSrc/src/main/groovy/theodolite.kstreams.gradle
@@ -6,6 +6,8 @@ plugins {
   id 'application'
 }
 
+tasks.distZip.enabled = false
+
 repositories {
   jcenter()
   maven {
diff --git a/theodolite-benchmarks/buildSrc/src/main/groovy/theodolite.load-generator.gradle b/theodolite-benchmarks/buildSrc/src/main/groovy/theodolite.load-generator.gradle
index 13b7ea191d11c942cd0ca58b882ffda7bc7912be..c6c2b6057cf35c32faa4d67b6ea6dba9e5c13beb 100644
--- a/theodolite-benchmarks/buildSrc/src/main/groovy/theodolite.load-generator.gradle
+++ b/theodolite-benchmarks/buildSrc/src/main/groovy/theodolite.load-generator.gradle
@@ -6,6 +6,8 @@ plugins {
   id 'application'
 }
 
+tasks.distZip.enabled = false
+
 repositories {
   jcenter()
   maven {
diff --git a/theodolite-quarkus/config/aggregation-service.yaml b/theodolite-quarkus/config/aggregation-service.yaml
index 916dd6677d60370b1d62e5d7e708c3ee966bda23..85432d04f225c30469f3232153ef6bd72bd02bdf 100644
--- a/theodolite-quarkus/config/aggregation-service.yaml
+++ b/theodolite-quarkus/config/aggregation-service.yaml
@@ -14,4 +14,4 @@ spec:
     targetPort: 80
     protocol: TCP
   - name: metrics
-    port: 9980
+    port: 5556
diff --git a/theodolite-quarkus/config/example-benchmark-yaml-resource.yaml b/theodolite-quarkus/config/example-benchmark-yaml-resource.yaml
index 42b1cd08dc1949355c97edebc92ea7b5ab8799b2..60eb3bb9c31e3eab3e70f916b450372d56db4968 100644
--- a/theodolite-quarkus/config/example-benchmark-yaml-resource.yaml
+++ b/theodolite-quarkus/config/example-benchmark-yaml-resource.yaml
@@ -20,7 +20,7 @@ loadTypes:
         container: "workload-generator"
         variableName: "NUM_SENSORS"
 kafkaConfig:
-  bootstrapServer: "my-confluent-cp-kafka:9092"
+  bootstrapServer: "theodolite-cp-kafka:9092"
   topics:
     - name: "input"
       numPartitions: 40
diff --git a/theodolite-quarkus/config/example-execution-yaml-resource.yaml b/theodolite-quarkus/config/example-execution-yaml-resource.yaml
index 80e4575d653bff8d67d8f66cf4ae9806a1fe102e..23c1587ec1e5c2a88fcf69d7127edbcc1ffdb00f 100644
--- a/theodolite-quarkus/config/example-execution-yaml-resource.yaml
+++ b/theodolite-quarkus/config/example-execution-yaml-resource.yaml
@@ -2,36 +2,34 @@ name: example-execution
 benchmark: "uc1-kstreams"
 load:
   loadType: "NumSensors"
-  loadValues:
-    - 50000
+  loadValues: [25000, 50000, 75000, 100000, 125000, 150000]
 resources:
   resourceType: "Instances"
-  resourceValues:
-    - 1
+  resourceValues: [1, 2, 3, 4, 5]
 slos:
   - sloType: "lag trend"
-    threshold: 1000
+    threshold: 2000
     prometheusUrl: "http://prometheus-operated:9090"
     externalSloUrl: "http://localhost:80/evaluate-slope"
     offset: 0
-    warmup: 0
+    warmup: 60 # in seconds
 execution:
   strategy: "LinearSearch"
-  duration: 60
+  duration: 300 # in seconds
   repetitions: 1
   restrictions:
     - "LowerBound"
-configOverrides:
-  - patcher:
-      type: "NodeSelectorPatcher"
-      resource: "uc1-load-generator-deployment.yaml"
-      variableName: "env"
-    value: "prod"
-  - patcher:
-      type: "NodeSelectorPatcher"
-      resource: "uc1-kstreams-deployment.yaml"
-      variableName: "env"
-    value: "prod"
+configOverrides: []
+#  - patcher:
+#      type: "NodeSelectorPatcher"
+#      resource: "uc1-load-generator-deployment.yaml"
+#      variableName: "env"
+#    value: "prod"
+#  - patcher:
+#      type: "NodeSelectorPatcher"
+#      resource: "uc1-kstreams-deployment.yaml"
+#      variableName: "env"
+#    value: "prod"
 #  - patcher:
 #      type: "ResourceLimitPatcher"
 #      resource: "uc1-kstreams-deployment.yaml"
diff --git a/theodolite-quarkus/config/example-operator-benchmark.yaml b/theodolite-quarkus/config/example-operator-benchmark.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..419042fdd0b1e58fed4d402b4bb329d54602d23f
--- /dev/null
+++ b/theodolite-quarkus/config/example-operator-benchmark.yaml
@@ -0,0 +1,31 @@
+apiVersion: theodolite.com/v1alpha1
+kind: benchmark
+metadata:
+  name: uc1-kstreams
+#name: "uc1-kstreams"
+appResource:
+  - "uc1-kstreams-deployment.yaml"
+  - "aggregation-service.yaml"
+  - "jmx-configmap.yaml"
+  - "uc1-service-monitor.yaml"
+loadGenResource:
+  - "uc1-load-generator-deployment.yaml"
+  - "uc1-load-generator-service.yaml"
+resourceTypes:
+  - typeName: "Instances"
+    patchers:
+      - type: "ReplicaPatcher"
+        resource: "uc1-kstreams-deployment.yaml"
+loadTypes:
+  - typeName: "NumSensors"
+    patchers:
+      - type: "EnvVarPatcher"
+        resource: "uc1-load-generator-deployment.yaml"
+        container: "workload-generator"
+        variableName: "NUM_SENSORS"
+kafkaConfig:
+  bootstrapServer: "theodolite-cp-kafka:9092"
+  topics:
+    - name: "input"
+      numPartitions: 40
+      replicationFactor: 1
\ No newline at end of file
diff --git a/theodolite-quarkus/config/example-operator-execution.yaml b/theodolite-quarkus/config/example-operator-execution.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..3df1a723dd771453ab1b267335176e4ae74c3ed5
--- /dev/null
+++ b/theodolite-quarkus/config/example-operator-execution.yaml
@@ -0,0 +1,52 @@
+apiVersion: theodolite.com/v1alpha1
+kind: execution
+metadata:
+  name: example-execution
+#name: example-execution
+benchmark: "uc1-kstreams"
+load:
+  loadType: "NumSensors"
+  loadValues: [25000, 50000, 75000, 100000, 125000, 150000]
+resources:
+  resourceType: "Instances"
+  resourceValues: [1, 2, 3, 4, 5]
+slos:
+  - sloType: "lag trend"
+    threshold: 2000
+    prometheusUrl: "http://prometheus-operated:9090"
+    externalSloUrl: "http://localhost:80/evaluate-slope"
+    offset: 0
+    warmup: 60 # in seconds
+execution:
+  strategy: "LinearSearch"
+  duration: 300 # in seconds
+  repetitions: 1
+  restrictions:
+    - "LowerBound"
+configOverrides: []
+#  - patcher:
+#      type: "NodeSelectorPatcher"
+#      resource: "uc1-load-generator-deployment.yaml"
+#      variableName: "env"
+#    value: "prod"
+#  - patcher:
+#      type: "NodeSelectorPatcher"
+#      resource: "uc1-kstreams-deployment.yaml"
+#      variableName: "env"
+#    value: "prod"
+#  - patcher:
+#      type: "ResourceLimitPatcher"
+#      resource: "uc1-kstreams-deployment.yaml"
+#      container: "uc-application"
+#      variableName: "cpu"
+#    value: "1000m"
+#  - patcher:
+#      type: "ResourceLimitPatcher"
+#      resource: "uc1-kstreams-deployment.yaml"
+#      container: "uc-application"
+#      variableName: "memory"
+#    value: "2Gi"
+#  - patcher:
+#      type: "SchedulerNamePatcher"
+#      resource: "uc1-kstreams-deployment.yaml"
+#    value: "random-scheduler"
diff --git a/theodolite-quarkus/config/uc1-kstreams-deployment.yaml b/theodolite-quarkus/config/uc1-kstreams-deployment.yaml
index 85fe243714f9523e760ad827ef697b3dcefdc8e3..171c3446db2719ee91bd8954233015316851fcf9 100644
--- a/theodolite-quarkus/config/uc1-kstreams-deployment.yaml
+++ b/theodolite-quarkus/config/uc1-kstreams-deployment.yaml
@@ -21,9 +21,9 @@ spec:
               name: jmx
           env:
             - name: KAFKA_BOOTSTRAP_SERVERS
-              value: "my-confluent-cp-kafka:9092"
+              value: "theodolite-cp-kafka:9092"
             - name: SCHEMA_REGISTRY_URL
-              value: "http://my-confluent-cp-schema-registry:8081"
+              value: "http://theodolite-cp-schema-registry:8081"
             - name: JAVA_OPTS
               value: "-Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.port=5555"
             - name: COMMIT_INTERVAL_MS # Set as default for the applications
diff --git a/theodolite-quarkus/config/uc1-load-generator-deployment.yaml b/theodolite-quarkus/config/uc1-load-generator-deployment.yaml
index e49c22d6a478fbd7f3f5987222c76ca73a661cb0..374dd60113e133ef0a793149e3786efb38973287 100644
--- a/theodolite-quarkus/config/uc1-load-generator-deployment.yaml
+++ b/theodolite-quarkus/config/uc1-load-generator-deployment.yaml
@@ -31,6 +31,6 @@ spec:
             - name: KUBERNETES_DNS_NAME
               value: "titan-ccp-load-generator.$(KUBERNETES_NAMESPACE).svc.cluster.local"
             - name: KAFKA_BOOTSTRAP_SERVERS
-              value: "my-confluent-cp-kafka:9092"
+              value: "theodolite-cp-kafka:9092"
             - name: SCHEMA_REGISTRY_URL
-              value: "http://my-confluent-cp-schema-registry:8081"
+              value: "http://theodolite-cp-schema-registry:8081"
diff --git a/theodolite-quarkus/src/main/kotlin/theodolite/benchmark/BenchmarkExecution.kt b/theodolite-quarkus/src/main/kotlin/theodolite/benchmark/BenchmarkExecution.kt
index 5e949e01a4f1c992ace0cdb77793b199ae8f6a5f..2d5d15b3389cf723be3a8ceb0fff8b27bd700419 100644
--- a/theodolite-quarkus/src/main/kotlin/theodolite/benchmark/BenchmarkExecution.kt
+++ b/theodolite-quarkus/src/main/kotlin/theodolite/benchmark/BenchmarkExecution.kt
@@ -27,6 +27,7 @@ import kotlin.properties.Delegates
 @JsonDeserialize
 @RegisterForReflection
 class BenchmarkExecution : CustomResource(), Namespaced {
+    var executionId: Int = 0
     lateinit var name: String
     lateinit var benchmark: String
     lateinit var load: LoadDefinition
diff --git a/theodolite-quarkus/src/main/kotlin/theodolite/benchmark/KubernetesBenchmark.kt b/theodolite-quarkus/src/main/kotlin/theodolite/benchmark/KubernetesBenchmark.kt
index 25ab9aba92a4b77bc444209a0046561e25bc62fc..71b65a28fd074e4554c283ee94a8db028d652d46 100644
--- a/theodolite-quarkus/src/main/kotlin/theodolite/benchmark/KubernetesBenchmark.kt
+++ b/theodolite-quarkus/src/main/kotlin/theodolite/benchmark/KubernetesBenchmark.kt
@@ -48,7 +48,6 @@ class KubernetesBenchmark : Benchmark, CustomResource(), Namespaced {
      */
     private fun loadKubernetesResources(resources: List<String>): List<Pair<String, KubernetesResource>> {
         val parser = YamlParser()
-
         val loader = K8sResourceLoader(DefaultKubernetesClient().inNamespace(namespace))
         return resources
             .map { resource ->
diff --git a/theodolite-quarkus/src/main/kotlin/theodolite/evaluation/AnalysisExecutor.kt b/theodolite-quarkus/src/main/kotlin/theodolite/evaluation/AnalysisExecutor.kt
index 71fe00be0c8b68ac4dc69a1fc0e00dc303245824..e76f6cf34fdec447fa4d581b94bbb51b972d888a 100644
--- a/theodolite-quarkus/src/main/kotlin/theodolite/evaluation/AnalysisExecutor.kt
+++ b/theodolite-quarkus/src/main/kotlin/theodolite/evaluation/AnalysisExecutor.kt
@@ -13,7 +13,11 @@ 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) {
+class AnalysisExecutor(
+    private val slo: BenchmarkExecution.Slo,
+    private val executionId: Int
+) {
+
     private val fetcher = MetricFetcher(
         prometheusURL = slo.prometheusUrl,
         offset = Duration.ofHours(slo.offset.toLong())
@@ -37,8 +41,7 @@ class AnalysisExecutor(private val slo: BenchmarkExecution.Slo) {
                 query = "sum by(group)(kafka_consumergroup_group_lag >= 0)"
             )
 
-            CsvExporter().toCsv(name = "${load.get()}_${res.get()}_${slo.sloType}", prom = prometheusData)
-
+            CsvExporter().toCsv(name = "$executionId-${load.get()}-${res.get()}-${slo.sloType}", prom = prometheusData)
             val sloChecker = SloCheckerFactory().create(
                 sloType = slo.sloType,
                 externalSlopeURL = slo.externalSloUrl,
diff --git a/theodolite-quarkus/src/main/kotlin/theodolite/execution/BenchmarkExecutor.kt b/theodolite-quarkus/src/main/kotlin/theodolite/execution/BenchmarkExecutor.kt
index 54dafd826ea3b7ba6f56e82fd194ffda5e4c5ddc..e179f7fa9492fc4fbe069330046dfd5d83ff8374 100644
--- a/theodolite-quarkus/src/main/kotlin/theodolite/execution/BenchmarkExecutor.kt
+++ b/theodolite-quarkus/src/main/kotlin/theodolite/execution/BenchmarkExecutor.kt
@@ -25,7 +25,8 @@ abstract class BenchmarkExecutor(
     val results: Results,
     val executionDuration: Duration,
     configurationOverrides: List<ConfigurationOverride?>,
-    val slo: BenchmarkExecution.Slo
+    val slo: BenchmarkExecution.Slo,
+    val executionId: Int
 ) {
 
     var run: AtomicBoolean = AtomicBoolean(true)
diff --git a/theodolite-quarkus/src/main/kotlin/theodolite/execution/BenchmarkExecutorImpl.kt b/theodolite-quarkus/src/main/kotlin/theodolite/execution/BenchmarkExecutorImpl.kt
index 456d6b4cb1671b30575d3752b027d1f066bb8954..efbfe4df41d70b1b35ea91667c8e0c85d8b58953 100644
--- a/theodolite-quarkus/src/main/kotlin/theodolite/execution/BenchmarkExecutorImpl.kt
+++ b/theodolite-quarkus/src/main/kotlin/theodolite/execution/BenchmarkExecutorImpl.kt
@@ -19,8 +19,9 @@ class BenchmarkExecutorImpl(
     results: Results,
     executionDuration: Duration,
     private val configurationOverrides: List<ConfigurationOverride?>,
-    slo: BenchmarkExecution.Slo
-) : BenchmarkExecutor(benchmark, results, executionDuration, configurationOverrides, slo) {
+    slo: BenchmarkExecution.Slo,
+    executionId: Int
+) : BenchmarkExecutor(benchmark, results, executionDuration, configurationOverrides, slo, executionId) {
     override fun runExperiment(load: LoadDimension, res: Resource): Boolean {
         var result = false
         val benchmarkDeployment = benchmark.buildDeployment(load, res, this.configurationOverrides)
@@ -39,7 +40,7 @@ class BenchmarkExecutorImpl(
          */
         if (this.run.get()) {
             result =
-                AnalysisExecutor(slo = slo).analyze(load = load, res = res, executionDuration = executionDuration)
+                AnalysisExecutor(slo = slo, executionId = executionId).analyze(load = load, res = res, executionDuration = executionDuration)
             this.results.setResult(Pair(load, res), result)
         }
         benchmarkDeployment.teardown()
diff --git a/theodolite-quarkus/src/main/kotlin/theodolite/execution/Shutdown.kt b/theodolite-quarkus/src/main/kotlin/theodolite/execution/Shutdown.kt
index 716bc2dfe6c4f6023af00fab58e9920a2bcf8484..f5c0251c298dea0801dc601c1d2b790de465459e 100644
--- a/theodolite-quarkus/src/main/kotlin/theodolite/execution/Shutdown.kt
+++ b/theodolite-quarkus/src/main/kotlin/theodolite/execution/Shutdown.kt
@@ -34,4 +34,4 @@ class Shutdown(private val benchmarkExecution: BenchmarkExecution, private val b
         deployment.teardown()
         logger.info { "Teardown completed" }
     }
-}
\ No newline at end of file
+}
diff --git a/theodolite-quarkus/src/main/kotlin/theodolite/execution/TheodoliteExecutor.kt b/theodolite-quarkus/src/main/kotlin/theodolite/execution/TheodoliteExecutor.kt
index 527ae4d58107a5947fdf31073e6ebfb67479b725..c2b8cc7d2ebc60b7cbb8328dfe1d7830ec5b5aff 100644
--- a/theodolite-quarkus/src/main/kotlin/theodolite/execution/TheodoliteExecutor.kt
+++ b/theodolite-quarkus/src/main/kotlin/theodolite/execution/TheodoliteExecutor.kt
@@ -1,5 +1,6 @@
 package theodolite.execution
 
+import com.google.gson.GsonBuilder
 import theodolite.benchmark.BenchmarkExecution
 import theodolite.benchmark.KubernetesBenchmark
 import theodolite.patcher.PatcherDefinitionFactory
@@ -9,6 +10,7 @@ import theodolite.util.Config
 import theodolite.util.LoadDimension
 import theodolite.util.Resource
 import theodolite.util.Results
+import java.io.PrintWriter
 import java.time.Duration
 
 /**
@@ -55,11 +57,12 @@ class TheodoliteExecutor(
 
         executor =
             BenchmarkExecutorImpl(
-                kubernetesBenchmark,
-                results,
-                executionDuration,
-                config.configOverrides,
-                config.slos[0]
+                benchmark = kubernetesBenchmark,
+                results = results,
+                executionDuration = executionDuration,
+                configurationOverrides = config.configOverrides,
+                slo = config.slos[0],
+                executionId = config.executionId
             )
 
         return Config(
@@ -94,6 +97,9 @@ class TheodoliteExecutor(
      * execution and benchmark objects.
      */
     fun run() {
+        storeAsFile(this.config, "${this.config.executionId}-execution-configuration")
+        storeAsFile(kubernetesBenchmark, "${this.config.executionId}-benchmark-configuration")
+
         val config = buildConfig()
         // execute benchmarks for each load
         for (load in config.loads) {
@@ -101,5 +107,14 @@ class TheodoliteExecutor(
                 config.compositeStrategy.findSuitableResource(load, config.resources)
             }
         }
+        storeAsFile(config.compositeStrategy.benchmarkExecutor.results, "${this.config.executionId}-result")
+    }
+
+    private fun <T> storeAsFile(saveObject: T, filename: String) {
+        val gson = GsonBuilder().enableComplexMapKeySerialization().setPrettyPrinting().create()
+
+        PrintWriter(filename).use { pw ->
+            pw.println(gson.toJson(saveObject))
+        }
     }
 }
diff --git a/theodolite-quarkus/src/main/kotlin/theodolite/execution/TheodoliteYamlExecutor.kt b/theodolite-quarkus/src/main/kotlin/theodolite/execution/TheodoliteYamlExecutor.kt
index f455f486edb97a12cc82629711e747a8fd2dd7db..8b2909f7658f4dffcfd961cad8cd00eb013a160c 100644
--- a/theodolite-quarkus/src/main/kotlin/theodolite/execution/TheodoliteYamlExecutor.kt
+++ b/theodolite-quarkus/src/main/kotlin/theodolite/execution/TheodoliteYamlExecutor.kt
@@ -44,13 +44,15 @@ class TheodoliteYamlExecutor {
         val benchmark =
             parser.parse(path = benchmarkPath, E = KubernetesBenchmark::class.java)!!
 
-        val shutdown = Shutdown(benchmarkExecution, benchmark)
-        Runtime.getRuntime().addShutdownHook(thread { shutdown.run()})
+        // Add shutdown hook
+        // Use thread{} with start = false, else the thread will start right away
+        val shutdown = thread(start = false) { Shutdown(benchmarkExecution, benchmark).run() }
+        Runtime.getRuntime().addShutdownHook(shutdown)
 
         val executor = TheodoliteExecutor(benchmarkExecution, benchmark)
         executor.run()
         logger.info { "Theodolite finished" }
-        Runtime.getRuntime().removeShutdownHook(thread { shutdown.run()})
+        Runtime.getRuntime().removeShutdownHook(shutdown)
         exitProcess(0)
     }
 }
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 65352cd0eb0b2cd91d4124259dd46202d7fb87e2..971d3428ffde9cf776711bbd68bae68f66597823 100644
--- a/theodolite-quarkus/src/main/kotlin/theodolite/execution/operator/ExecutionEventHandler.kt
+++ b/theodolite-quarkus/src/main/kotlin/theodolite/execution/operator/ExecutionEventHandler.kt
@@ -3,7 +3,7 @@ package theodolite.execution.operator
 import io.fabric8.kubernetes.client.informers.ResourceEventHandler
 import mu.KotlinLogging
 import theodolite.benchmark.BenchmarkExecution
-import theodolite.benchmark.KubernetesBenchmark
+import java.lang.NullPointerException
 
 private val logger = KotlinLogging.logger {}
 
@@ -37,9 +37,13 @@ class ExecutionHandler(private val controller: TheodoliteController): ResourceEv
     override fun onUpdate(oldExecution: BenchmarkExecution, newExecution: BenchmarkExecution) {
         logger.info { "Add updated execution to queue." }
         newExecution.name = newExecution.metadata.name
-        this.controller.executionsQueue.removeIf { e -> e.name == newExecution.metadata.name }
+        try {
+            this.controller.executionsQueue.removeIf { e -> e.name == newExecution.metadata.name }
+        } catch(e: NullPointerException) {
+            logger.warn { "No execution found for deletion" }
+        }
         this.controller.executionsQueue.addFirst(newExecution)
-        if (this.controller.isInitialized() &&  this.controller.executor.getExecution().name == newExecution.metadata.name) {
+        if (this.controller.isInitialized() && this.controller.executor.getExecution().name == newExecution.metadata.name) {
             this.controller.isUpdated.set(true)
             this.controller.executor.executor.run.compareAndSet(true, false)
         }
@@ -51,8 +55,12 @@ class ExecutionHandler(private val controller: TheodoliteController): ResourceEv
      * @param execution the execution to delete
      */
     override fun onDelete(execution: BenchmarkExecution, b: Boolean) {
-        logger.info { "Delete execution ${execution.metadata.name} from queue." }
-        this.controller.executionsQueue.removeIf { e -> e.name == execution.metadata.name }
+        try {
+            this.controller.executionsQueue.removeIf { e -> e.name == execution.metadata.name }
+            logger.info { "Delete execution ${execution.metadata.name} from queue." }
+        } catch(e: NullPointerException) {
+            logger.warn { "No execution found for deletion" }
+        }
         if (this.controller.isInitialized() && this.controller.executor.getExecution().name == execution.metadata.name) {
             this.controller.isUpdated.set(true)
             this.controller.executor.executor.run.compareAndSet(true, false)
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 8c907aed0c0a122f312c77d957c79eb8f84b8af3..532185841a7a8ee000722c1dc513219177f00cae 100644
--- a/theodolite-quarkus/src/main/kotlin/theodolite/execution/operator/TheodoliteController.kt
+++ b/theodolite-quarkus/src/main/kotlin/theodolite/execution/operator/TheodoliteController.kt
@@ -2,6 +2,7 @@ package theodolite.execution.operator
 
 import io.fabric8.kubernetes.client.NamespacedKubernetesClient
 import io.fabric8.kubernetes.client.dsl.base.CustomResourceDefinitionContext
+import khttp.patch
 import mu.KotlinLogging
 import theodolite.benchmark.BenchmarkExecution
 import theodolite.benchmark.KubernetesBenchmark
@@ -10,6 +11,7 @@ import java.lang.Thread.sleep
 import java.util.concurrent.ConcurrentHashMap
 import java.util.concurrent.ConcurrentLinkedDeque
 import java.util.concurrent.atomic.AtomicBoolean
+import java.util.concurrent.atomic.AtomicInteger
 
 private val logger = KotlinLogging.logger {}
 
@@ -29,12 +31,14 @@ private val logger = KotlinLogging.logger {}
  */
 class TheodoliteController(
     val client: NamespacedKubernetesClient,
-    val executionContext: CustomResourceDefinitionContext
+    val executionContext: CustomResourceDefinitionContext,
+    val path: String
 ) {
     lateinit var executor: TheodoliteExecutor
     val executionsQueue: ConcurrentLinkedDeque<BenchmarkExecution> = ConcurrentLinkedDeque()
     val benchmarks: ConcurrentHashMap<String, KubernetesBenchmark> = ConcurrentHashMap()
     var isUpdated = AtomicBoolean(false)
+    var executionID = AtomicInteger(0)
 
     /**
      * Runs the TheodoliteController forever.
@@ -88,14 +92,22 @@ class TheodoliteController(
      */
     @Synchronized
     fun runExecution(execution: BenchmarkExecution, benchmark: KubernetesBenchmark) {
+        execution.executionId = executionID.getAndSet(executionID.get() + 1)
         isUpdated.set(false)
+        benchmark.path = path
         logger.info { "Start execution ${execution.name} with benchmark ${benchmark.name}." }
         executor = TheodoliteExecutor(config = execution, kubernetesBenchmark = benchmark)
         executor.run()
 
-        if (!isUpdated.get()) {
-            client.customResource(executionContext).delete(client.namespace, execution.metadata.name)
+        try {
+            if (!isUpdated.get()) {
+                this.executionsQueue.removeIf { e -> e.name == execution.name }
+                client.customResource(executionContext).delete(client.namespace, execution.metadata.name)
+            }
+        } catch (e: Exception) {
+            logger.warn { "Deletion skipped." }
         }
+
         logger.info { "Execution of ${execution.name} is finally stopped." }
     }
 
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 255bf0e82ce8818738bee51bb51dcbc9c8dc27fb..5e15a4a80e67f47a42d605d4af39102927139331 100644
--- a/theodolite-quarkus/src/main/kotlin/theodolite/execution/operator/TheodoliteOperator.kt
+++ b/theodolite-quarkus/src/main/kotlin/theodolite/execution/operator/TheodoliteOperator.kt
@@ -7,6 +7,7 @@ import theodolite.benchmark.BenchmarkExecution
 import theodolite.benchmark.BenchmarkExecutionList
 import theodolite.benchmark.KubernetesBenchmark
 import theodolite.benchmark.KubernetesBenchmarkList
+import theodolite.k8s.K8sContextFactory
 
 
 private const val DEFAULT_NAMESPACE = "default"
@@ -51,7 +52,8 @@ class TheodoliteOperator {
         val executionContext = contextFactory.create(API_VERSION, SCOPE, GROUP, EXECUTION_PLURAL)
         val benchmarkContext = contextFactory.create(API_VERSION, SCOPE, GROUP, BENCHMARK_PLURAL)
 
-        val controller = TheodoliteController(client = client, executionContext = executionContext)
+        val appResource = System.getenv("THEODOLITE_APP_RESOURCES") ?: "./config"
+        val controller = TheodoliteController(client = client, executionContext = executionContext, path = appResource)
 
         val informerFactory = client.informers()
         val informerExecution = informerFactory.sharedIndexInformerForCustomResource(
diff --git a/theodolite-quarkus/src/main/kotlin/theodolite/execution/operator/K8sContextFactory.kt b/theodolite-quarkus/src/main/kotlin/theodolite/k8s/K8sContextFactory.kt
similarity index 95%
rename from theodolite-quarkus/src/main/kotlin/theodolite/execution/operator/K8sContextFactory.kt
rename to theodolite-quarkus/src/main/kotlin/theodolite/k8s/K8sContextFactory.kt
index 6e870c1d35e28683bcc5663cf99c5ddb537bdb3c..c0e07610171b40c6704602ffa86ec15accb14c19 100644
--- a/theodolite-quarkus/src/main/kotlin/theodolite/execution/operator/K8sContextFactory.kt
+++ b/theodolite-quarkus/src/main/kotlin/theodolite/k8s/K8sContextFactory.kt
@@ -1,4 +1,4 @@
-package theodolite.execution.operator
+package theodolite.k8s
 
 import io.fabric8.kubernetes.client.dsl.base.CustomResourceDefinitionContext
 
diff --git a/theodolite-quarkus/src/main/kotlin/theodolite/k8s/K8sCustomResourceWrapper.kt b/theodolite-quarkus/src/main/kotlin/theodolite/k8s/K8sCustomResourceWrapper.kt
deleted file mode 100644
index afd04da973eef5bbfe9d4c333f52b3de9add96b4..0000000000000000000000000000000000000000
--- a/theodolite-quarkus/src/main/kotlin/theodolite/k8s/K8sCustomResourceWrapper.kt
+++ /dev/null
@@ -1,54 +0,0 @@
-package theodolite.k8s
-
-import io.fabric8.kubernetes.client.CustomResource
-import io.fabric8.kubernetes.client.NamespacedKubernetesClient
-import io.fabric8.kubernetes.client.dsl.base.CustomResourceDefinitionContext
-
-/**
- * Fabric8 handles custom resources as plain HashMaps. These need to be handled differently than normal
- * Kubernetes resources.  The K8sCustomResourceWrapper class provides a wrapper to deploy and delete
- * custom resources in a uniform way.
- *
- * @param map custom resource as plain hashmap
- * @constructor Create empty K8s custom resource wrapper
- */
-class K8sCustomResourceWrapper(private val map: Map<String, String>) : CustomResource() {
-
-    /**
-     * Deploy a custom resource
-     *
-     * @param client a namespaced Kubernetes client which are used to deploy the CR object.
-     */
-    fun deploy(client: NamespacedKubernetesClient) {
-        val kind = this.map["kind"]
-        // Search the CustomResourceDefinition to which the CR Object belongs.
-        // This should be exactly one if the CRD is registered for Kubernetes, zero otherwise.
-        val crds = client.apiextensions().v1beta1().customResourceDefinitions().list()
-        crds.items
-            .filter { crd -> crd.toString().contains("kind=$kind") }
-            .map { crd -> CustomResourceDefinitionContext.fromCrd(crd) }
-            .forEach { context ->
-                client.customResource(context).createOrReplace(
-                    client.configuration.namespace, this.map as Map<String, Any>
-                )
-            }
-    }
-
-    /**
-     * Delete a custom resource
-     * @param client KubernetesClient which is used to delete the CR object.
-     */
-    fun delete(client: NamespacedKubernetesClient) {
-        val kind = this.map["kind"]
-        val metadata = this.map["metadata"] as HashMap<String, String>
-        val name = metadata["name"]
-
-        val crds = client.apiextensions().v1beta1().customResourceDefinitions().list()
-        crds.items
-            .filter { crd -> crd.toString().contains("kind=$kind") }
-            .map { crd -> CustomResourceDefinitionContext.fromCrd(crd) }
-            .forEach { context ->
-                client.customResource(context).delete(client.configuration.namespace, name)
-            }
-    }
-}
diff --git a/theodolite-quarkus/src/main/kotlin/theodolite/k8s/K8sManager.kt b/theodolite-quarkus/src/main/kotlin/theodolite/k8s/K8sManager.kt
index 17adc85e3b9f683f6a5c7eddda8fc4527cf43eea..ac2165303f083be066c4398e294e456f1d268dad 100644
--- a/theodolite-quarkus/src/main/kotlin/theodolite/k8s/K8sManager.kt
+++ b/theodolite-quarkus/src/main/kotlin/theodolite/k8s/K8sManager.kt
@@ -28,7 +28,7 @@ class K8sManager(private val client: NamespacedKubernetesClient) {
                 this.client.configMaps().createOrReplace(resource)
             is StatefulSet ->
                 this.client.apps().statefulSets().createOrReplace(resource)
-            is K8sCustomResourceWrapper -> resource.deploy(client)
+            is ServiceMonitorWrapper -> resource.deploy(client)
             else -> throw IllegalArgumentException("Unknown Kubernetes resource.")
         }
     }
@@ -47,7 +47,7 @@ class K8sManager(private val client: NamespacedKubernetesClient) {
                 this.client.configMaps().delete(resource)
             is StatefulSet ->
                 this.client.apps().statefulSets().delete(resource)
-            is K8sCustomResourceWrapper -> resource.delete(client)
+            is ServiceMonitorWrapper -> resource.delete(client)
             else -> throw IllegalArgumentException("Unknown Kubernetes resource.")
         }
     }
diff --git a/theodolite-quarkus/src/main/kotlin/theodolite/k8s/K8sResourceLoader.kt b/theodolite-quarkus/src/main/kotlin/theodolite/k8s/K8sResourceLoader.kt
index a374e97f6c9f4f80d86fadf790c021e432f1a568..324b02b74b2c53eb1292667f037f3fdbcc114b73 100644
--- a/theodolite-quarkus/src/main/kotlin/theodolite/k8s/K8sResourceLoader.kt
+++ b/theodolite-quarkus/src/main/kotlin/theodolite/k8s/K8sResourceLoader.kt
@@ -31,10 +31,8 @@ class K8sResourceLoader(private val client: NamespacedKubernetesClient) {
      * @param path of the yaml file
      * @return CustomResource from fabric8
      */
-    private fun loadCustomResource(path: String): K8sCustomResourceWrapper {
-        return loadGenericResource(path) { x: String ->
-            K8sCustomResourceWrapper(YamlParser().parse(path, HashMap<String, String>()::class.java)!!)
-        }
+    private fun loadServiceMonitor(path: String): ServiceMonitorWrapper {
+        return loadGenericResource(path) { x: String -> ServiceMonitorWrapper(YamlParser().parse(path, HashMap<String, String>()::class.java)!!) }
     }
 
     /**
@@ -89,16 +87,11 @@ class K8sResourceLoader(private val client: NamespacedKubernetesClient) {
         return when (kind) {
             "Deployment" -> loadDeployment(path)
             "Service" -> loadService(path)
-            "ServiceMonitor" -> loadCustomResource(path)
+            "ServiceMonitor" -> loadServiceMonitor(path)
             "ConfigMap" -> loadConfigmap(path)
             else -> {
-                logger.warn { "Try to load $kind from $path as Custom ressource" }
-                try {
-                    loadCustomResource(path)
-                } catch (e: Exception) {
-                    logger.error { "Error during loading of unspecified CustomResource: $e" }
-                    throw e
-                }
+                logger.error { "Error during loading of unspecified resource Kind" }
+                throw java.lang.IllegalArgumentException("error while loading resource with kind: $kind")
             }
         }
     }
diff --git a/theodolite-quarkus/src/main/kotlin/theodolite/k8s/ServiceMonitorWrapper.kt b/theodolite-quarkus/src/main/kotlin/theodolite/k8s/ServiceMonitorWrapper.kt
new file mode 100644
index 0000000000000000000000000000000000000000..4950cee225e103ff095def91de64471ec1894a79
--- /dev/null
+++ b/theodolite-quarkus/src/main/kotlin/theodolite/k8s/ServiceMonitorWrapper.kt
@@ -0,0 +1,56 @@
+package theodolite.k8s
+
+import io.fabric8.kubernetes.client.CustomResource
+import io.fabric8.kubernetes.client.NamespacedKubernetesClient
+import mu.KotlinLogging
+
+private val logger = KotlinLogging.logger {}
+
+class ServiceMonitorWrapper(private val serviceMonitor: Map<String, String>) : CustomResource() {
+
+    /**
+     * Deploy a service monitor
+     *
+     * @param client a namespaced Kubernetes client which are used to deploy the CR object.
+     *
+     * @throws java.io.IOException if the resource could not be deployed.
+     */
+    fun deploy(client: NamespacedKubernetesClient) {
+        val serviceMonitorContext = K8sContextFactory().create(
+            api = "v1",
+            scope = "Namespaced",
+            group = "monitoring.coreos.com",
+            plural = "servicemonitors"
+        )
+        client.customResource(serviceMonitorContext)
+            .createOrReplace(client.configuration.namespace, this.serviceMonitor as Map<String, Any>)
+    }
+
+    /**
+     * Delete a service monitor
+     *
+     * @param client a namespaced Kubernetes client which are used to delete the CR object.
+     */
+    fun delete(client: NamespacedKubernetesClient) {
+        val serviceMonitorContext = K8sContextFactory().create(
+            api = "v1",
+            scope = "Namespaced",
+            group = "monitoring.coreos.com",
+            plural = "servicemonitors"
+        )
+        try {
+            client.customResource(serviceMonitorContext)
+                .delete(client.configuration.namespace, this.getServiceMonitorName())
+        } catch (e: Exception) {
+            logger.warn { "Could not delete service monitor" }
+        }
+    }
+
+    /**
+     * @throws NullPointerException if name or metadata is null
+     */
+    private fun getServiceMonitorName(): String {
+        val smAsMap = this.serviceMonitor["metadata"]!! as Map<String, String>
+        return smAsMap["name"]!!
+    }
+}
diff --git a/theodolite-quarkus/src/main/kotlin/theodolite/k8s/TopicManager.kt b/theodolite-quarkus/src/main/kotlin/theodolite/k8s/TopicManager.kt
index 44a23cb68de3bb48c468e8f43086ea8224f7196c..2cbe16b5a460f0caf55bf2c99bc84dc0b3b5ac69 100644
--- a/theodolite-quarkus/src/main/kotlin/theodolite/k8s/TopicManager.kt
+++ b/theodolite-quarkus/src/main/kotlin/theodolite/k8s/TopicManager.kt
@@ -2,7 +2,6 @@ package theodolite.k8s
 
 import mu.KotlinLogging
 import org.apache.kafka.clients.admin.AdminClient
-import org.apache.kafka.clients.admin.ListTopicsResult
 import org.apache.kafka.clients.admin.NewTopic
 
 private val logger = KotlinLogging.logger {}
@@ -20,9 +19,15 @@ class TopicManager(private val kafkaConfig: HashMap<String, Any>) {
      */
     fun createTopics(newTopics: Collection<NewTopic>) {
         var kafkaAdmin: AdminClient = AdminClient.create(this.kafkaConfig)
-        kafkaAdmin.createTopics(newTopics)
+        val result = kafkaAdmin.createTopics(newTopics)
+        result.all().get()// wait for the future object
+        logger.info {
+            "Topics created finished with result: ${
+                result.values().map { it -> it.key + ": " + it.value.isDone }
+                    .joinToString(separator = ",")
+            } "
+        }
         kafkaAdmin.close()
-        logger.info { "Topics created" }
     }
 
     /**
@@ -31,15 +36,19 @@ class TopicManager(private val kafkaConfig: HashMap<String, Any>) {
      */
     fun removeTopics(topics: List<String>) {
         var kafkaAdmin: AdminClient = AdminClient.create(this.kafkaConfig)
-        val result = kafkaAdmin.deleteTopics(topics)
-
         try {
-            result.all().get()
+            val result = kafkaAdmin.deleteTopics(topics)
+            result.all().get() // wait for the future object
+            logger.info {
+                "\"Topics deletion finished with result: ${
+                    result.values().map { it -> it.key + ": " + it.value.isDone }
+                        .joinToString(separator = ",")
+                } "
+            }
         } catch (e: Exception) {
-            logger.error { "Error while removing topics: $e"  }
-            logger.debug { "Existing topics are: ${kafkaAdmin.listTopics()}."  }
+            logger.error { "Error while removing topics: $e" }
+            logger.debug { "Existing topics are: ${kafkaAdmin.listTopics()}." }
         }
         kafkaAdmin.close()
-        logger.info { "Topics removed" }
     }
 }
diff --git a/theodolite-quarkus/src/test/kotlin/theodolite/CompositeStrategyTest.kt b/theodolite-quarkus/src/test/kotlin/theodolite/CompositeStrategyTest.kt
index 67c9857220a0d419183644ffaf8c6a6e16a6ce9b..7802529bfda309131cafc0ab3f39fda43285c32f 100644
--- a/theodolite-quarkus/src/test/kotlin/theodolite/CompositeStrategyTest.kt
+++ b/theodolite-quarkus/src/test/kotlin/theodolite/CompositeStrategyTest.kt
@@ -31,7 +31,7 @@ class CompositeStrategyTest {
         val results = Results()
         val benchmark = TestBenchmark()
         val sloChecker: BenchmarkExecution.Slo = BenchmarkExecution.Slo()
-        val benchmarkExecutor = TestBenchmarkExecutorImpl(mockResults, benchmark, results, sloChecker)
+        val benchmarkExecutor = TestBenchmarkExecutorImpl(mockResults, benchmark, results, sloChecker, 0)
         val linearSearch = LinearSearch(benchmarkExecutor)
         val lowerBoundRestriction = LowerBoundRestriction(results)
         val strategy =
@@ -65,7 +65,7 @@ class CompositeStrategyTest {
         val benchmark = TestBenchmark()
         val sloChecker: BenchmarkExecution.Slo = BenchmarkExecution.Slo()
         val benchmarkExecutorImpl =
-            TestBenchmarkExecutorImpl(mockResults, benchmark, results, sloChecker)
+            TestBenchmarkExecutorImpl(mockResults, benchmark, results, sloChecker, 0)
         val binarySearch = BinarySearch(benchmarkExecutorImpl)
         val lowerBoundRestriction = LowerBoundRestriction(results)
         val strategy =
@@ -98,7 +98,7 @@ class CompositeStrategyTest {
         val results = Results()
         val benchmark = TestBenchmark()
         val sloChecker: BenchmarkExecution.Slo = BenchmarkExecution.Slo()
-        val benchmarkExecutor = TestBenchmarkExecutorImpl(mockResults, benchmark, results, sloChecker)
+        val benchmarkExecutor = TestBenchmarkExecutorImpl(mockResults, benchmark, results, sloChecker, 0)
         val binarySearch = BinarySearch(benchmarkExecutor)
         val lowerBoundRestriction = LowerBoundRestriction(results)
         val strategy =
diff --git a/theodolite-quarkus/src/test/kotlin/theodolite/TestBenchmarkExecutorImpl.kt b/theodolite-quarkus/src/test/kotlin/theodolite/TestBenchmarkExecutorImpl.kt
index 2de07d48dbe59313506745e6c97b2ae9a5ed114e..2bafcb76dfc3463d9aa350b88c9f73d52cea6629 100644
--- a/theodolite-quarkus/src/test/kotlin/theodolite/TestBenchmarkExecutorImpl.kt
+++ b/theodolite-quarkus/src/test/kotlin/theodolite/TestBenchmarkExecutorImpl.kt
@@ -12,14 +12,16 @@ class TestBenchmarkExecutorImpl(
     private val mockResults: Array<Array<Boolean>>,
     benchmark: Benchmark,
     results: Results,
-    slo: BenchmarkExecution.Slo
+    slo: BenchmarkExecution.Slo,
+    executionId: Int
 ) :
     BenchmarkExecutor(
         benchmark,
         results,
         executionDuration = Duration.ofSeconds(1),
         configurationOverrides = emptyList(),
-        slo = slo
+        slo = slo,
+        executionId = executionId
     ) {
 
     override fun runExperiment(load: LoadDimension, res: Resource): Boolean {