diff --git a/execution/helm/templates/theodolite/crd-benchmark.yaml b/execution/helm/templates/theodolite/crd-benchmark.yaml
index 9d7468b490fa2f2a6cf829bdcafab8c4bd6fc5bf..848ecb37213f2810853a47fd45d3869198acd720 100644
--- a/execution/helm/templates/theodolite/crd-benchmark.yaml
+++ b/execution/helm/templates/theodolite/crd-benchmark.yaml
@@ -1,15 +1,121 @@
 {{- if .Values.benchmarkCRD.create -}}
-apiVersion: apiextensions.k8s.io/v1beta1
+apiVersion: apiextensions.k8s.io/v1
 kind: CustomResourceDefinition
 metadata:
   name: benchmarks.theodolite.com
 spec:
   group: theodolite.com
-  version: v1alpha1
   names:
     kind: benchmark
     plural: benchmarks
+    shortNames:
+      - bench
+  versions:
+  - name: v1
+    served: true
+    storage: true
+    schema:
+      openAPIV3Schema:
+        type: object
+        required: ["spec"]
+        properties:
+          spec:
+            type: object
+            required: []
+            properties:
+              name:
+                type: string
+              appResource:
+                type: array
+                minItems: 1
+                items:
+                  type: string
+              loadGenResource:
+                type: array
+                minItems: 1
+                items:
+                  type: string
+              resourceTypes:
+                type: array
+                minItems: 1
+                items:
+                  type: object
+                  properties:
+                    typeName:
+                      type: string
+                    patchers:
+                      type: array
+                      minItems: 1
+                      items:
+                        type: object
+                        properties:
+                          type:
+                            type: string
+                            default: ""
+                          resource:
+                            type: string
+                            default: ""
+                          container:
+                            type: string
+                            default: ""
+                          variableName:
+                            type: string
+                            default: ""
+              loadTypes:
+                type: array
+                minItems: 1
+                items:
+                  type: object
+                  properties:
+                    typeName:
+                      type: string
+                    patchers:
+                      type: array
+                      minItems: 1
+                      items:
+                        type: object
+                        properties:
+                          type:
+                            type: string
+                            default: ""
+                          resource:
+                            type: string
+                            default: ""
+                          container:
+                            type: string
+                            default: ""
+                          variableName:
+                            type: string
+                            default: ""
+              kafkaConfig:
+                type: object
+                properties:
+                  bootstrapServer:
+                    type: string
+                  topics:
+                    type: array
+                    minItems: 1
+                    items:
+                      type: object
+                      required: []
+                      properties:
+                        name:
+                          type: string
+                          default: ""
+                        numPartitions:
+                          type: integer
+                          default: 0
+                        replicationFactor:
+                          type: integer
+                          default: 0
+                        removeOnly:
+                          type: boolean
+                          default: false
+    additionalPrinterColumns:
+    - name: Age
+      type: date
+      jsonPath: .metadata.creationTimestamp
+    subresources:
+      status: {}
   scope: Namespaced
-  subresources:
-    status: {}
 {{- end }}
\ No newline at end of file
diff --git a/execution/helm/templates/theodolite/crd-execution.yaml b/execution/helm/templates/theodolite/crd-execution.yaml
index 73b58397b8c1fc15ffef5da74e8f1dbdabaa3a30..92835ee1d5a016d0fe6e2db874ae222d7f49f461 100644
--- a/execution/helm/templates/theodolite/crd-execution.yaml
+++ b/execution/helm/templates/theodolite/crd-execution.yaml
@@ -1,15 +1,132 @@
 {{- if .Values.executionCRD.create -}}
-apiVersion: apiextensions.k8s.io/v1beta1
+apiVersion: apiextensions.k8s.io/v1
 kind: CustomResourceDefinition
 metadata:
   name: executions.theodolite.com
 spec:
   group: theodolite.com
-  version: v1alpha1
   names:
     kind: execution
     plural: executions
+    shortNames:
+      - exec
+  versions:
+  - name: v1
+    served: true
+    storage: true
+    schema:
+      openAPIV3Schema:
+        type: object
+        required: ["spec"]
+        properties:
+          spec:
+            type: object
+            required: ["benchmark", "load", "resources", "slos", "execution", "configOverrides"]
+            properties:
+              name:
+                type: string
+                default: ""
+              benchmark:
+                type: string
+              load: # definition of the load dimension
+                type: object
+                required: ["loadType", "loadValues"]
+                properties:
+                  loadType:
+                   type: string
+                  loadValues:
+                    type: array
+                    items:
+                      type: integer
+              resources: # definition of the resource dimension
+                type: object
+                required: ["resourceType", "resourceValues"]
+                properties:
+                  resourceType:
+                    type: string
+                  resourceValues:
+                    type: array
+                    items:
+                      type: integer
+              slos: # def of service level objectives
+                type: array
+                items:
+                  type: object
+                  required: ["sloType", "threshold", "prometheusUrl", "externalSloUrl", "offset", "warmup"]
+                  properties:
+                    sloType:
+                      type: string
+                    threshold:
+                      type: integer
+                    prometheusUrl:
+                      type: string
+                    externalSloUrl:
+                      type: string
+                    offset:
+                      type: integer
+                    warmup:
+                      type: integer
+              execution: # def execution config
+                type: object
+                required: ["strategy", "duration", "repetitions", "restrictions"]
+                properties:
+                  strategy:
+                    type: string
+                  duration:
+                    type: integer
+                  repetitions:
+                    type: integer
+                  loadGenerationDelay:
+                    type: integer
+                  restrictions:
+                    type: array
+                    items:
+                      type: string
+              configOverrides:
+                type: array
+                items:
+                  type: object
+                  properties:
+                    patcher:
+                      type: object
+                      properties:
+                        type:
+                          type: string
+                          default: ""
+                        resource:
+                          type: string
+                          default: ""
+                        container:
+                          type: string
+                          default: ""
+                        variableName:
+                          type: string
+                          default: ""
+                    value:
+                      type: string
+          status:
+            type: object
+            properties:
+              executionState:
+                description: ""
+                type: string
+              executionDuration:
+                description: "Duration of the execution in seconds"
+                type: string
+    additionalPrinterColumns:
+    - name: STATUS
+      type: string
+      description: State of the execution
+      jsonPath: .status.executionState
+    - name: Duration
+      type: string
+      description: Duration of the execution
+      jsonPath: .status.executionDuration
+    - name: Age
+      type: date
+      jsonPath: .metadata.creationTimestamp
+    subresources:
+      status: {}
   scope: Namespaced
-  subresources:
-    status: {}
+  status: {}
 {{- end }}
\ No newline at end of file
diff --git a/execution/helm/values.yaml b/execution/helm/values.yaml
index 5a34ab24fa7b8942fce01dd7b7fd38baab6aa874..d245e37e6f50d3874a1a5c06bd0156009777a999 100644
--- a/execution/helm/values.yaml
+++ b/execution/helm/values.yaml
@@ -32,6 +32,9 @@ grafana:
       org_role: Admin # Role for unauthenticated users, other valid values are `Viewer`, `Editor` and `Admin`
     users:
       default_theme: light
+    #dashboards: # the following doesn't work but is planed
+      # Path to the default home dashboard. If this value is empty, then Grafana uses StaticRootPath + "dashboards/home.json"
+      #default_home_dashboard_path: "/tmp/dashboards/k8s-dashboard.json"
   ## 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:
diff --git a/theodolite-quarkus/build.gradle b/theodolite-quarkus/build.gradle
index 8b61bf506dd241c5b0d4e75b1357ef3fd5966135..8c0a13d1f24cfde01c8604285c49d3f48fe6a3b7 100644
--- a/theodolite-quarkus/build.gradle
+++ b/theodolite-quarkus/build.gradle
@@ -25,9 +25,12 @@ dependencies {
     implementation 'io.quarkus:quarkus-kubernetes-client'
     implementation 'org.apache.kafka:kafka-clients:2.7.0'
     implementation 'khttp:khttp:1.0.0'
+    compile 'junit:junit:4.12'
+
 
     testImplementation 'io.quarkus:quarkus-junit5'
     testImplementation 'io.rest-assured:rest-assured'
+    testImplementation 'org.junit-pioneer:junit-pioneer:1.4.0'
 }
 
 group 'theodolite'
diff --git a/theodolite-quarkus/config/example-operator-benchmark.yaml b/theodolite-quarkus/config/example-operator-benchmark.yaml
index 3ed5218d8a8988b130e8d549c120cbca7329ffe3..5cf39492a8d88dc0260db8bf98a966b4ce1bccb7 100644
--- a/theodolite-quarkus/config/example-operator-benchmark.yaml
+++ b/theodolite-quarkus/config/example-operator-benchmark.yaml
@@ -1,35 +1,35 @@
-apiVersion: theodolite.com/v1alpha1
+apiVersion: theodolite.com/v1
 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"
-      - type: "NumSensorsLoadGeneratorReplicaPatcher"
-        resource: "uc1-load-generator-deployment.yaml"
-kafkaConfig:
-  bootstrapServer: "theodolite-cp-kafka:9092"
-  topics:
-    - name: "input"
-      numPartitions: 40
-      replicationFactor: 1
-    - name: "theodolite-.*"
-      removeOnly: True
\ No newline at end of file
+spec:
+  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"
+        - type: "NumSensorsLoadGeneratorReplicaPatcher"
+          resource: "uc1-load-generator-deployment.yaml"
+  kafkaConfig:
+    bootstrapServer: "theodolite-cp-kafka:9092"
+    topics:
+      - name: "input"
+        numPartitions: 40
+        replicationFactor: 1
+      - name: "theodolite-.*"
+        removeOnly: True
\ No newline at end of file
diff --git a/theodolite-quarkus/config/example-operator-execution.yaml b/theodolite-quarkus/config/example-operator-execution.yaml
index 882c38a97c882ac180a2416e0b5046fa6d467efd..e01ea377e0762a56132a709a73fb418e4c914e26 100644
--- a/theodolite-quarkus/config/example-operator-execution.yaml
+++ b/theodolite-quarkus/config/example-operator-execution.yaml
@@ -1,53 +1,53 @@
-apiVersion: theodolite.com/v1alpha1
+apiVersion: theodolite.com/v1
 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
-  delay: 30 # in seconds
-  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"
+spec:
+  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
+    delay: 30 # in seconds
+    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/src/main/kotlin/theodolite/benchmark/Benchmark.kt b/theodolite-quarkus/src/main/kotlin/theodolite/benchmark/Benchmark.kt
index 05d021b1bcfb77fa8ffeb0522510d49e39ef501c..d57a28e8bbcf4dc101e4814ecaa0d52fe28c08a9 100644
--- a/theodolite-quarkus/src/main/kotlin/theodolite/benchmark/Benchmark.kt
+++ b/theodolite-quarkus/src/main/kotlin/theodolite/benchmark/Benchmark.kt
@@ -1,5 +1,8 @@
 package theodolite.benchmark
 
+import io.fabric8.kubernetes.api.model.KubernetesResource
+import io.fabric8.kubernetes.api.model.Namespaced
+import io.fabric8.kubernetes.client.CustomResource
 import io.quarkus.runtime.annotations.RegisterForReflection
 import theodolite.util.ConfigurationOverride
 import theodolite.util.LoadDimension
diff --git a/theodolite-quarkus/src/main/kotlin/theodolite/benchmark/BenchmarkExecution.kt b/theodolite-quarkus/src/main/kotlin/theodolite/benchmark/BenchmarkExecution.kt
index 38d0f0389ce92a8720df05e892d11cf4f1ac480a..62ab75898d16ff2732ab6aa5c254ec8f87fb7266 100644
--- a/theodolite-quarkus/src/main/kotlin/theodolite/benchmark/BenchmarkExecution.kt
+++ b/theodolite-quarkus/src/main/kotlin/theodolite/benchmark/BenchmarkExecution.kt
@@ -2,8 +2,6 @@ package theodolite.benchmark
 
 import com.fasterxml.jackson.databind.annotation.JsonDeserialize
 import io.fabric8.kubernetes.api.model.KubernetesResource
-import io.fabric8.kubernetes.api.model.Namespaced
-import io.fabric8.kubernetes.client.CustomResource
 import io.quarkus.runtime.annotations.RegisterForReflection
 import theodolite.util.ConfigurationOverride
 import kotlin.properties.Delegates
@@ -26,7 +24,7 @@ import kotlin.properties.Delegates
  */
 @JsonDeserialize
 @RegisterForReflection
-class BenchmarkExecution : CustomResource(), Namespaced {
+class BenchmarkExecution : KubernetesResource {
     var executionId: Int = 0
     lateinit var name: String
     lateinit var benchmark: String
@@ -34,7 +32,7 @@ class BenchmarkExecution : CustomResource(), Namespaced {
     lateinit var resources: ResourceDefinition
     lateinit var slos: List<Slo>
     lateinit var execution: Execution
-    lateinit var configOverrides: List<ConfigurationOverride?>
+    lateinit var configOverrides: MutableList<ConfigurationOverride?>
 
     /**
      * This execution encapsulates the [strategy], the [duration], the [repetitions], and the [restrictions]
diff --git a/theodolite-quarkus/src/main/kotlin/theodolite/benchmark/BenchmarkExecutionList.kt b/theodolite-quarkus/src/main/kotlin/theodolite/benchmark/BenchmarkExecutionList.kt
deleted file mode 100644
index 50e8967f20aebad880ebd218136749af8e3ea6ee..0000000000000000000000000000000000000000
--- a/theodolite-quarkus/src/main/kotlin/theodolite/benchmark/BenchmarkExecutionList.kt
+++ /dev/null
@@ -1,5 +0,0 @@
-package theodolite.benchmark
-
-import io.fabric8.kubernetes.client.CustomResourceList
-
-class BenchmarkExecutionList : CustomResourceList<BenchmarkExecution>()
\ No newline at end of file
diff --git a/theodolite-quarkus/src/main/kotlin/theodolite/benchmark/KubernetesBenchmark.kt b/theodolite-quarkus/src/main/kotlin/theodolite/benchmark/KubernetesBenchmark.kt
index c89e9e85323d6e51c104b1d34ff1ef9d8d4d60cd..aa9c36ad912437e3b104dccf6ff1f4dea5905946 100644
--- a/theodolite-quarkus/src/main/kotlin/theodolite/benchmark/KubernetesBenchmark.kt
+++ b/theodolite-quarkus/src/main/kotlin/theodolite/benchmark/KubernetesBenchmark.kt
@@ -1,5 +1,6 @@
 package theodolite.benchmark
 
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize
 import io.fabric8.kubernetes.api.model.KubernetesResource
 import io.fabric8.kubernetes.api.model.Namespaced
 import io.fabric8.kubernetes.client.CustomResource
@@ -30,16 +31,18 @@ private var DEFAULT_NAMESPACE = "default"
  *  for the deserializing in the [theodolite.execution.operator.TheodoliteOperator].
  * @constructor construct an empty Benchmark.
  */
+@JsonDeserialize
 @RegisterForReflection
-class KubernetesBenchmark : Benchmark, CustomResource(), Namespaced {
+class KubernetesBenchmark: KubernetesResource, Benchmark{
     lateinit var name: String
     lateinit var appResource: List<String>
     lateinit var loadGenResource: List<String>
     lateinit var resourceTypes: List<TypeName>
     lateinit var loadTypes: List<TypeName>
     lateinit var kafkaConfig: KafkaConfig
-    private val namespace = System.getenv("NAMESPACE") ?: DEFAULT_NAMESPACE
-    var path = System.getenv("THEODOLITE_APP_RESOURCES") ?: "./config"
+    var namespace = System.getenv("NAMESPACE") ?: DEFAULT_NAMESPACE
+    var path =  System.getenv("THEODOLITE_APP_RESOURCES") ?: "./config"
+
 
     /**
      * Loads [KubernetesResource]s.
diff --git a/theodolite-quarkus/src/main/kotlin/theodolite/benchmark/KubernetesBenchmarkList.kt b/theodolite-quarkus/src/main/kotlin/theodolite/benchmark/KubernetesBenchmarkList.kt
deleted file mode 100644
index 0930875e96146fda58301478bda68b00c229e99f..0000000000000000000000000000000000000000
--- a/theodolite-quarkus/src/main/kotlin/theodolite/benchmark/KubernetesBenchmarkList.kt
+++ /dev/null
@@ -1,5 +0,0 @@
-package theodolite.benchmark
-
-import io.fabric8.kubernetes.client.CustomResourceList
-
-class KubernetesBenchmarkList : CustomResourceList<KubernetesBenchmark>()
\ No newline at end of file
diff --git a/theodolite-quarkus/src/main/kotlin/theodolite/evaluation/AnalysisExecutor.kt b/theodolite-quarkus/src/main/kotlin/theodolite/evaluation/AnalysisExecutor.kt
index 59a793bd7c6d2897fc715c58deda54c178d160f4..ef4d371173c7099eb091f90cddbe26d31e6522be 100644
--- a/theodolite-quarkus/src/main/kotlin/theodolite/evaluation/AnalysisExecutor.kt
+++ b/theodolite-quarkus/src/main/kotlin/theodolite/evaluation/AnalysisExecutor.kt
@@ -2,6 +2,7 @@ package theodolite.evaluation
 
 import mu.KotlinLogging
 import theodolite.benchmark.BenchmarkExecution
+import theodolite.util.IOHandler
 import theodolite.util.LoadDimension
 import theodolite.util.Resource
 import java.text.Normalizer
@@ -36,24 +37,25 @@ class AnalysisExecutor(
      */
     fun analyze(load: LoadDimension, res: Resource, executionIntervals: List<Pair<Instant, Instant>>): Boolean {
         var result = false
-        val exporter = CsvExporter()
         var repetitionCounter = 1
 
         try {
-            var resultsFolder: String = System.getenv("RESULTS_FOLDER") ?: ""
-            if (resultsFolder.isNotEmpty()){
-                resultsFolder += "/"
-            }
+            val ioHandler = IOHandler()
+            val resultsFolder: String = ioHandler.getResultFolderURL()
+            val fileURL = "${resultsFolder}exp${executionId}_${load.get()}_${res.get()}_${slo.sloType.toSlug()}"
+
             val prometheusData = executionIntervals
                 .map { interval -> fetcher.fetchMetric(
                         start = interval.first,
                         end = interval.second,
                         query = "sum by(group)(kafka_consumergroup_group_lag >= 0)") }
 
-            val fileName= "${resultsFolder}exp${executionId}_${load.get()}_${res.get()}_${slo.sloType.toSlug()}"
             prometheusData.forEach{ data ->
-                exporter.toCsv(name = "${fileName}_${repetitionCounter++}", prom = data) }
-
+                ioHandler.writeToCSVFile(
+                    fileURL = "${fileURL}_${repetitionCounter++}",
+                    data = data.getResultAsList(),
+                    columns = listOf("group", "timestamp", "value"))
+            }
 
             val sloChecker = SloCheckerFactory().create(
                 sloType = slo.sloType,
diff --git a/theodolite-quarkus/src/main/kotlin/theodolite/evaluation/CsvExporter.kt b/theodolite-quarkus/src/main/kotlin/theodolite/evaluation/CsvExporter.kt
deleted file mode 100644
index 970d1487014bfbddf7391e381d27ad5dbb246a7d..0000000000000000000000000000000000000000
--- a/theodolite-quarkus/src/main/kotlin/theodolite/evaluation/CsvExporter.kt
+++ /dev/null
@@ -1,53 +0,0 @@
-package theodolite.evaluation
-
-import mu.KotlinLogging
-import theodolite.util.PrometheusResponse
-import java.io.File
-import java.io.PrintWriter
-import java.util.*
-
-private val logger = KotlinLogging.logger {}
-
-/**
- *  Used to document the data received from prometheus for additional offline analysis.
- */
-class CsvExporter {
-
-    /**
-     * Uses the [PrintWriter] to transform a [PrometheusResponse] to a CSV file.
-     * @param name of the file.
-     * @param prom Response that is documented.
-     *
-     */
-    fun toCsv(name: String, prom: PrometheusResponse) {
-        val responseArray = promResponseToList(prom)
-        val csvOutputFile = File("$name.csv")
-
-        PrintWriter(csvOutputFile).use { pw ->
-            pw.println(listOf("group", "timestamp", "value").joinToString(separator=","))
-            responseArray.forEach {
-                pw.println(it.joinToString(separator=","))
-            }
-        }
-        logger.info { "Wrote CSV file: $name to ${csvOutputFile.absolutePath}." }
-    }
-
-    /**
-     * Converts a [PrometheusResponse] into a [List] of [List]s of [String]s
-     */
-    private fun promResponseToList(prom: PrometheusResponse): List<List<String>> {
-        val name = prom.data?.result?.get(0)?.metric?.group.toString()
-        val values = prom.data?.result?.get(0)?.values
-        val dataList = mutableListOf<List<String>>()
-
-        if (values != null) {
-            for (maybeValuePair in values) {
-                val valuePair = maybeValuePair as List<*>
-                val timestamp = (valuePair[0] as Double).toLong().toString()
-                val value = valuePair[1].toString()
-                dataList.add(listOf(name, timestamp, value))
-            }
-        }
-        return Collections.unmodifiableList(dataList)
-    }
-}
diff --git a/theodolite-quarkus/src/main/kotlin/theodolite/execution/TheodoliteExecutor.kt b/theodolite-quarkus/src/main/kotlin/theodolite/execution/TheodoliteExecutor.kt
index ded4943c14ff8250f3dcad5279ff670af5fd7fd3..d38b50b70c63c90e6bbb618386e0ed897087e6f1 100644
--- a/theodolite-quarkus/src/main/kotlin/theodolite/execution/TheodoliteExecutor.kt
+++ b/theodolite-quarkus/src/main/kotlin/theodolite/execution/TheodoliteExecutor.kt
@@ -1,22 +1,13 @@
 package theodolite.execution
 
-import com.google.gson.GsonBuilder
 import mu.KotlinLogging
 import theodolite.benchmark.BenchmarkExecution
 import theodolite.benchmark.KubernetesBenchmark
 import theodolite.patcher.PatcherDefinitionFactory
 import theodolite.strategies.StrategyFactory
 import theodolite.strategies.searchstrategy.CompositeStrategy
-import theodolite.util.Config
-import theodolite.util.LoadDimension
-import theodolite.util.Resource
-import theodolite.util.Results
+import theodolite.util.*
 import java.io.File
-import java.io.PrintWriter
-import java.lang.IllegalArgumentException
-import java.lang.Thread.sleep
-import java.nio.file.Files
-import java.nio.file.Path
 import java.time.Duration
 
 
@@ -104,34 +95,16 @@ class TheodoliteExecutor(
         return this.kubernetesBenchmark
     }
 
-    private fun getResultFolderString(): String {
-        var resultsFolder: String = System.getenv("RESULTS_FOLDER") ?: ""
-        val createResultsFolder = System.getenv("CREATE_RESULTS_FOLDER") ?: "false"
-
-        if (resultsFolder != ""){
-            logger.info { "RESULT_FOLDER: $resultsFolder" }
-            val directory = File(resultsFolder)
-            if (!directory.exists()) {
-                logger.error { "Folder $resultsFolder does not exist" }
-                if (createResultsFolder.toBoolean()) {
-                    directory.mkdirs()
-                } else {
-                    throw IllegalArgumentException("Result folder not found")
-                }
-            }
-            resultsFolder += "/"
-        }
-        return  resultsFolder
-    }
-
     /**
      * Run all experiments which are specified in the corresponding
      * execution and benchmark objects.
      */
     fun run() {
-        val resultsFolder = getResultFolderString()
-        storeAsFile(this.config, "$resultsFolder${this.config.executionId}-execution-configuration")
-        storeAsFile(kubernetesBenchmark, "$resultsFolder${this.config.executionId}-benchmark-configuration")
+        val ioHandler = IOHandler()
+        val resultsFolder = ioHandler.getResultFolderURL()
+        this.config.executionId = getAndIncrementExecutionID(resultsFolder+"expID.txt")
+        ioHandler.writeToJSONFile(this.config, "$resultsFolder${this.config.executionId}-execution-configuration")
+        ioHandler.writeToJSONFile(kubernetesBenchmark, "$resultsFolder${this.config.executionId}-benchmark-configuration")
 
         val config = buildConfig()
         // execute benchmarks for each load
@@ -140,14 +113,17 @@ class TheodoliteExecutor(
                 config.compositeStrategy.findSuitableResource(load, config.resources)
             }
         }
-        storeAsFile(config.compositeStrategy.benchmarkExecutor.results, "$resultsFolder${this.config.executionId}-result")
+        ioHandler.writeToJSONFile(config.compositeStrategy.benchmarkExecutor.results, "$resultsFolder${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))
-        }
+   private fun getAndIncrementExecutionID(fileURL: String): Int {
+       val ioHandler = IOHandler()
+       var executionID = 0
+       if (File(fileURL).exists()) {
+           executionID = ioHandler.readFileAsString(fileURL).toInt() + 1
+       }
+       ioHandler.writeStringToTextFile(fileURL, (executionID).toString())
+       return executionID
     }
+
 }
diff --git a/theodolite-quarkus/src/main/kotlin/theodolite/execution/operator/AbstractStateHandler.kt b/theodolite-quarkus/src/main/kotlin/theodolite/execution/operator/AbstractStateHandler.kt
new file mode 100644
index 0000000000000000000000000000000000000000..75cbcad051e2055f25d876e66e0fffcdc249c4f5
--- /dev/null
+++ b/theodolite-quarkus/src/main/kotlin/theodolite/execution/operator/AbstractStateHandler.kt
@@ -0,0 +1,55 @@
+package theodolite.execution.operator
+
+import io.fabric8.kubernetes.api.model.Namespaced
+import io.fabric8.kubernetes.client.CustomResource
+import io.fabric8.kubernetes.client.CustomResourceDoneable
+import io.fabric8.kubernetes.client.CustomResourceList
+import io.fabric8.kubernetes.client.KubernetesClient
+import io.fabric8.kubernetes.client.dsl.MixedOperation
+import io.fabric8.kubernetes.client.dsl.Resource
+import io.fabric8.kubernetes.client.dsl.base.CustomResourceDefinitionContext
+import java.lang.Thread.sleep
+
+abstract class AbstractStateHandler<T,L,D>(
+    private val context: CustomResourceDefinitionContext,
+    private val client: KubernetesClient,
+    private val crd: Class<T>,
+    private val crdList: Class<L>,
+    private val donableCRD: Class<D>
+    ): StateHandler where T : CustomResource, T: Namespaced, L: CustomResourceList<T>, D: CustomResourceDoneable<T> {
+
+    private val crdClient: MixedOperation<T, L, D, Resource<T, D>> =
+        this.client.customResources(this.context, this.crd, this.crdList, this.donableCRD)
+
+    @Synchronized
+    override fun setState(resourceName: String, f: (CustomResource) -> CustomResource?) {
+        this.crdClient
+            .inNamespace(this.client.namespace)
+            .list().items
+            .filter { item -> item.metadata.name == resourceName }
+            .map { customResource -> f(customResource) }
+            .forEach { this.crdClient.updateStatus(it as T) }
+       }
+
+    @Synchronized
+    override fun getState(resourceName: String, f: (CustomResource) -> String?): String? {
+        return this.crdClient
+            .inNamespace(this.client.namespace)
+            .list().items
+            .filter { item -> item.metadata.name == resourceName }
+            .map { customResource -> f(customResource) }
+            .firstOrNull()
+    }
+
+    @Synchronized
+    override fun blockUntilStateIsSet(resourceName: String, desiredStatusString: String, f: (CustomResource) -> String?, maxTries: Int): Boolean {
+        for (i in 0.rangeTo(maxTries)) {
+            val currentStatus = getState(resourceName, f)
+                if(currentStatus == desiredStatusString) {
+                    return true
+                }
+            sleep(50)
+        }
+        return false
+    }
+}
\ No newline at end of file
diff --git a/theodolite-quarkus/src/main/kotlin/theodolite/execution/operator/BenchmarkEventHandler.kt b/theodolite-quarkus/src/main/kotlin/theodolite/execution/operator/BenchmarkEventHandler.kt
deleted file mode 100644
index 7c8188e3c342cfc1b22fefdd3ca91e7dbce85905..0000000000000000000000000000000000000000
--- a/theodolite-quarkus/src/main/kotlin/theodolite/execution/operator/BenchmarkEventHandler.kt
+++ /dev/null
@@ -1,67 +0,0 @@
-package theodolite.execution.operator
-
-import io.fabric8.kubernetes.client.informers.ResourceEventHandler
-import mu.KotlinLogging
-import theodolite.benchmark.KubernetesBenchmark
-
-private val logger = KotlinLogging.logger {}
-
-/**
- * Handles adding, updating and deleting KubernetesBenchmarks.
- *
- * @param controller The TheodoliteController that handles the application state
- *
- * @see TheodoliteController
- * @see KubernetesBenchmark
- */
-class BenchmarkEventHandler(private val controller: TheodoliteController) : ResourceEventHandler<KubernetesBenchmark> {
-
-    /**
-     * Add a KubernetesBenchmark.
-     *
-     * @param benchmark the KubernetesBenchmark to add
-     *
-     * @see KubernetesBenchmark
-     */
-    override fun onAdd(benchmark: KubernetesBenchmark) {
-        benchmark.name = benchmark.metadata.name
-        logger.info { "Add new benchmark ${benchmark.name}." }
-        this.controller.benchmarks[benchmark.name] = benchmark
-    }
-
-    /**
-     * Update a KubernetesBenchmark.
-     *
-     * @param oldBenchmark the KubernetesBenchmark to update
-     * @param newBenchmark the updated KubernetesBenchmark
-     *
-     * @see KubernetesBenchmark
-     */
-    override fun onUpdate(oldBenchmark: KubernetesBenchmark, newBenchmark: KubernetesBenchmark) {
-        logger.info { "Update benchmark ${newBenchmark.metadata.name}." }
-        newBenchmark.name = newBenchmark.metadata.name
-        if (this.controller.isInitialized() && this.controller.executor.getBenchmark().name == oldBenchmark.metadata.name) {
-            this.controller.isUpdated.set(true)
-            this.controller.executor.executor.run.compareAndSet(true, false)
-        } else {
-            onAdd(newBenchmark)
-        }
-    }
-
-    /**
-     * Delete a KubernetesBenchmark.
-     *
-     * @param benchmark the KubernetesBenchmark to delete
-     *
-     * @see KubernetesBenchmark
-     */
-    override fun onDelete(benchmark: KubernetesBenchmark, b: Boolean) {
-        logger.info { "Delete benchmark ${benchmark.metadata.name}." }
-        this.controller.benchmarks.remove(benchmark.metadata.name)
-        if (this.controller.isInitialized() && this.controller.executor.getBenchmark().name == benchmark.metadata.name) {
-            this.controller.isUpdated.set(true)
-            this.controller.executor.executor.run.compareAndSet(true, false)
-            logger.info { "Current benchmark stopped." }
-        }
-    }
-}
diff --git a/theodolite-quarkus/src/main/kotlin/theodolite/execution/operator/ClusterSetup.kt b/theodolite-quarkus/src/main/kotlin/theodolite/execution/operator/ClusterSetup.kt
new file mode 100644
index 0000000000000000000000000000000000000000..6c8c48f791543b6d8a7716cf26a80bdb449ee7a7
--- /dev/null
+++ b/theodolite-quarkus/src/main/kotlin/theodolite/execution/operator/ClusterSetup.kt
@@ -0,0 +1,76 @@
+package theodolite.execution.operator
+
+import io.fabric8.kubernetes.client.NamespacedKubernetesClient
+import io.fabric8.kubernetes.client.dsl.MixedOperation
+import io.fabric8.kubernetes.client.dsl.Resource
+import mu.KotlinLogging
+import org.json.JSONObject
+import theodolite.execution.Shutdown
+import theodolite.k8s.K8sContextFactory
+import theodolite.model.crd.*
+
+private val logger = KotlinLogging.logger {}
+
+class ClusterSetup(
+    private val executionCRDClient: MixedOperation<ExecutionCRD, BenchmarkExecutionList, DoneableExecution, Resource<ExecutionCRD, DoneableExecution>>,
+    private val benchmarkCRDClient: MixedOperation<BenchmarkCRD, KubernetesBenchmarkList, DoneableBenchmark, Resource<BenchmarkCRD, DoneableBenchmark>>,
+    private val client: NamespacedKubernetesClient
+
+    ) {
+    private val serviceMonitorContext = K8sContextFactory().create(
+        api = "v1",
+        scope = "Namespaced",
+        group = "monitoring.coreos.com",
+        plural = "servicemonitors"
+    )
+
+    fun clearClusterState(){
+        stopRunningExecution()
+        clearByLabel()
+    }
+
+    private fun stopRunningExecution() {
+        executionCRDClient
+            .inNamespace(client.namespace)
+            .list()
+            .items
+            .asSequence()
+            .filter {   it.status.executionState == States.RUNNING.value }
+            .forEach { execution ->
+                val benchmark = benchmarkCRDClient
+                    .inNamespace(client.namespace)
+                    .list()
+                    .items
+                    .firstOrNull { it.metadata.name == execution.spec.benchmark }
+
+                if (benchmark != null) {
+                    execution.spec.name = execution.metadata.name
+                    benchmark.spec.name = benchmark.metadata.name
+                    Shutdown(execution.spec, benchmark.spec).start()
+                } else {
+                    logger.error {
+                        "Execution with state ${States.RUNNING.value} was found, but no corresponding benchmark. " +
+                                "Could not initialize cluster." }
+                }
+
+
+            }
+        }
+
+    private  fun clearByLabel() {
+        this.client.services().withLabel("app.kubernetes.io/created-by=theodolite").delete()
+        this.client.apps().deployments().withLabel("app.kubernetes.io/created-by=theodolite").delete()
+        this.client.apps().statefulSets().withLabel("app.kubernetes.io/created-by=theodolite").delete()
+        this.client.configMaps().withLabel("app.kubernetes.io/created-by=theodolite").delete()
+
+        val serviceMonitors = JSONObject(
+            this.client.customResource(serviceMonitorContext)
+                .list(client.namespace, mapOf(Pair("app.kubernetes.io/created-by", "theodolite")))
+        )
+            .getJSONArray("items")
+
+        (0 until serviceMonitors.length())
+            .map { serviceMonitors.getJSONObject(it).getJSONObject("metadata").getString("name") }
+            .forEach { this.client.customResource(serviceMonitorContext).delete(client.namespace, it) }
+    }
+}
\ No newline at end of file
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 ea62b7b895fce772a1f89019ea4aaac0f3957dc1..a1617b4988d500baab7b02bf5fa993f7a4ae76a3 100644
--- a/theodolite-quarkus/src/main/kotlin/theodolite/execution/operator/ExecutionEventHandler.kt
+++ b/theodolite-quarkus/src/main/kotlin/theodolite/execution/operator/ExecutionEventHandler.kt
@@ -1,8 +1,11 @@
 package theodolite.execution.operator
 
+import com.google.gson.Gson
+import com.google.gson.GsonBuilder
 import io.fabric8.kubernetes.client.informers.ResourceEventHandler
 import mu.KotlinLogging
 import theodolite.benchmark.BenchmarkExecution
+import theodolite.model.crd.*
 
 private val logger = KotlinLogging.logger {}
 
@@ -14,17 +17,30 @@ private val logger = KotlinLogging.logger {}
  * @see TheodoliteController
  * @see BenchmarkExecution
  */
-class ExecutionHandler(private val controller: TheodoliteController) : ResourceEventHandler<BenchmarkExecution> {
+class ExecutionHandler(
+    private val controller: TheodoliteController,
+    private val stateHandler: ExecutionStateHandler
+) : ResourceEventHandler<ExecutionCRD> {
+    private val gson: Gson = GsonBuilder().enableComplexMapKeySerialization().create()
 
     /**
      * Add an execution to the end of the queue of the TheodoliteController.
      *
-     * @param execution the execution to add
+     * @param ExecutionCRD the execution to add
      */
-    override fun onAdd(execution: BenchmarkExecution) {
-        execution.name = execution.metadata.name
-        logger.info { "Add new execution ${execution.metadata.name} to queue." }
-        this.controller.executionsQueue.add(execution)
+    @Synchronized
+    override fun onAdd(execution: ExecutionCRD) {
+        logger.info { "Add execution ${execution.metadata.name}" }
+        execution.spec.name = execution.metadata.name
+        when (this.stateHandler.getExecutionState(execution.metadata.name)) {
+            null -> this.stateHandler.setExecutionState(execution.spec.name, States.PENDING)
+            States.RUNNING -> {
+                this.stateHandler.setExecutionState(execution.spec.name, States.RESTART)
+                if(this.controller.isExecutionRunning(execution.spec.name)){
+                    this.controller.stop(restart=true)
+                    }
+                }
+        }
     }
 
     /**
@@ -32,40 +48,39 @@ class ExecutionHandler(private val controller: TheodoliteController) : ResourceE
      * added to the beginning of the queue of the TheodoliteController.
      * Otherwise, it is just added to the beginning of the queue.
      *
-     * @param oldExecution the old execution
-     * @param newExecution the new execution
+     * @param oldExecutionCRD the old execution
+     * @param newExecutionCRD the new execution
      */
-    override fun onUpdate(oldExecution: BenchmarkExecution, newExecution: BenchmarkExecution) {
-        logger.info { "Add updated execution to queue." }
-        newExecution.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) {
-            this.controller.isUpdated.set(true)
-            this.controller.executor.executor.run.compareAndSet(true, false)
+    @Synchronized
+    override fun onUpdate(oldExecution: ExecutionCRD, newExecution: ExecutionCRD) {
+        logger.info { "Receive update event for execution ${oldExecution.metadata.name}" }
+        newExecution.spec.name = newExecution.metadata.name
+        oldExecution.spec.name = oldExecution.metadata.name
+        if(gson.toJson(oldExecution.spec) != gson.toJson(newExecution.spec)) {
+            when(this.stateHandler.getExecutionState(newExecution.metadata.name)) {
+                States.RUNNING -> {
+                        this.stateHandler.setExecutionState(newExecution.spec.name, States.RESTART)
+                         if (this.controller.isExecutionRunning(newExecution.spec.name)){
+                            this.controller.stop(restart=true)
+                            }
+                        }
+                States.RESTART -> {} // should this set to pending?
+                else -> this.stateHandler.setExecutionState(newExecution.spec.name, States.PENDING)
+                }
+            }
         }
-    }
 
     /**
      * Delete an execution from the queue of the TheodoliteController.
      *
-     * @param execution the execution to delete
+     * @param ExecutionCRD the execution to delete
      */
-    override fun onDelete(execution: BenchmarkExecution, b: Boolean) {
-        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)
-            logger.info { "Current benchmark stopped." }
+    @Synchronized
+    override fun onDelete(execution: ExecutionCRD, b: Boolean) {
+        logger.info { "Delete execution ${execution.metadata.name}" }
+         if(execution.status.executionState == States.RUNNING.value
+             && this.controller.isExecutionRunning(execution.spec.name)) {
+            this.controller.stop()
         }
     }
 }
diff --git a/theodolite-quarkus/src/main/kotlin/theodolite/execution/operator/ExecutionStateHandler.kt b/theodolite-quarkus/src/main/kotlin/theodolite/execution/operator/ExecutionStateHandler.kt
new file mode 100644
index 0000000000000000000000000000000000000000..fe1b95f95c74efe77913ea435dd1ac896805b065
--- /dev/null
+++ b/theodolite-quarkus/src/main/kotlin/theodolite/execution/operator/ExecutionStateHandler.kt
@@ -0,0 +1,82 @@
+package theodolite.execution.operator
+
+import io.fabric8.kubernetes.client.CustomResource
+import io.fabric8.kubernetes.client.KubernetesClient
+import io.fabric8.kubernetes.client.dsl.base.CustomResourceDefinitionContext
+import theodolite.model.crd.BenchmarkExecutionList
+import theodolite.model.crd.ExecutionCRD
+import theodolite.model.crd.States
+import java.lang.Thread.sleep
+import java.time.Duration
+import java.time.Instant
+import java.util.concurrent.atomic.AtomicBoolean
+
+class ExecutionStateHandler(context: CustomResourceDefinitionContext, val client: KubernetesClient):
+    AbstractStateHandler<ExecutionCRD, BenchmarkExecutionList, DoneableExecution>(
+        context = context,
+        client = client,
+        crd = ExecutionCRD::class.java,
+        crdList = BenchmarkExecutionList::class.java,
+        donableCRD = DoneableExecution::class.java) {
+
+    private var runExecutionDurationTimer: AtomicBoolean = AtomicBoolean(false)
+
+    private fun getExecutionLambda() = { cr: CustomResource ->
+        var execState = ""
+        if (cr is ExecutionCRD) { execState = cr.status.executionState }
+        execState
+    }
+
+    private fun getDurationLambda() = { cr: CustomResource ->
+        var execState = ""
+        if (cr is ExecutionCRD) { execState = cr.status.executionState }
+        execState
+    }
+
+    fun setExecutionState(resourceName: String, status: States): Boolean {
+        setState(resourceName) {cr -> if(cr is ExecutionCRD) cr.status.executionState = status.value; cr}
+        return blockUntilStateIsSet(resourceName, status.value, getExecutionLambda())
+    }
+
+    fun getExecutionState(resourceName: String) : States? {
+        val status = this.getState(resourceName, getExecutionLambda())
+        return  States.values().firstOrNull { it.value == status }
+    }
+
+    fun setDurationState(resourceName: String, duration: Duration) {
+        setState(resourceName) { cr -> if (cr is ExecutionCRD) cr.status.executionDuration = durationToK8sString(duration); cr }
+        blockUntilStateIsSet(resourceName, durationToK8sString(duration), getDurationLambda())
+    }
+
+    fun getDurationState(resourceName: String): String? {
+        return this.getState(resourceName, getDurationLambda())
+    }
+
+    private fun durationToK8sString(duration: Duration): String {
+        val sec = duration.seconds
+        return when {
+            sec <= 120 -> "${sec}s" // max 120s
+            sec < 60 * 99 -> "${duration.toMinutes()}m" // max 99m
+            sec < 60 * 60 * 99 -> "${duration.toHours()}h"   // max 99h
+            else -> "${duration.toDays()}d + ${duration.minusDays(duration.toDays()).toHours()}h"
+        }
+    }
+
+    fun startDurationStateTimer(resourceName: String) {
+        this.runExecutionDurationTimer.set(true)
+        val startTime = Instant.now().toEpochMilli()
+        Thread {
+            while (this.runExecutionDurationTimer.get()) {
+                val duration = Duration.ofMillis(Instant.now().minusMillis(startTime).toEpochMilli())
+                setDurationState(resourceName, duration)
+                sleep(100 * 1)
+            }
+        }.start()
+    }
+
+    @Synchronized
+    fun stopDurationStateTimer() {
+        this.runExecutionDurationTimer.set(false)
+        sleep(100 * 2)
+    }
+}
\ No newline at end of file
diff --git a/theodolite-quarkus/src/main/kotlin/theodolite/execution/operator/StateHandler.kt b/theodolite-quarkus/src/main/kotlin/theodolite/execution/operator/StateHandler.kt
new file mode 100644
index 0000000000000000000000000000000000000000..0fbd97e5cca4a9be220eb0b0c89ea0af129a7860
--- /dev/null
+++ b/theodolite-quarkus/src/main/kotlin/theodolite/execution/operator/StateHandler.kt
@@ -0,0 +1,15 @@
+package theodolite.execution.operator
+
+import io.fabric8.kubernetes.client.CustomResource
+private const val MAX_TRIES: Int = 5
+
+interface StateHandler {
+    fun setState(resourceName: String, f: (CustomResource) -> CustomResource?)
+    fun getState(resourceName: String, f: (CustomResource) -> String?): String?
+    fun blockUntilStateIsSet(
+        resourceName: String,
+        desiredStatusString: String,
+        f: (CustomResource) -> String?,
+        maxTries: Int = MAX_TRIES): Boolean
+
+}
\ No newline at end of file
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 0e889e0393e17fd65f4d8f12c5b95a3dbed7f593..1e3929da98be060e2cbebf305dbae2f25519798a 100644
--- a/theodolite-quarkus/src/main/kotlin/theodolite/execution/operator/TheodoliteController.kt
+++ b/theodolite-quarkus/src/main/kotlin/theodolite/execution/operator/TheodoliteController.kt
@@ -1,122 +1,194 @@
 package theodolite.execution.operator
 
 import io.fabric8.kubernetes.client.NamespacedKubernetesClient
-import io.fabric8.kubernetes.client.dsl.base.CustomResourceDefinitionContext
+import io.fabric8.kubernetes.client.dsl.MixedOperation
+import io.fabric8.kubernetes.client.dsl.Resource
 import mu.KotlinLogging
 import theodolite.benchmark.BenchmarkExecution
 import theodolite.benchmark.KubernetesBenchmark
 import theodolite.execution.TheodoliteExecutor
+import theodolite.model.crd.*
+import theodolite.util.ConfigurationOverride
+import theodolite.util.PatcherDefinition
 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 {}
 
 /**
  * The controller implementation for Theodolite.
  *
- * Maintains a Dequeue, based on ConcurrentLinkedDequeue, of executions to be executed for a benchmark.
- *
- * @param client The NamespacedKubernetesClient
- * @param executionContext The CustomResourceDefinitionContext
- *
  * @see NamespacedKubernetesClient
  * @see CustomResourceDefinitionContext
  * @see BenchmarkExecution
  * @see KubernetesBenchmark
  * @see ConcurrentLinkedDeque
  */
+
 class TheodoliteController(
-    val client: NamespacedKubernetesClient,
-    val executionContext: CustomResourceDefinitionContext,
-    val path: String
+    private val namespace: String,
+    val path: String,
+    private val executionCRDClient: MixedOperation<ExecutionCRD, BenchmarkExecutionList, DoneableExecution, Resource<ExecutionCRD, DoneableExecution>>,
+    private val benchmarkCRDClient: MixedOperation<BenchmarkCRD, KubernetesBenchmarkList, DoneableBenchmark, Resource<BenchmarkCRD, DoneableBenchmark>>,
+    private val executionStateHandler: ExecutionStateHandler
 ) {
     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.
      */
     fun run() {
+        sleep(5000) // wait until all states are correctly set
         while (true) {
-            try {
-                reconcile()
-                logger.info { "Theodolite is waiting for new matching benchmark and execution." }
-                logger.info { "Currently available executions: " }
-                executionsQueue.forEach {
-                    logger.info { "${it.name} : waiting for : ${it.benchmark}" }
-                }
-                logger.info { "Currently available benchmarks: " }
-                benchmarks.forEach {
-                    logger.info { it.key }
-                }
-                sleep(2000)
-            } catch (e: InterruptedException) {
-                logger.error { "Execution interrupted with error: $e." }
-            }
+            reconcile()
+            sleep(2000)
         }
     }
 
-    /**
-     * Ensures that the application state corresponds to the defined KubernetesBenchmarks and BenchmarkExecutions.
-     *
-     * @see KubernetesBenchmark
-     * @see BenchmarkExecution
-     */
-    @Synchronized
     private fun reconcile() {
-        while (executionsQueue.isNotEmpty()) {
-            val execution = executionsQueue.peek()
-            val benchmark = benchmarks[execution.benchmark]
-
-            if (benchmark == null) {
-                logger.debug { "No benchmark found for execution ${execution.name}." }
-                sleep(1000)
+        do {
+            val execution = getNextExecution()
+            if (execution != null) {
+                val benchmark = getBenchmarks()
+                    .firstOrNull { it.name == execution.benchmark }
+                if (benchmark != null) {
+                    runExecution(execution, benchmark)
+                }
             } else {
-                runExecution(execution, benchmark)
+                logger.info { "Could not find executable execution." }
             }
-        }
+        } while (execution != null)
     }
 
     /**
      * Execute a benchmark with a defined KubernetesBenchmark and BenchmarkExecution
      *
-     * @see KubernetesBenchmark
      * @see BenchmarkExecution
      */
-    @Synchronized
-    fun runExecution(execution: BenchmarkExecution, benchmark: KubernetesBenchmark) {
-        execution.executionId = executionID.getAndSet(executionID.get() + 1)
-        isUpdated.set(false)
-        benchmark.path = path
-        logger.info { "Start execution ${execution.name} with benchmark ${benchmark.name}." }
-        executor = TheodoliteExecutor(config = execution, kubernetesBenchmark = benchmark)
-        executor.run()
+    private fun runExecution(execution: BenchmarkExecution, benchmark: KubernetesBenchmark) {
+        setAdditionalLabels(execution.name,
+            "deployed-for-execution",
+            benchmark.appResource + benchmark.loadGenResource,
+            execution)
+        setAdditionalLabels(benchmark.name,
+            "deployed-for-benchmark",
+            benchmark.appResource + benchmark.loadGenResource,
+            execution)
+        setAdditionalLabels("theodolite",
+            "app.kubernetes.io/created-by",
+            benchmark.appResource + benchmark.loadGenResource,
+            execution)
+
+        executionStateHandler.setExecutionState(execution.name, States.RUNNING)
+        executionStateHandler.startDurationStateTimer(execution.name)
 
         try {
-            if (!isUpdated.get()) {
-                this.executionsQueue.removeIf { e -> e.name == execution.name }
-                client.customResource(executionContext).delete(client.namespace, execution.metadata.name)
+            executor = TheodoliteExecutor(execution, benchmark)
+            executor.run()
+            when (executionStateHandler.getExecutionState(execution.name)) {
+                States.RESTART -> runExecution(execution, benchmark)
+                States.RUNNING -> {
+                    executionStateHandler.setExecutionState(execution.name, States.FINISHED)
+                    logger.info { "Execution of ${execution.name} is finally stopped." }
+                }
             }
         } catch (e: Exception) {
-            logger.warn { "Deletion skipped." }
+            logger.error { "Failure while executing execution ${execution.name} with benchmark ${benchmark.name}." }
+            logger.error { "Problem is: $e" }
+            executionStateHandler.setExecutionState(execution.name, States.FAILURE)
         }
+        executionStateHandler.stopDurationStateTimer()
+    }
 
-        logger.info { "Execution of ${execution.name} is finally stopped." }
+    @Synchronized
+    fun stop(restart: Boolean = false) {
+        if (!::executor.isInitialized) return
+        if (restart) {
+            executionStateHandler.setExecutionState(this.executor.getExecution().name, States.RESTART)
+        } else {
+            executionStateHandler.setExecutionState(this.executor.getExecution().name, States.INTERRUPTED)
+            logger.warn { "Execution ${executor.getExecution().name} unexpected interrupted" }
+        }
+        this.executor.executor.run.set(false)
+    }
+
+    /**
+     * @return all available [BenchmarkCRD]s
+     */
+    private fun getBenchmarks(): List<KubernetesBenchmark> {
+        return this.benchmarkCRDClient
+            .inNamespace(namespace)
+            .list()
+            .items
+            .map { it.spec.name = it.metadata.name; it }
+            .map { it.spec.path = path; it }
+            .map { it.spec }
     }
 
     /**
-     * @return true if the TheodoliteExecutor of this controller is initialized. Else returns false.
+     * Get the [BenchmarkExecution] for the next run. Which [BenchmarkExecution]
+     * is selected for the next execution depends on three points:
      *
-     * @see TheodoliteExecutor
+     * 1. Only executions are considered for which a matching benchmark is available on the cluster
+     * 2. The Status of the execution must be [States.PENDING] or [States.RESTART]
+     * 3. Of the remaining [BenchmarkCRD], those with status [States.RESTART] are preferred,
+     * then, if there is more than one, the oldest execution is chosen.
+     *
+     * @return the next execution or null
      */
-    @Synchronized
-    fun isInitialized(): Boolean {
-        return ::executor.isInitialized
+    private fun getNextExecution(): BenchmarkExecution? {
+        val availableBenchmarkNames = getBenchmarks()
+            .map { it.name }
+
+        return executionCRDClient
+            .inNamespace(namespace)
+            .list()
+            .items
+            .asSequence()
+            .map { it.spec.name = it.metadata.name; it }
+            .filter {
+                it.status.executionState == States.PENDING.value ||
+                        it.status.executionState == States.RESTART.value
+            }
+            .filter { availableBenchmarkNames.contains(it.spec.benchmark) }
+            .sortedWith(stateComparator().thenBy { it.metadata.creationTimestamp })
+            .map { it.spec }
+            .firstOrNull()
+    }
+
+    /**
+     * Simple comparator which can be used to order a list of [ExecutionCRD] such that executions with
+     * status [States.RESTART] are before all other executions.
+     */
+    private fun stateComparator() = Comparator<ExecutionCRD> { a, b ->
+        when {
+            (a == null && b == null) -> 0
+            (a.status.executionState == States.RESTART.value) -> -1
+            else -> 1
+        }
+    }
+
+    fun isExecutionRunning(executionName: String): Boolean {
+        return this.executor.getExecution().name == executionName
+    }
+
+    private fun setAdditionalLabels(
+        labelValue: String,
+        labelName: String,
+        resources: List<String>,
+        execution: BenchmarkExecution
+    ) {
+        val additionalConfigOverrides = mutableListOf<ConfigurationOverride>()
+        resources.forEach {
+            run {
+                val configurationOverride = ConfigurationOverride()
+                configurationOverride.patcher = PatcherDefinition()
+                configurationOverride.patcher.type = "LabelPatcher"
+                configurationOverride.patcher.variableName = labelName
+                configurationOverride.patcher.resource = it
+                configurationOverride.value = labelValue
+                additionalConfigOverrides.add(configurationOverride)
+            }
+        }
+        execution.configOverrides.addAll(additionalConfigOverrides)
     }
-}
+}
\ No newline at end of file
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 0acfc52889e8f922f4c9386422e9f9cc90836d99..0d55b0c1c1c76dba226d34554e0d96a3df77c1c3 100644
--- a/theodolite-quarkus/src/main/kotlin/theodolite/execution/operator/TheodoliteOperator.kt
+++ b/theodolite-quarkus/src/main/kotlin/theodolite/execution/operator/TheodoliteOperator.kt
@@ -1,13 +1,13 @@
 package theodolite.execution.operator
 
 import io.fabric8.kubernetes.client.DefaultKubernetesClient
+import io.fabric8.kubernetes.client.NamespacedKubernetesClient
+import io.fabric8.kubernetes.client.dsl.MixedOperation
+import io.fabric8.kubernetes.client.dsl.Resource
 import io.fabric8.kubernetes.internal.KubernetesDeserializer
 import mu.KotlinLogging
-import theodolite.benchmark.BenchmarkExecution
-import theodolite.benchmark.BenchmarkExecutionList
-import theodolite.benchmark.KubernetesBenchmark
-import theodolite.benchmark.KubernetesBenchmarkList
 import theodolite.k8s.K8sContextFactory
+import theodolite.model.crd.*
 
 
 private const val DEFAULT_NAMESPACE = "default"
@@ -16,7 +16,7 @@ private const val EXECUTION_SINGULAR = "execution"
 private const val EXECUTION_PLURAL = "executions"
 private const val BENCHMARK_SINGULAR = "benchmark"
 private const val BENCHMARK_PLURAL = "benchmarks"
-private const val API_VERSION = "v1alpha1"
+private const val API_VERSION = "v1"
 private const val RESYNC_PERIOD = 10 * 60 * 1000.toLong()
 private const val GROUP = "theodolite.com"
 private val logger = KotlinLogging.logger {}
@@ -28,7 +28,7 @@ private val logger = KotlinLogging.logger {}
  */
 class TheodoliteOperator {
     private val namespace = System.getenv("NAMESPACE") ?: DEFAULT_NAMESPACE
-    val client = DefaultKubernetesClient().inNamespace(namespace)
+    val client: NamespacedKubernetesClient = DefaultKubernetesClient().inNamespace(namespace)
 
 
     fun start() {
@@ -44,40 +44,79 @@ class TheodoliteOperator {
      */
    private fun startOperator() {
         logger.info { "Using $namespace as namespace." }
+        client.use {
+            KubernetesDeserializer.registerCustomKind(
+                "$GROUP/$API_VERSION",
+                EXECUTION_SINGULAR,
+                ExecutionCRD::class.java
+            )
 
-        KubernetesDeserializer.registerCustomKind(
-            "$GROUP/$API_VERSION",
-            EXECUTION_SINGULAR,
-            BenchmarkExecution::class.java
-        )
+            KubernetesDeserializer.registerCustomKind(
+                "$GROUP/$API_VERSION",
+                BENCHMARK_SINGULAR,
+                BenchmarkCRD::class.java
+            )
 
-        KubernetesDeserializer.registerCustomKind(
-            "$GROUP/$API_VERSION",
-            BENCHMARK_SINGULAR,
-            KubernetesBenchmark::class.java
-        )
+            val contextFactory = K8sContextFactory()
+            val executionContext = contextFactory.create(API_VERSION, SCOPE, GROUP, EXECUTION_PLURAL)
+            val benchmarkContext = contextFactory.create(API_VERSION, SCOPE, GROUP, BENCHMARK_PLURAL)
 
-        val contextFactory = K8sContextFactory()
-        val executionContext = contextFactory.create(API_VERSION, SCOPE, GROUP, EXECUTION_PLURAL)
-        val benchmarkContext = contextFactory.create(API_VERSION, SCOPE, GROUP, BENCHMARK_PLURAL)
+            val executionCRDClient: MixedOperation<
+                    ExecutionCRD,
+                    BenchmarkExecutionList,
+                    DoneableExecution,
+                    Resource<ExecutionCRD, DoneableExecution>>
+                = client.customResources(
+                    executionContext,
+                    ExecutionCRD::class.java,
+                    BenchmarkExecutionList::class.java,
+                    DoneableExecution::class.java)
 
-        val appResource = System.getenv("THEODOLITE_APP_RESOURCES") ?: "./config"
-        val controller = TheodoliteController(client = client, executionContext = executionContext, path = appResource)
+            val benchmarkCRDClient: MixedOperation<
+                    BenchmarkCRD,
+                    KubernetesBenchmarkList,
+                    DoneableBenchmark,
+                    Resource<BenchmarkCRD, DoneableBenchmark>>
+                = client.customResources(
+                    benchmarkContext,
+                    BenchmarkCRD::class.java,
+                    KubernetesBenchmarkList::class.java,
+                    DoneableBenchmark::class.java)
 
-        val informerFactory = client.informers()
-        val informerExecution = informerFactory.sharedIndexInformerForCustomResource(
-            executionContext, BenchmarkExecution::class.java,
-            BenchmarkExecutionList::class.java, RESYNC_PERIOD
-        )
-        val informerBenchmark = informerFactory.sharedIndexInformerForCustomResource(
-            benchmarkContext, KubernetesBenchmark::class.java,
-            KubernetesBenchmarkList::class.java, RESYNC_PERIOD
-        )
+            val executionStateHandler = ExecutionStateHandler(
+                context = executionContext,
+                client = client)
+
+            val appResource = System.getenv("THEODOLITE_APP_RESOURCES") ?: "./config"
+            val controller =
+                TheodoliteController(
+                    namespace = client.namespace,
+                    path = appResource,
+                    benchmarkCRDClient = benchmarkCRDClient,
+                    executionCRDClient = executionCRDClient,
+                    executionStateHandler = executionStateHandler)
+
+            val informerFactory = client.informers()
+            val informerExecution = informerFactory.sharedIndexInformerForCustomResource(
+                executionContext,
+                ExecutionCRD::class.java,
+                BenchmarkExecutionList::class.java,
+                RESYNC_PERIOD
+            )
+
+            informerExecution.addEventHandler(ExecutionHandler(
+                controller = controller,
+                stateHandler = executionStateHandler))
+
+            ClusterSetup(
+                executionCRDClient = executionCRDClient,
+                benchmarkCRDClient = benchmarkCRDClient,
+                client = client
+            ).clearClusterState()
 
-        informerExecution.addEventHandler(ExecutionHandler(controller))
-        informerBenchmark.addEventHandler(BenchmarkEventHandler(controller))
-        informerFactory.startAllRegisteredInformers()
+            informerFactory.startAllRegisteredInformers()
+            controller.run()
 
-        controller.run()
+        }
     }
 }
diff --git a/theodolite-quarkus/src/main/kotlin/theodolite/k8s/ServiceMonitorWrapper.kt b/theodolite-quarkus/src/main/kotlin/theodolite/k8s/ServiceMonitorWrapper.kt
index 4950cee225e103ff095def91de64471ec1894a79..56452d74968db0fd4c939f44f3ed8a7abbe7b928 100644
--- a/theodolite-quarkus/src/main/kotlin/theodolite/k8s/ServiceMonitorWrapper.kt
+++ b/theodolite-quarkus/src/main/kotlin/theodolite/k8s/ServiceMonitorWrapper.kt
@@ -53,4 +53,9 @@ class ServiceMonitorWrapper(private val serviceMonitor: Map<String, String>) : C
         val smAsMap = this.serviceMonitor["metadata"]!! as Map<String, String>
         return smAsMap["name"]!!
     }
+
+    fun getLabels(): Map<String, String>{
+        val smAsMap = this.serviceMonitor["metadata"]!! as Map<String, String>
+        return smAsMap["labels"]!! as Map<String, String>
+    }
 }
diff --git a/theodolite-quarkus/src/main/kotlin/theodolite/model/crd/BenchmarkCRD.kt b/theodolite-quarkus/src/main/kotlin/theodolite/model/crd/BenchmarkCRD.kt
new file mode 100644
index 0000000000000000000000000000000000000000..326aa10a21bebd913eaafcb8315188288ae97ff1
--- /dev/null
+++ b/theodolite-quarkus/src/main/kotlin/theodolite/model/crd/BenchmarkCRD.kt
@@ -0,0 +1,11 @@
+package theodolite.model.crd
+
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize
+import io.fabric8.kubernetes.api.model.Namespaced
+import io.fabric8.kubernetes.client.CustomResource
+import theodolite.benchmark.KubernetesBenchmark
+
+@JsonDeserialize
+class BenchmarkCRD(
+    var spec: KubernetesBenchmark = KubernetesBenchmark()
+) : CustomResource(), Namespaced
\ No newline at end of file
diff --git a/theodolite-quarkus/src/main/kotlin/theodolite/model/crd/BenchmarkExecutionList.kt b/theodolite-quarkus/src/main/kotlin/theodolite/model/crd/BenchmarkExecutionList.kt
new file mode 100644
index 0000000000000000000000000000000000000000..2b2dcc07f9c37f1712109e3d092f2db0c139e1c8
--- /dev/null
+++ b/theodolite-quarkus/src/main/kotlin/theodolite/model/crd/BenchmarkExecutionList.kt
@@ -0,0 +1,5 @@
+package theodolite.model.crd
+
+import io.fabric8.kubernetes.client.CustomResourceList
+
+class BenchmarkExecutionList : CustomResourceList<ExecutionCRD>()
diff --git a/theodolite-quarkus/src/main/kotlin/theodolite/model/crd/DoneableBenchmark.kt b/theodolite-quarkus/src/main/kotlin/theodolite/model/crd/DoneableBenchmark.kt
new file mode 100644
index 0000000000000000000000000000000000000000..e00e8268b2ec8eba17b3706feb3940eded1b2b0c
--- /dev/null
+++ b/theodolite-quarkus/src/main/kotlin/theodolite/model/crd/DoneableBenchmark.kt
@@ -0,0 +1,7 @@
+package theodolite.model.crd
+
+import io.fabric8.kubernetes.api.builder.Function
+import io.fabric8.kubernetes.client.CustomResourceDoneable
+
+class DoneableBenchmark(resource: BenchmarkCRD, function: Function<BenchmarkCRD, BenchmarkCRD>) :
+    CustomResourceDoneable<BenchmarkCRD>(resource, function)
\ No newline at end of file
diff --git a/theodolite-quarkus/src/main/kotlin/theodolite/model/crd/DoneableExecution.kt b/theodolite-quarkus/src/main/kotlin/theodolite/model/crd/DoneableExecution.kt
new file mode 100644
index 0000000000000000000000000000000000000000..be07725b405c29a0d9000b6e6ec455536ad111fb
--- /dev/null
+++ b/theodolite-quarkus/src/main/kotlin/theodolite/model/crd/DoneableExecution.kt
@@ -0,0 +1,8 @@
+package theodolite.execution.operator
+
+import io.fabric8.kubernetes.client.CustomResourceDoneable
+import io.fabric8.kubernetes.api.builder.Function
+import theodolite.model.crd.ExecutionCRD
+
+class DoneableExecution(resource: ExecutionCRD, function: Function<ExecutionCRD, ExecutionCRD>) :
+    CustomResourceDoneable<ExecutionCRD>(resource, function)
\ No newline at end of file
diff --git a/theodolite-quarkus/src/main/kotlin/theodolite/model/crd/ExecutionCRD.kt b/theodolite-quarkus/src/main/kotlin/theodolite/model/crd/ExecutionCRD.kt
new file mode 100644
index 0000000000000000000000000000000000000000..79a387cee250d3abf0fdb576a5f0f33424596792
--- /dev/null
+++ b/theodolite-quarkus/src/main/kotlin/theodolite/model/crd/ExecutionCRD.kt
@@ -0,0 +1,13 @@
+package theodolite.model.crd
+
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize
+import io.fabric8.kubernetes.api.model.KubernetesResource
+import io.fabric8.kubernetes.api.model.Namespaced
+import io.fabric8.kubernetes.client.CustomResource
+import theodolite.benchmark.BenchmarkExecution
+
+@JsonDeserialize
+class ExecutionCRD(
+    var spec: BenchmarkExecution = BenchmarkExecution(),
+    var status: ExecutionStatus = ExecutionStatus()
+    ) : CustomResource(), Namespaced
\ No newline at end of file
diff --git a/theodolite-quarkus/src/main/kotlin/theodolite/model/crd/ExecutionStatus.kt b/theodolite-quarkus/src/main/kotlin/theodolite/model/crd/ExecutionStatus.kt
new file mode 100644
index 0000000000000000000000000000000000000000..43e9035b3120eb22304576f2006092eec376b6d2
--- /dev/null
+++ b/theodolite-quarkus/src/main/kotlin/theodolite/model/crd/ExecutionStatus.kt
@@ -0,0 +1,13 @@
+package theodolite.model.crd
+
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize
+import io.fabric8.kubernetes.api.model.KubernetesResource
+import io.fabric8.kubernetes.api.model.Namespaced
+import io.fabric8.kubernetes.client.CustomResource
+
+@JsonDeserialize
+class ExecutionStatus(): KubernetesResource, CustomResource(), Namespaced {
+    var executionState: String = ""
+    var executionDuration: String = "-"
+
+}
\ No newline at end of file
diff --git a/theodolite-quarkus/src/main/kotlin/theodolite/model/crd/KubernetesBenchmarkList.kt b/theodolite-quarkus/src/main/kotlin/theodolite/model/crd/KubernetesBenchmarkList.kt
new file mode 100644
index 0000000000000000000000000000000000000000..8ad0a493d948bf5f78741052100766dcf6e316ec
--- /dev/null
+++ b/theodolite-quarkus/src/main/kotlin/theodolite/model/crd/KubernetesBenchmarkList.kt
@@ -0,0 +1,5 @@
+package theodolite.model.crd
+
+import io.fabric8.kubernetes.client.CustomResourceList
+
+class KubernetesBenchmarkList : CustomResourceList<BenchmarkCRD>()
diff --git a/theodolite-quarkus/src/main/kotlin/theodolite/model/crd/States.kt b/theodolite-quarkus/src/main/kotlin/theodolite/model/crd/States.kt
new file mode 100644
index 0000000000000000000000000000000000000000..79af297915b6703b209acb0c13913482e54db2be
--- /dev/null
+++ b/theodolite-quarkus/src/main/kotlin/theodolite/model/crd/States.kt
@@ -0,0 +1,11 @@
+package theodolite.model.crd
+
+enum class States(val value: String) {
+    RUNNING("RUNNING"),
+    PENDING("PENDING"),
+    FAILURE("FAILURE"),
+    FINISHED("FINISHED"),
+    RESTART("RESTART"),
+    INTERRUPTED("INTERRUPTED"),
+    NO_STATE("NoState")
+}
\ No newline at end of file
diff --git a/theodolite-quarkus/src/main/kotlin/theodolite/patcher/LabelPatcher.kt b/theodolite-quarkus/src/main/kotlin/theodolite/patcher/LabelPatcher.kt
new file mode 100644
index 0000000000000000000000000000000000000000..d9feff00726c8c73483118276eeae7b7975d8c8e
--- /dev/null
+++ b/theodolite-quarkus/src/main/kotlin/theodolite/patcher/LabelPatcher.kt
@@ -0,0 +1,50 @@
+package theodolite.patcher
+
+import io.fabric8.kubernetes.api.model.ConfigMap
+import io.fabric8.kubernetes.api.model.KubernetesResource
+import io.fabric8.kubernetes.api.model.Service
+import io.fabric8.kubernetes.api.model.apps.Deployment
+import io.fabric8.kubernetes.api.model.apps.StatefulSet
+import io.fabric8.kubernetes.client.CustomResource
+
+class LabelPatcher(private val k8sResource: KubernetesResource, val variableName: String) :
+    AbstractPatcher(k8sResource, variableName) {
+
+    override fun <String> patch(labelValue: String) {
+        println("call patcher for resource $k8sResource !")
+        if(labelValue is kotlin.String){
+            when(k8sResource){
+                is Deployment -> {
+                    if (k8sResource.metadata.labels == null){
+                        k8sResource.metadata.labels = mutableMapOf()
+                    }
+                    k8sResource.metadata.labels[this.variableName] = labelValue
+                }
+                is StatefulSet -> {
+                    if (k8sResource.metadata.labels == null){
+                        k8sResource.metadata.labels = mutableMapOf()
+                    }
+                    k8sResource.metadata.labels[this.variableName] = labelValue
+                }
+                is Service -> {
+                    if (k8sResource.metadata.labels == null){
+                        k8sResource.metadata.labels = mutableMapOf()
+                    }
+                    k8sResource.metadata.labels[this.variableName] = labelValue
+                }
+                is ConfigMap -> {
+                    if (k8sResource.metadata.labels == null){
+                        k8sResource.metadata.labels = mutableMapOf()
+                    }
+                    k8sResource.metadata.labels[this.variableName] = labelValue
+                }
+                is CustomResource -> {
+                    if (k8sResource.metadata.labels == null){
+                        k8sResource.metadata.labels = mutableMapOf()
+                    }
+                    k8sResource.metadata.labels[this.variableName] = labelValue
+                }
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/theodolite-quarkus/src/main/kotlin/theodolite/patcher/PatcherFactory.kt b/theodolite-quarkus/src/main/kotlin/theodolite/patcher/PatcherFactory.kt
index 2ee1f6c7b46322cb0f8de03c37aabe64ccf0ba5a..9ca6570ff56a673ffde144b68d3f3d9c90913ef9 100644
--- a/theodolite-quarkus/src/main/kotlin/theodolite/patcher/PatcherFactory.kt
+++ b/theodolite-quarkus/src/main/kotlin/theodolite/patcher/PatcherFactory.kt
@@ -45,6 +45,7 @@ class PatcherFactory {
                 patcherDefinition.variableName
             )
             "SchedulerNamePatcher" -> SchedulerNamePatcher(resource)
+            "LabelPatcher" -> LabelPatcher(resource, patcherDefinition.variableName)
             else -> throw IllegalArgumentException("Patcher type ${patcherDefinition.type} not found")
         }
     }
diff --git a/theodolite-quarkus/src/main/kotlin/theodolite/util/IOHandler.kt b/theodolite-quarkus/src/main/kotlin/theodolite/util/IOHandler.kt
new file mode 100644
index 0000000000000000000000000000000000000000..8d379fcf0543257edafd2e45383a02ba0254563d
--- /dev/null
+++ b/theodolite-quarkus/src/main/kotlin/theodolite/util/IOHandler.kt
@@ -0,0 +1,94 @@
+package theodolite.util
+
+import com.google.gson.GsonBuilder
+import mu.KotlinLogging
+import java.io.File
+import java.io.PrintWriter
+
+private val logger = KotlinLogging.logger {}
+
+/**
+ * The IOHandler handles most common I/O operations within the Theodolite framework
+ */
+class IOHandler {
+
+    /**
+     * The location in which Theodolite store result and configuration file are depends on
+     * the values of the environment variables `RESULT_FOLDER` and `CREATE_RESULTS_FOLDER`
+     *
+     * @return the URL of the result folder
+     */
+    fun getResultFolderURL(): String {
+        var resultsFolder: String = System.getenv("RESULTS_FOLDER") ?: ""
+        val createResultsFolder = System.getenv("CREATE_RESULTS_FOLDER") ?: "false"
+
+        if (resultsFolder != ""){
+            logger.info { "RESULT_FOLDER: $resultsFolder" }
+            val directory = File(resultsFolder)
+            if (!directory.exists()) {
+                logger.error { "Folder $resultsFolder does not exist" }
+                if (createResultsFolder.toBoolean()) {
+                    directory.mkdirs()
+                } else {
+                    throw IllegalArgumentException("Result folder not found")
+                }
+            }
+            resultsFolder += "/"
+        }
+        return  resultsFolder
+    }
+
+    /**
+     * Read a file as String
+     *
+     * @param fileURL the URL of the file
+     * @return The content of the file as String
+     */
+    fun readFileAsString(fileURL: String): String {
+        return File(fileURL).inputStream().readBytes().toString(Charsets.UTF_8).trim()
+    }
+
+    /**
+     * Creates a JSON string of the given object and store them to file
+     *
+     * @param T class of the object to save
+     * @param objectToSave object which should be saved as file
+     * @param fileURL the URL of the file
+     */
+    fun <T> writeToJSONFile(objectToSave: T, fileURL: String) {
+        val gson = GsonBuilder().enableComplexMapKeySerialization().setPrettyPrinting().create()
+        writeStringToTextFile(fileURL, gson.toJson(objectToSave))
+    }
+
+    /**
+     * Write to CSV file
+     *
+     * @param fileURL the URL of the file
+     * @param data  the data to write in the file, as list of list, each subList corresponds to a row in the CSV file
+     * @param columns columns of the CSV file
+     */
+    fun writeToCSVFile(fileURL: String, data: List<List<String>>, columns: List<String>) {
+        val outputFile = File("$fileURL.csv")
+        PrintWriter(outputFile).use { pw ->
+            pw.println(columns.joinToString(separator=","))
+            data.forEach {
+                pw.println(it.joinToString(separator=","))
+            }
+        }
+        logger.info { "Wrote CSV file: $fileURL to ${outputFile.absolutePath}." }
+    }
+
+    /**
+     * Write to text file
+     *
+     * @param fileURL the URL of the file
+     * @param data the data to write in the file as String
+     */
+    fun writeStringToTextFile(fileURL: String, data: String) {
+        val outputFile = File("$fileURL")
+        outputFile.printWriter().use {
+                it.println(data)
+        }
+        logger.info { "Wrote txt file: $fileURL to ${outputFile.absolutePath}." }
+    }
+}
\ No newline at end of file
diff --git a/theodolite-quarkus/src/main/kotlin/theodolite/util/KafkaConfig.kt b/theodolite-quarkus/src/main/kotlin/theodolite/util/KafkaConfig.kt
index 398ff90bed8f48683321e2375458b3a065c39463..4e72ccb0d86749a6538c26556241ac114ef8d9a4 100644
--- a/theodolite-quarkus/src/main/kotlin/theodolite/util/KafkaConfig.kt
+++ b/theodolite-quarkus/src/main/kotlin/theodolite/util/KafkaConfig.kt
@@ -28,6 +28,7 @@ class KafkaConfig {
      * Wrapper for a topic definition.
      */
     @RegisterForReflection
+    @JsonDeserialize
     class TopicWrapper {
         /**
          * The topic name
diff --git a/theodolite-quarkus/src/main/kotlin/theodolite/util/PrometheusResponse.kt b/theodolite-quarkus/src/main/kotlin/theodolite/util/PrometheusResponse.kt
index d1d59c482e64fd14c4744d8fcd606f286da24fb4..846577387c425e920da1c2fca1f972c880e1540a 100644
--- a/theodolite-quarkus/src/main/kotlin/theodolite/util/PrometheusResponse.kt
+++ b/theodolite-quarkus/src/main/kotlin/theodolite/util/PrometheusResponse.kt
@@ -1,6 +1,7 @@
 package theodolite.util
 
 import io.quarkus.runtime.annotations.RegisterForReflection
+import java.util.*
 
 /**
  * This class corresponds to the JSON response format of a Prometheus
@@ -17,6 +18,27 @@ data class PrometheusResponse(
      */
     var data: PromData? = null
 )
+{
+    /**
+     * Return the data of the PrometheusResponse as [List] of [List]s of [String]s
+     * The format of the returned list is: `[[ group, timestamp, value ], [ group, timestamp, value ], ... ]`
+     */
+    fun getResultAsList(): List<List<String>> {
+        val group = data?.result?.get(0)?.metric?.group.toString()
+        val values = data?.result?.get(0)?.values
+        val result = mutableListOf<List<String>>()
+
+        if (values != null) {
+            for (value in values) {
+                val valueList = value as List<*>
+                val timestamp = (valueList[0] as Double).toLong().toString()
+                val value = valueList[1].toString()
+                result.add(listOf(group, timestamp, value))
+            }
+        }
+        return Collections.unmodifiableList(result)
+    }
+}
 
 /**
  * Description of Prometheus data.
@@ -56,4 +78,5 @@ data class PromResult(
 @RegisterForReflection
 data class PromMetric(
     var group: String? = null
-)
\ No newline at end of file
+)
+
diff --git a/theodolite-quarkus/src/main/resources/operator/benchmarkCRD.yaml b/theodolite-quarkus/src/main/resources/operator/benchmarkCRD.yaml
index 8fb3de1928f051d338a78ee58da074a73ef933c1..4e481c51231999e2e7a1e75ecbc018d40db75c91 100644
--- a/theodolite-quarkus/src/main/resources/operator/benchmarkCRD.yaml
+++ b/theodolite-quarkus/src/main/resources/operator/benchmarkCRD.yaml
@@ -1,13 +1,119 @@
-apiVersion: apiextensions.k8s.io/v1beta1
+apiVersion: apiextensions.k8s.io/v1
 kind: CustomResourceDefinition
 metadata:
   name: benchmarks.theodolite.com
 spec:
   group: theodolite.com
-  version: v1alpha1
   names:
     kind: benchmark
     plural: benchmarks
-  scope: Namespaced
-  subresources:
-    status: {}
\ No newline at end of file
+    shortNames:
+      - bench
+  versions:
+  - name: v1
+    served: true
+    storage: true
+    schema:
+      openAPIV3Schema:
+        type: object
+        required: ["spec"]
+        properties:
+          spec:
+            type: object
+            required: []
+            properties:
+              name:
+                type: string
+              appResource:
+                type: array
+                minItems: 1
+                items:
+                  type: string
+              loadGenResource:
+                type: array
+                minItems: 1
+                items:
+                  type: string
+              resourceTypes:
+                type: array
+                minItems: 1
+                items:
+                  type: object
+                  properties:
+                    typeName:
+                      type: string
+                    patchers:
+                      type: array
+                      minItems: 1
+                      items:
+                        type: object
+                        properties:
+                          type:
+                            type: string
+                            default: ""
+                          resource:
+                            type: string
+                            default: ""
+                          container:
+                            type: string
+                            default: ""
+                          variableName:
+                            type: string
+                            default: ""
+              loadTypes:
+                type: array
+                minItems: 1
+                items:
+                  type: object
+                  properties:
+                    typeName:
+                      type: string
+                    patchers:
+                      type: array
+                      minItems: 1
+                      items:
+                        type: object
+                        properties:
+                          type:
+                            type: string
+                            default: ""
+                          resource:
+                            type: string
+                            default: ""
+                          container:
+                            type: string
+                            default: ""
+                          variableName:
+                            type: string
+                            default: ""
+              kafkaConfig:
+                type: object
+                properties:
+                  bootstrapServer:
+                    type: string
+                  topics:
+                    type: array
+                    minItems: 1
+                    items:
+                      type: object
+                      required: []
+                      properties:
+                        name:
+                          type: string
+                          default: ""
+                        numPartitions:
+                          type: integer
+                          default: 0
+                        replicationFactor:
+                          type: integer
+                          default: 0
+                        removeOnly:
+                          type: boolean
+                          default: false
+    additionalPrinterColumns:
+    - name: Age
+      type: date
+      jsonPath: .metadata.creationTimestamp
+    subresources:
+      status: {}
+  scope: Namespaced
\ No newline at end of file
diff --git a/theodolite-quarkus/src/main/resources/operator/example-benchmark-k8s-resource.yaml b/theodolite-quarkus/src/main/resources/operator/example-benchmark-k8s-resource.yaml
index 19ec972be8236fbdcad123e9c9ef63945bb53d16..16c14b665b99a4863279880d9ad6c03c7435578c 100644
--- a/theodolite-quarkus/src/main/resources/operator/example-benchmark-k8s-resource.yaml
+++ b/theodolite-quarkus/src/main/resources/operator/example-benchmark-k8s-resource.yaml
@@ -1,31 +1,34 @@
-apiVersion: theodolite.com/v1alpha1
+apiVersion: theodolite.com/v1
 kind: benchmark
 metadata:
   name: uc1-kstreams
-appResource:
-  - "uc1-kstreams-deployment.yaml"
-  - "aggregation-service.yaml"
-  - "jmx-configmap.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: "localhost:31290"
-  topics:
-    - name: "input"
-      numPartitions: 40
-      replicationFactor: 1
-    - name: "theodolite-.*"
-      removeOnly: True
\ No newline at end of file
+spec:
+  name: test
+  appResource:
+    - "uc1-kstreams-deployment.yaml"
+    - "aggregation-service.yaml"
+    - "jmx-configmap.yaml"
+  loadGenResource:
+    - "uc1-load-generator-deployment.yaml"
+    - "uc1-load-generator-service.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: "localhost:31290"
+    topics:
+      - name: "input"
+        numPartitions: 40
+        replicationFactor: 1
+      - name: "theodolite-.*"
+        removeOnly: True
\ No newline at end of file
diff --git a/theodolite-quarkus/src/main/resources/operator/example-execution-k8s-resource.yaml b/theodolite-quarkus/src/main/resources/operator/example-execution-k8s-resource.yaml
index 7f76b1bca0db77df08861e0611487642e19bbc1a..4227020e7750c8e93f92c469d7796e381eb452e3 100644
--- a/theodolite-quarkus/src/main/resources/operator/example-execution-k8s-resource.yaml
+++ b/theodolite-quarkus/src/main/resources/operator/example-execution-k8s-resource.yaml
@@ -1,38 +1,41 @@
-apiVersion: theodolite.com/v1alpha1
+apiVersion: theodolite.com/v1
 kind: execution
 metadata:
   name: theodolite-example-execution
-benchmark: "uc1-kstreams"
-load:
-  loadType: "NumSensors"
-  loadValues:
-    - 50000
-resources:
-  resourceType: "Instances"
-  resourceValues:
-    - 1
-slos:
-  - sloType: "lag trend"
-    threshold: 1000
-    prometheusUrl: "http://localhost:32656"
-    externalSloUrl: "http://localhost:80/evaluate-slope"
-    offset: 0
-    warmup: 0
-execution:
-  strategy: "LinearSearch"
-  duration: 60
-  repetitions: 1
-  delay: 30 # in seconds
-  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"
\ No newline at end of file
+spec:  
+  benchmark: uc1-kstreams
+  load:  
+    loadType: "NumSensors"
+    loadValues:
+      - 50000 
+  resources:
+    resourceType: "Instances"
+    resourceValues:
+      - 1
+  slos:
+    - sloType: "lag trend"
+      threshold: 1000
+      prometheusUrl: "http://localhost:32656"
+      externalSloUrl: "http://localhost:80/evaluate-slope"
+      offset: 0
+      warmup: 0
+  execution:
+    strategy: "LinearSearch"
+    duration: 60
+    repetitions: 1
+    loadGenerationDelay: 30 # in seconds
+    restrictions:
+      - "LowerBound"
+  configOverrides:
+    - patcher:
+        type: "NodeSelectorPatcher"
+        resource: "uc1-load-generator-deployment.yaml"
+        container: ""
+        variableName: "env"
+      value: "prod"
+    - patcher:
+        type: "NodeSelectorPatcher"
+        resource: "uc1-kstreams-deployment.yaml"
+        container: ""
+        variableName: "env"
+      value: "prod"
diff --git a/theodolite-quarkus/src/main/resources/operator/executionCRD.yaml b/theodolite-quarkus/src/main/resources/operator/executionCRD.yaml
index 0bdb83c6201112a750bad41b81321b7a108a66fa..8e1189572ee993c37dd565fc62a66996654766f2 100644
--- a/theodolite-quarkus/src/main/resources/operator/executionCRD.yaml
+++ b/theodolite-quarkus/src/main/resources/operator/executionCRD.yaml
@@ -1,13 +1,129 @@
-apiVersion: apiextensions.k8s.io/v1beta1
+apiVersion: apiextensions.k8s.io/v1
 kind: CustomResourceDefinition
 metadata:
   name: executions.theodolite.com
 spec:
   group: theodolite.com
-  version: v1alpha1
   names:
     kind: execution
     plural: executions
-  scope: Namespaced
-  subresources:
-    status: {}
\ No newline at end of file
+    shortNames:
+      - exec
+  versions:
+  - name: v1
+    served: true
+    storage: true
+    schema:
+      openAPIV3Schema:
+        type: object
+        required: ["spec"]
+        properties:
+          spec:
+            type: object
+            required: ["benchmark", "load", "resources", "slos", "execution", "configOverrides"]
+            properties:
+              name:
+                type: string
+                default: ""
+              benchmark:
+                type: string
+              load: # definition of the load dimension
+                type: object
+                required: ["loadType", "loadValues"]
+                properties:
+                  loadType:
+                   type: string
+                  loadValues:
+                    type: array
+                    items:
+                      type: integer
+              resources: # definition of the resource dimension
+                type: object
+                required: ["resourceType", "resourceValues"]
+                properties:
+                  resourceType:
+                    type: string
+                  resourceValues:
+                    type: array
+                    items:
+                      type: integer
+              slos: # def of service level objectives
+                type: array
+                items:
+                  type: object
+                  required: ["sloType", "threshold", "prometheusUrl", "externalSloUrl", "offset", "warmup"]
+                  properties:
+                    sloType:
+                      type: string
+                    threshold:
+                      type: integer
+                    prometheusUrl:
+                      type: string
+                    externalSloUrl:
+                      type: string
+                    offset:
+                      type: integer
+                    warmup:
+                      type: integer
+              execution: # def execution config
+                type: object
+                required: ["strategy", "duration", "repetitions", "restrictions"]
+                properties:
+                  strategy:
+                    type: string
+                  duration:
+                    type: integer
+                  repetitions:
+                    type: integer
+                  loadGenerationDelay:
+                    type: integer
+                  restrictions:
+                    type: array
+                    items:
+                      type: string
+              configOverrides:
+                type: array
+                items:
+                  type: object
+                  properties:
+                    patcher:
+                      type: object
+                      properties:
+                        type:
+                          type: string
+                          default: ""
+                        resource:
+                          type: string
+                          default: ""
+                        container:
+                          type: string
+                          default: ""
+                        variableName:
+                          type: string
+                          default: ""
+                    value:
+                      type: string
+          status:
+            type: object
+            properties:
+              executionState:
+                description: ""
+                type: string
+              executionDuration:
+                description: "Duration of the execution in seconds"
+                type: string
+    additionalPrinterColumns:
+    - name: STATUS
+      type: string
+      description: State of the execution
+      jsonPath: .status.executionState
+    - name: Duration
+      type: string
+      description: Duration of the execution
+      jsonPath: .status.executionDuration
+    - name: Age
+      type: date
+      jsonPath: .metadata.creationTimestamp
+    subresources:
+      status: {}
+  scope: Namespaced
\ No newline at end of file
diff --git a/theodolite-quarkus/src/test/kotlin/theodolite/ResourceLimitPatcherTest.kt b/theodolite-quarkus/src/test/kotlin/theodolite/ResourceLimitPatcherTest.kt
index 82e4bc5d77f3f35d217c56a377513c0e7d329170..4f4308a35df598dc4932a8ac89a6c07fb967e416 100644
--- a/theodolite-quarkus/src/test/kotlin/theodolite/ResourceLimitPatcherTest.kt
+++ b/theodolite-quarkus/src/test/kotlin/theodolite/ResourceLimitPatcherTest.kt
@@ -21,7 +21,7 @@ import theodolite.util.PatcherDefinition
  */
 @QuarkusTest
 class ResourceLimitPatcherTest {
-    val testPath = "./src/main/resources/testYaml/"
+    val testPath = "./src/test/resources/"
     val loader = K8sResourceLoader(DefaultKubernetesClient().inNamespace(""))
     val patcherFactory = PatcherFactory()
 
diff --git a/theodolite-quarkus/src/test/kotlin/theodolite/ResourceRequestPatcherTest.kt b/theodolite-quarkus/src/test/kotlin/theodolite/ResourceRequestPatcherTest.kt
index 3cd6b012f09c5471b1b011b5cd03e61a0fab1c4e..7214422705a64f507a196a4b5f9b334665407422 100644
--- a/theodolite-quarkus/src/test/kotlin/theodolite/ResourceRequestPatcherTest.kt
+++ b/theodolite-quarkus/src/test/kotlin/theodolite/ResourceRequestPatcherTest.kt
@@ -21,7 +21,7 @@ import theodolite.util.PatcherDefinition
  */
 @QuarkusTest
 class ResourceRequestPatcherTest {
-    val testPath = "./src/main/resources/testYaml/"
+    val testPath = "./src/test/resources/"
     val loader = K8sResourceLoader(DefaultKubernetesClient().inNamespace(""))
     val patcherFactory = PatcherFactory()
 
diff --git a/theodolite-quarkus/src/test/kotlin/theodolite/util/IOHandlerTest.kt b/theodolite-quarkus/src/test/kotlin/theodolite/util/IOHandlerTest.kt
new file mode 100644
index 0000000000000000000000000000000000000000..6984c3935da3d26f95565b758547a98e0eeb155f
--- /dev/null
+++ b/theodolite-quarkus/src/test/kotlin/theodolite/util/IOHandlerTest.kt
@@ -0,0 +1,134 @@
+package theodolite.util
+
+import com.google.gson.GsonBuilder
+import io.quarkus.test.junit.QuarkusTest
+import org.hamcrest.CoreMatchers.containsString
+import org.hamcrest.MatcherAssert.assertThat
+import org.junit.Rule
+import org.junit.jupiter.api.Assertions.assertEquals
+import org.junit.jupiter.api.Assertions.assertTrue
+import org.junit.jupiter.api.Test
+import org.junit.rules.TemporaryFolder
+import org.junitpioneer.jupiter.ClearEnvironmentVariable
+import org.junitpioneer.jupiter.SetEnvironmentVariable
+
+
+const val FOLDER_URL = "Test-Folder"
+@QuarkusTest
+internal class IOHandlerTest {
+
+    @Rule
+    private var temporaryFolder = TemporaryFolder()
+
+    @Test
+    fun testWriteStringToText() {
+        temporaryFolder.create()
+        val testContent = "Test-File-Content"
+        val folder = temporaryFolder.newFolder(FOLDER_URL)
+
+        IOHandler().writeStringToTextFile(
+            fileURL = "${folder.absolutePath}/test-file.txt",
+            data = testContent)
+
+        assertEquals(
+           testContent,
+            IOHandler().readFileAsString("${folder.absolutePath}/test-file.txt")
+        )
+    }
+
+    @Test
+    fun testWriteToCSVFile() {
+        temporaryFolder.create()
+        val folder = temporaryFolder.newFolder(FOLDER_URL)
+
+        val testContent = listOf(
+            listOf("apples","red"),
+            listOf("bananas","yellow"),
+            listOf("avocado","brown"))
+        val columns = listOf("Fruit", "Color")
+
+        IOHandler().writeToCSVFile(
+            fileURL = "${folder.absolutePath}/test-file",
+            data = testContent,
+            columns = columns)
+
+        var expected = "Fruit,Color\n"
+        testContent.forEach { expected += it[0] + "," +  it[1] + "\n" }
+
+        assertEquals(
+            expected.trim(),
+            IOHandler().readFileAsString("${folder.absolutePath}/test-file.csv")
+        )
+    }
+
+    @Test
+    fun testWriteToJSONFile() {
+        temporaryFolder.create()
+        val folder = temporaryFolder.newFolder(FOLDER_URL)
+        val testContent = Resource(0, emptyList())
+
+        IOHandler().writeToJSONFile(
+            fileURL =  "${folder.absolutePath}/test-file.json",
+            objectToSave = testContent)
+
+        val expected = GsonBuilder().enableComplexMapKeySerialization().setPrettyPrinting().create().toJson(testContent)
+
+        assertEquals(
+            expected,
+            IOHandler().readFileAsString("${folder.absolutePath}/test-file.json")
+        )
+    }
+
+    // Test the function `getResultFolderString`
+
+    @Test
+    @ClearEnvironmentVariable.ClearEnvironmentVariables(
+        ClearEnvironmentVariable(key = "RESULTS_FOLDER"),
+        ClearEnvironmentVariable(key = "CREATE_RESULTS_FOLDER")
+    )
+    fun testGetResultFolderURL_emptyEnvironmentVars() {
+        assertEquals("",IOHandler().getResultFolderURL())
+    }
+
+
+    @Test()
+    @SetEnvironmentVariable.SetEnvironmentVariables(
+        SetEnvironmentVariable(key = "RESULTS_FOLDER", value = "./src/test/resources"),
+        SetEnvironmentVariable(key = "CREATE_RESULTS_FOLDER", value = "false")
+    )
+    fun testGetResultFolderURL_FolderExist() {
+        assertEquals("./src/test/resources/", IOHandler().getResultFolderURL())
+    }
+
+    @Test()
+    @SetEnvironmentVariable.SetEnvironmentVariables(
+        SetEnvironmentVariable(key = "RESULTS_FOLDER", value = "$FOLDER_URL-0"),
+        SetEnvironmentVariable(key = "CREATE_RESULTS_FOLDER", value = "false")
+    )
+    fun testGetResultFolderURL_FolderNotExist() {
+        var exceptionWasThrown = false
+        try {
+            IOHandler().getResultFolderURL()
+          } catch (e: Exception){
+              exceptionWasThrown = true
+              assertThat(e.toString(), containsString("Result folder not found"));
+          }
+        assertTrue(exceptionWasThrown)
+    }
+
+    @Test()
+    @SetEnvironmentVariable.SetEnvironmentVariables(
+        SetEnvironmentVariable(key = "RESULTS_FOLDER", value = FOLDER_URL),
+        SetEnvironmentVariable(key = "CREATE_RESULTS_FOLDER", value = "true")
+    )
+    fun testGetResultFolderURL_CreateFolderIfNotExist() {
+        assertEquals("$FOLDER_URL/",  IOHandler().getResultFolderURL())
+    }
+
+    @Test()
+    @ClearEnvironmentVariable(key = "RESULTS_FOLDER" )
+    @SetEnvironmentVariable(key = "CREATE_RESULTS_FOLDER", value = "true")
+    fun testGetResultFolderURL_CreateFolderButNoFolderGiven() {
+        assertEquals("",  IOHandler().getResultFolderURL())
+    }
+}
diff --git a/theodolite-quarkus/src/main/resources/testYaml/cpu-deployment.yaml b/theodolite-quarkus/src/test/resources/cpu-deployment.yaml
similarity index 100%
rename from theodolite-quarkus/src/main/resources/testYaml/cpu-deployment.yaml
rename to theodolite-quarkus/src/test/resources/cpu-deployment.yaml
diff --git a/theodolite-quarkus/src/main/resources/testYaml/cpu-memory-deployment.yaml b/theodolite-quarkus/src/test/resources/cpu-memory-deployment.yaml
similarity index 100%
rename from theodolite-quarkus/src/main/resources/testYaml/cpu-memory-deployment.yaml
rename to theodolite-quarkus/src/test/resources/cpu-memory-deployment.yaml
diff --git a/theodolite-quarkus/src/main/resources/testYaml/memory-deployment.yaml b/theodolite-quarkus/src/test/resources/memory-deployment.yaml
similarity index 100%
rename from theodolite-quarkus/src/main/resources/testYaml/memory-deployment.yaml
rename to theodolite-quarkus/src/test/resources/memory-deployment.yaml
diff --git a/theodolite-quarkus/src/main/resources/testYaml/no-resources-deployment.yaml b/theodolite-quarkus/src/test/resources/no-resources-deployment.yaml
similarity index 100%
rename from theodolite-quarkus/src/main/resources/testYaml/no-resources-deployment.yaml
rename to theodolite-quarkus/src/test/resources/no-resources-deployment.yaml