diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index b7d75d5470d8073a2021e06428e9170f605ec7b3..01630912c52ccc728870d72c33a8f02cdf7c2c7f 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -51,7 +51,7 @@ build-benchmarks:
       - "theodolite-benchmarks/build/libs/*.jar"
       - "theodolite-benchmarks/*/build/libs/*.jar"
       - "theodolite-benchmarks/*/build/distributions/*.tar"
-    expire_in: 1 day
+    expire_in: 6 hours
 
 test-benchmarks:
   stage: test
@@ -234,7 +234,7 @@ build-theodolite-jvm:
     paths:
       - "theodolite-quarkus/build/lib/*"
       - "theodolite-quarkus/build/*-runner.jar"
-    expire_in: 1 day
+    expire_in: 6 hours
 
 build-theodolite-native:
   stage: build
@@ -242,17 +242,18 @@ build-theodolite-native:
   script:
     - gu install native-image # TODO move to image
     - ./gradlew --build-cache assemble -Dquarkus.package.type=native
+  when: manual
   artifacts:
     paths:
       - "theodolite-quarkus/build/*-runner"
-    expire_in: 1 day
+    expire_in: 6 hours
 
 test-theodolite:
   stage: test
   extends: .theodolite
   needs:
     - build-theodolite-jvm
-    - build-theodolite-native
+    #- build-theodolite-native
   script: ./gradlew test --stacktrace
 
 # Disabled for now
@@ -279,12 +280,13 @@ deploy-theodolite:
     - .theodolite
     - .dind
   needs:
-    - build-theodolite-native
+    #- build-theodolite-native
+    - build-theodolite-jvm
     - test-theodolite
   script:
     - DOCKER_TAG_NAME=$(echo $CI_COMMIT_REF_SLUG- | sed 's/^master-$//')
-    - docker build -f src/main/docker/Dockerfile.native -t theodolite .
-    #- docker build -f src/main/docker/Dockerfile.jvm -t theodolite .
+    #- docker build -f src/main/docker/Dockerfile.native -t theodolite .
+    - docker build -f src/main/docker/Dockerfile.jvm -t theodolite .
     - "[ ! $CI_COMMIT_TAG ] && docker tag theodolite $CR_HOST/$CR_ORG/theodolite:${DOCKER_TAG_NAME}latest"
     - "[ ! $CI_COMMIT_TAG ] && docker tag theodolite $CR_HOST/$CR_ORG/theodolite:$DOCKER_TAG_NAME$CI_COMMIT_SHORT_SHA"
     - "[ $CI_COMMIT_TAG ] && docker tag theodolite $CR_HOST/$CR_ORG/theodolite:$CI_COMMIT_TAG"
@@ -309,6 +311,7 @@ deploy-slo-checker-lag-trend:
   stage: deploy
   extends:
     - .dind
+  needs: []
   script:
     - DOCKER_TAG_NAME=$(echo $CI_COMMIT_REF_SLUG- | sed 's/^master-$//')
     - docker build --pull -t theodolite-slo-checker-lag-trend slope-evaluator
@@ -335,6 +338,7 @@ deploy-random-scheduler:
   stage: deploy
   extends:
     - .dind
+  needs: []
   script:
     - DOCKER_TAG_NAME=$(echo $CI_COMMIT_REF_SLUG- | sed 's/^master-$//')
     - docker build --pull -t theodolite-random-scheduler execution/infrastructure/random-scheduler
diff --git a/execution/README.md b/execution/README.md
index ca15111c0ad7000a200c0c50427a2c2aeb75e093..eb6ade9f2aab28b6d237e9622f22da0ea5998a50 100644
--- a/execution/README.md
+++ b/execution/README.md
@@ -225,7 +225,17 @@ Theodolite locally on your machine see the description below.
 see the [Configuration](#configuration) section below. Note, that you might uncomment the `serviceAccountName` line if
 RBAC is enabled on your cluster (see installation of [Theodolite RBAC](#Theodolite-RBAC)).
 
-To start the execution of a benchmark run (with `<your-theodolite-yaml>` being your job definition):
+To start the execution of a benchmark create a ConfigMap which containts all required Kubernetes resource files for the SUT and the load generator, a ConfigMap for the execution and a ConfigMap for the benchmark.
+
+```sh
+kubectl create configmap app-resources-configmap --from-file=<folder-with-all-required-k8s-resources>
+kubectl create configmap execution-configmap --from-file=<execution.yaml>
+kubectl create configmap benchmark-configmap --from-file=<benchmark.yaml>
+```
+
+This will create three ConfigMaps. You can verify this via `kubectl get configmaps`.
+
+Start the Theodolite job (with `<your-theodolite-yaml>` being your job definition):
 
 ```sh
 kubectl create -f <your-theodolite-yaml>
@@ -241,24 +251,7 @@ Kubernetes volume.
 
 ### Configuration
 
-| Command line         | Kubernetes          | Description                                                  |
-| -------------------- | ------------------- | ------------------------------------------------------------ |
-| --uc                 | UC                  | **[Mandatory]** Stream processing use case to be benchmarked. Has to be one of `1`, `2`, `3` or `4`. |
-| --loads              | LOADS               | **[Mandatory]** Values for the workload generator to be tested, should be sorted in ascending order. |
-| --instances          | INSTANCES           | **[Mandatory]** Numbers of instances to be benchmarked, should be sorted in ascending order. |
-| --duration           | DURATION            | Duration in minutes subexperiments should be executed for. *Default:* `5`. |
-| --partitions         | PARTITIONS          | Number of partitions for Kafka topics. *Default:* `40`.      |
-| --cpu-limit          | CPU_LIMIT           | Kubernetes CPU limit for a single Pod.  *Default:* `1000m`.  |
-| --memory-limit       | MEMORY_LIMIT        | Kubernetes memory limit for a single Pod. *Default:* `4Gi`.  |
-| --domain-restriction | DOMAIN_RESTRICTION  | A flag that indiciates domain restriction should be used. *Default:* not set. For more details see Section [Domain Restriction](#domain-restriction). |
-| --search-strategy    | SEARCH_STRATEGY     | The benchmarking search strategy. Can be set to `check-all`, `linear-search` or `binary-search`. *Default:* `check-all`. For more details see Section [Benchmarking Search Strategies](#benchmarking-search-strategies). |
-| --reset              | RESET               | Resets the environment before each subexperiment. Useful if execution was aborted and just one experiment should be executed. |
-| --reset-only         | RESET_ONLY          | Only resets the environment. Ignores all other parameters. Useful if execution was aborted and one want a clean state for new executions. |
-| --namespace          | NAMESPACE        | Kubernetes namespace. *Default:* `default`.  |
-| --prometheus         | PROMETHEUS_BASE_URL | Defines where to find the prometheus instance. *Default:* `http://localhost:9090` |
-| --path               | RESULT_PATH         | A directory path for the results. Relative to the Execution folder. *Default:* `results` |
-| --configurations     | CONFIGURATIONS      | Defines environment variables for the use cases and, thus, enables further configuration options. |
-| --threshold          | THRESHOLD           | The threshold for the trend slop that the search strategies use to determine that a load could be handled. *Default:* `2000` |
+Be sure, that the names of the configmap corresponds correctly to the specifications of the mounted `configmaps`, `volumes`, `mountPath`. In particular: The name of the execution file and the benchmark file must match the value of the corresponding environment variable.
 
 ### Domain Restriction
 
diff --git a/execution/helm/README.md b/execution/helm/README.md
index 6a2f083559fd0fe7b5674834e99a075e9bea1851..c545804aaec8eb8ed91054f1f7ee97dd293816a4 100644
--- a/execution/helm/README.md
+++ b/execution/helm/README.md
@@ -6,11 +6,9 @@ Install the chart via:
 
 ```sh
 helm dependencies update .
-helm install my-confluent .
+helm install theodolite .
 ```
 
-**Please note: Theodolite currently uses hard-coded URLs, to connect to Kafka and Zookeeper. For that reason, the name of this chart must be `my-confluent`.** We will change this behavior soon.
-
 This chart installs requirements to execute benchmarks with Theodolite.
 
 Dependencies and subcharts:
@@ -27,7 +25,7 @@ Dependencies and subcharts:
 Test the installation:
 
 ```sh
-helm test <release-name>
+helm test theodolite
 ```
 
 Our test files are located [here](templates/../../theodolite-chart/templates/tests). Many subcharts have their own tests, these are also executed and are placed in the respective /templates folders. 
@@ -46,18 +44,18 @@ helm install theodolite . -f preconfigs/one-broker-values.yaml
 
 ## Uninstall this Chart
 
-To uninstall/delete the `my-release` deployment:
+To uninstall/delete the `theodolite` deployment:
 
 ```sh
-helm delete my-release
+helm delete theodolite
 ```
 
 This command does not remove the CRDs which are created by this chart. Remove them manually with:
 
 ```sh
 # CRDs from Theodolite
-kubectl delete crd execution
-kubectl delete crd benchmark
+kubectl delete crd executions.theodolite.com
+kubectl delete crd benchmarks.theodolite.com
 # CRDs from Prometheus operator (see https://github.com/prometheus-community/helm-charts/tree/main/charts/kube-prometheus-stack#uninstall-chart)
 kubectl delete crd alertmanagerconfigs.monitoring.coreos.com
 kubectl delete crd alertmanagers.monitoring.coreos.com
diff --git a/execution/helm/templates/theodolite/role.yaml b/execution/helm/templates/theodolite/role.yaml
index 73e3e5098448c88e6b030f1dc2056caf999da3ce..ee699a5b861c64e355ca2ba44330a7d600756b77 100644
--- a/execution/helm/templates/theodolite/role.yaml
+++ b/execution/helm/templates/theodolite/role.yaml
@@ -42,7 +42,7 @@ rules:
     - delete
     - list
     - create
-  {{- if .Values.operator.enabled -}}
+  {{- if .Values.operator.enabled }}
   - apiGroups:
     - theodolite.com
     resources: 
diff --git a/execution/helm/templates/theodolite/thedolite-operator.yaml b/execution/helm/templates/theodolite/thedolite-operator.yaml
index cb3f193b89c3a198bab853fb85a0db1184394dce..3af9cfffd71059993688d5be77ae110a2e3cdc2d 100644
--- a/execution/helm/templates/theodolite/thedolite-operator.yaml
+++ b/execution/helm/templates/theodolite/thedolite-operator.yaml
@@ -23,9 +23,19 @@ spec:
               value: {{ .Release.Namespace }}
             - name: MODE
               value: operator
+            - name: THEODOLITE_APP_RESOURCES
+              value: "./benchmark-resources"
+          volumeMounts:
+            - name: benchmark-resources
+              mountPath: /work/benchmark-resources
         - name: lag-analysis
           image: ghcr.io/cau-se/theodolite-slo-checker-lag-trend:theodolite-kotlin-latest
           ports:
           - containerPort: 80
-            name: analysis 
+            name: analysis
+      volumes:
+      - name: benchmark-resources
+        configMap:
+          name: benchmark-resources
+          optional: true
 {{- end }}
diff --git a/execution/helm/values.yaml b/execution/helm/values.yaml
index 5e48ad46b01689c974189f471b0016e45d71f958..67dab74c3931ce13a1ab0f7504a946a208b4dfb8 100644
--- a/execution/helm/values.yaml
+++ b/execution/helm/values.yaml
@@ -24,6 +24,9 @@ grafana:
   # Administrator credentials when not using an existing secret (see below)
   adminUser: admin
   adminPassword: admin
+  grafana.ini:
+    users:
+      default_theme: light
   ## Sidecars that collect the configmaps with specified label and stores the included files them into the respective folders
   ## Requires at least Grafana 5 to work and can't be used together with parameters dashboardProviders, datasources and dashboards
   sidecar:
@@ -145,8 +148,8 @@ kafka-lag-exporter:
   enabled: true
   nodeSelector: {}
   clusters:
-    - name: "my-confluent-cp-kafka"
-      bootstrapBrokers: "my-confluent-cp-kafka:9092"
+    - name: "theodolite-cp-kafka"
+      bootstrapBrokers: "theodolite-cp-kafka:9092"
 
   ## The interval between refreshing metrics
   pollIntervalSeconds: 15
diff --git a/execution/theodolite.yaml b/execution/theodolite.yaml
index 06d14a0f589b2ac7a16ebaaae4d1490b840ea57b..ff8eecb312d052eab6f2e66a0bd57d8a983d38e1 100644
--- a/execution/theodolite.yaml
+++ b/execution/theodolite.yaml
@@ -5,47 +5,60 @@ metadata:
 spec:
   template:
     spec:
-      volumes:
-      - name: theodolite-pv-storage
-        persistentVolumeClaim:
-          claimName: theodolite-pv-claim
+      securityContext:
+        runAsUser: 0 # Set the permissions for write access to the volumes.
       containers:
+        - name: lag-analysis
+          image: ghcr.io/cau-se/theodolite-slo-checker-lag-trend:theodolite-kotlin-latest
+          ports:
+          - containerPort: 80
+            name: analysis
         - name: theodolite
-          image: ghcr.io/cau-se/theodolite:latest
-          # imagePullPolicy: Never # Used to pull "own" local image
+          image: ghcr.io/cau-se/theodolite:theodolite-kotlin-latest
+          imagePullPolicy: Always
           env:
-            - name: UC # mandatory
-              value: "1"
-            - name: LOADS # mandatory
-              value: "100000, 200000"
-            - name: INSTANCES # mandatory
-              value: "1, 2, 3"
-            # - name: DURATION
-            #   value: "5"
-            # - name: PARTITIONS
-            #   value: "40"
-            # - name: DOMAIN_RESTRICTION
-            #   value: "True"
-            # - name: SEARCH_STRATEGY
-            #   value: "linear-search"
-            # - name: CPU_LIMIT
-            #   value: "1000m"
-            # - name: MEMORY_LIMIT
-            #   value: "4Gi"
-            - name: PROMETHEUS_BASE_URL
-              value: "http://prometheus-operated:9090"
-            # - name: NAMESPACE
-            #   value: "default"
-            # - name: CONFIGURATIONS
-            #   value: "COMMIT_INTERVAL_MS=100, NUM_STREAM_THREADS=1"
-            - name: RESULT_PATH
-              value: "results"
-            - name: PYTHONUNBUFFERED # Enable logs in Kubernetes
-              value: "1"
+            - name: NAMESPACE
+              valueFrom:
+                fieldRef:
+                  fieldPath: metadata.namespace
+
+            # - name: MODE
+            #   value: yaml-executor # Default is `yaml-executor`
+            - name: THEODOLITE_EXECUTION
+              value: /etc/execution/example-execution-yaml-resource.yaml # The name of this file must correspond to the filename of the execution, from which the config map is created.
+            - name: THEODOLITE_BENCHMARK
+              value: /etc/benchmark/example-benchmark-yaml-resource.yaml # The name of this file must correspond to the filename of the benchmark, from which the config map is created.
+            - name: THEODOLITE_APP_RESOURCES
+              value: /etc/app-resources
+            - name: RESULTS_FOLDER # Folder for saving results
+              value: results # Default is the pwd (/deployments)
+            # - name: CREATE_RESULTS_FOLDER # Specify whether the specified result folder should be created if it does not exist.
+            #   value: "false" # Default is false.
           volumeMounts:
-            - mountPath: "/app/results"
+            - mountPath: "/deployments/results" # the mounted path must corresponds to the value of `RESULT_FOLDER`.
               name: theodolite-pv-storage
+            - mountPath: "/etc/app-resources" # must be corresponds to the value of `THEODOLITE_APP_RESOURCES`.
+              name: app-resources
+            - mountPath: "/etc/benchmark"  # must be corresponds to the value of `THEODOLITE_BENCHMARK`.
+              name: benchmark
+            - mountPath: "/etc/execution" # must be corresponds to the value of `THEODOLITE_EXECUTION`.
+              name: execution
       restartPolicy: Never
       # Uncomment if RBAC is enabled and configured
-      # serviceAccountName: theodolite
-  backoffLimit: 4
+      serviceAccountName: theodolite
+      # Multiple volumes are needed to provide the corresponding files.
+      # The names must correspond to the created configmaps and the volumeMounts.
+      volumes:
+        - name: theodolite-pv-storage
+          persistentVolumeClaim:
+            claimName: theodolite-pv-claim
+        - name: app-resources
+          configMap:
+            name: app-resources-configmap
+        - name: benchmark
+          configMap:
+            name: benchmark-configmap
+        - name: execution
+          configMap:
+            name: execution-configmap
+  backoffLimit: 4
\ No newline at end of file
diff --git a/execution/uc-application/aggregation-deployment.yaml b/execution/uc-application/aggregation-deployment.yaml
index 07732ca1dd1e6b2b06f098dfb10a53d38e8d5cae..1d3ebdb20dd06433e97e112edef76d7deac17395 100644
--- a/execution/uc-application/aggregation-deployment.yaml
+++ b/execution/uc-application/aggregation-deployment.yaml
@@ -21,9 +21,9 @@ spec:
           name: jmx
         env:
         - name: KAFKA_BOOTSTRAP_SERVERS
-          value: "my-confluent-cp-kafka:9092"
+          value: "theodolite-cp-kafka:9092"
         - name: SCHEMA_REGISTRY_URL
-          value: "http://my-confluent-cp-schema-registry:8081"
+          value: "http://theodolite-cp-schema-registry:8081"
         - name: JAVA_OPTS
           value: "-Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.port=5555"
         - name: COMMIT_INTERVAL_MS # Set as default for the applications
diff --git a/execution/uc-workload-generator/workloadGenerator.yaml b/execution/uc-workload-generator/workloadGenerator.yaml
index 146e285f66d4c0e1a88d613e4ac2d5571234fad6..8f7cc3a8df20752eccb321242bb774c18f4e1d0a 100644
--- a/execution/uc-workload-generator/workloadGenerator.yaml
+++ b/execution/uc-workload-generator/workloadGenerator.yaml
@@ -32,6 +32,6 @@ spec:
         - name: KUBERNETES_DNS_NAME
           value: "titan-ccp-load-generator.$(KUBERNETES_NAMESPACE).svc.cluster.local"
         - name: KAFKA_BOOTSTRAP_SERVERS
-          value: "my-confluent-cp-kafka:9092"
+          value: "theodolite-cp-kafka:9092"
         - name: SCHEMA_REGISTRY_URL
-          value: "http://my-confluent-cp-schema-registry:8081"
+          value: "http://theodolite-cp-schema-registry:8081"
diff --git a/slope-evaluator/README.md b/slope-evaluator/README.md
index 25c02b42e6a6eb4611972febf935403b8b8703c8..5929fb157a7c783bd37497885a5e3bc373b84aa0 100644
--- a/slope-evaluator/README.md
+++ b/slope-evaluator/README.md
@@ -17,10 +17,43 @@ docker build . -t theodolite-evaluator
 Run the Docker image:
 
 ```sh
- docker run -p 80:80 theodolite-evaluator
+docker run -p 80:80 theodolite-evaluator
 ```
 
 ## Configuration
 
 You can set the `HOST` and the `PORT` (and a lot of more parameters) via environment variables. Default is `0.0.0.0:80`.
-For more information see [here](https://github.com/tiangolo/uvicorn-gunicorn-fastapi-docker#advanced-usage).
+For more information see the [Gunicorn/FastAPI Docker docs](https://github.com/tiangolo/uvicorn-gunicorn-fastapi-docker#advanced-usage).
+
+## API Documentation
+
+The running webserver provides a REST API with the following route:
+
+* /evaluate-slope
+    * Method: POST
+    * Body:
+        * total_lag
+        * threshold
+        * warmup
+
+The body of the request must be a JSON string that satisfies the following conditions:
+
+* **total_lag**: This property is based on the [Range Vector type](https://www.prometheus.io/docs/prometheus/latest/querying/api/#range-vectors) from Prometheus and must have the following JSON structure:
+    ```
+        {
+            "metric": {
+                "group": "<label_value>"
+            },
+            "values": [
+                [
+                    <unix_timestamp>,
+                    "<sample_value>"
+                ]
+            ]
+        }
+    ```
+    * The `<label_value>` provided in "metric.group" must be equal to the id of the Kafka consumer group.
+    * The `<unix_timestamp>` provided as the first element of each element in the "values" array must be the timestamp of the measurement value in seconds (with optional decimal precision)
+    * The `<sample_value>` must be the measurement value as string.
+* **threshold**: Must be an unsigned integer that specifies the threshold for the SLO evaluation. The SLO is considered fulfilled, if the result value is below the threshold. If the result value is equal or above the threshold, the SLO is considered not fulfilled.
+* **warmup**: Specifieds the warmup time in seconds that are ignored for evaluating the SLO.
\ No newline at end of file
diff --git a/slope-evaluator/app/main.py b/slope-evaluator/app/main.py
index b3941d1fed0f77d2046199aaaf5a8b7d4edcf680..13df2481a37a965be149317468d85ec597812ef1 100644
--- a/slope-evaluator/app/main.py
+++ b/slope-evaluator/app/main.py
@@ -18,7 +18,7 @@ if os.getenv('LOG_LEVEL') == 'INFO':
 elif os.getenv('LOG_LEVEL') == 'WARNING':
     logger.setLevel(logging.WARNING)
 elif os.getenv('LOG_LEVEL') == 'DEBUG':
-    logger.setLevel((logging.DEBUG))
+    logger.setLevel(logging.DEBUG)
 
 def execute(results, threshold, warmup):
     d = []
@@ -30,18 +30,18 @@ def execute(results, threshold, warmup):
 
     df = pd.DataFrame(d)
 
-    logger.info(df)
+    logger.info("Calculating trend slope with warmup of %s seconds for data frame:\n %s", warmup, df)
     try:
         trend_slope = trend_slope_computer.compute(df, warmup)
     except Exception as e:
-        err_msg = 'Computing trend slope failed'
+        err_msg = 'Computing trend slope failed.'
         logger.exception(err_msg)
-        logger.error('Mark this subexperiment as not successful and continue benchmark')
+        logger.error('Mark this subexperiment as not successful and continue benchmark.')
         return False
 
-    logger.info("Trend Slope: %s", trend_slope)
-
-    return trend_slope < threshold
+    result = trend_slope < threshold
+    logger.info("Computed lag trend slope is '%s'. Result is: %s", trend_slope, result)
+    return result
 
 @app.post("/evaluate-slope",response_model=bool)
 async def evaluate_slope(request: Request):
diff --git a/slope-evaluator/app/trend_slope_computer.py b/slope-evaluator/app/trend_slope_computer.py
index c128d9f48c1e7ba20e43dfbfd6a0391eeec2b60b..51b28f2baa5110a6d64f3adc1ac9a94c6b6f3ce9 100644
--- a/slope-evaluator/app/trend_slope_computer.py
+++ b/slope-evaluator/app/trend_slope_computer.py
@@ -2,13 +2,12 @@ from sklearn.linear_model import LinearRegression
 import pandas as pd
 import os
 
-def compute(x, warmup_sec):
-    input = x
-    input['sec_start'] = input.loc[0:, 'timestamp'] - input.iloc[0]['timestamp']
-    regress = input.loc[input['sec_start'] >= warmup_sec] # Warm-Up
+def compute(data, warmup_sec):
+    data['sec_start'] = data.loc[0:, 'timestamp'] - data.iloc[0]['timestamp']
+    regress = data.loc[data['sec_start'] >= warmup_sec] # Warm-Up
 
-    X = regress.iloc[:, 2].values.reshape(-1, 1)  # values converts it into a numpy array
-    Y = regress.iloc[:, 3].values.reshape(-1, 1)  # -1 means that calculate the dimension of rows, but have 1 column
+    X = regress.iloc[:, 1].values.reshape(-1, 1)  # values converts it into a numpy array
+    Y = regress.iloc[:, 2].values.reshape(-1, 1)  # -1 means that calculate the dimension of rows, but have 1 column
     linear_regressor = LinearRegression()  # create object for the class
     linear_regressor.fit(X, Y)  # perform linear regression
     Y_pred = linear_regressor.predict(X)  # make predictions
diff --git a/theodolite-benchmarks/buildSrc/src/main/groovy/theodolite.flink.gradle b/theodolite-benchmarks/buildSrc/src/main/groovy/theodolite.flink.gradle
index f9c1d98d9f88a95bdc3fa25e7c1bec2f3c9bddb4..d6d5217667a73a1529d73ac59260bcf47d8cf2e1 100644
--- a/theodolite-benchmarks/buildSrc/src/main/groovy/theodolite.flink.gradle
+++ b/theodolite-benchmarks/buildSrc/src/main/groovy/theodolite.flink.gradle
@@ -6,6 +6,23 @@ plugins {
 
 applicationDefaultJvmArgs = ["-Dlog4j.configuration=log4j.properties"]
 
+
+run.classpath = sourceSets.main.runtimeClasspath
+
+jar {
+    manifest {
+        attributes 'Built-By': System.getProperty('user.name'),
+                   'Build-Jdk': System.getProperty('java.version')
+    }
+}
+
+shadowJar {
+    configurations = [project.configurations.compile]
+    zip64 true
+}
+
+tasks.distZip.enabled = false
+
 ext {
   flinkVersion = '1.12.2'
   scalaBinaryVersion = '2.12'
@@ -48,17 +65,3 @@ dependencies {
     // Use JUnit test framework
     testImplementation 'junit:junit:4.12'
 }
-
-run.classpath = sourceSets.main.runtimeClasspath
-
-jar {
-    manifest {
-        attributes 'Built-By': System.getProperty('user.name'),
-                   'Build-Jdk': System.getProperty('java.version')
-    }
-}
-
-shadowJar {
-    configurations = [project.configurations.compile]
-    zip64 true
-}
diff --git a/theodolite-benchmarks/buildSrc/src/main/groovy/theodolite.kstreams.gradle b/theodolite-benchmarks/buildSrc/src/main/groovy/theodolite.kstreams.gradle
index c6779fbc4348a8d665776e68688858ab3d2f4146..eece7b835ae9d6f39283ea371ce8b0b8194cdaa0 100644
--- a/theodolite-benchmarks/buildSrc/src/main/groovy/theodolite.kstreams.gradle
+++ b/theodolite-benchmarks/buildSrc/src/main/groovy/theodolite.kstreams.gradle
@@ -6,6 +6,8 @@ plugins {
   id 'application'
 }
 
+tasks.distZip.enabled = false
+
 repositories {
   jcenter()
   maven {
diff --git a/theodolite-benchmarks/buildSrc/src/main/groovy/theodolite.load-generator.gradle b/theodolite-benchmarks/buildSrc/src/main/groovy/theodolite.load-generator.gradle
index 13b7ea191d11c942cd0ca58b882ffda7bc7912be..c6c2b6057cf35c32faa4d67b6ea6dba9e5c13beb 100644
--- a/theodolite-benchmarks/buildSrc/src/main/groovy/theodolite.load-generator.gradle
+++ b/theodolite-benchmarks/buildSrc/src/main/groovy/theodolite.load-generator.gradle
@@ -6,6 +6,8 @@ plugins {
   id 'application'
 }
 
+tasks.distZip.enabled = false
+
 repositories {
   jcenter()
   maven {
diff --git a/theodolite-quarkus/build.gradle b/theodolite-quarkus/build.gradle
index 7f32c0c5bab25b1d62b6cd9aa6f5db5eb6d78e47..8b61bf506dd241c5b0d4e75b1357ef3fd5966135 100644
--- a/theodolite-quarkus/build.gradle
+++ b/theodolite-quarkus/build.gradle
@@ -18,20 +18,20 @@ dependencies {
     implementation 'org.jetbrains.kotlin:kotlin-stdlib-jdk8'
     implementation 'io.quarkus:quarkus-arc'
     implementation 'io.quarkus:quarkus-resteasy'
-    testImplementation 'io.quarkus:quarkus-junit5'
-    testImplementation 'io.rest-assured:rest-assured'
     implementation 'com.google.code.gson:gson:2.8.5'
-
     implementation 'org.slf4j:slf4j-simple:1.7.29'
     implementation 'io.github.microutils:kotlin-logging:1.12.0'
     implementation 'io.fabric8:kubernetes-client:5.0.0-alpha-2'
     implementation 'io.quarkus:quarkus-kubernetes-client'
     implementation 'org.apache.kafka:kafka-clients:2.7.0'
     implementation 'khttp:khttp:1.0.0'
+
+    testImplementation 'io.quarkus:quarkus-junit5'
+    testImplementation 'io.rest-assured:rest-assured'
 }
 
 group 'theodolite'
-version '1.0.0-SNAPSHOT'
+version '0.5.0-SNAPSHOT'
 
 java {
     sourceCompatibility = JavaVersion.VERSION_11
diff --git a/theodolite-quarkus/config/aggregation-service.yaml b/theodolite-quarkus/config/aggregation-service.yaml
index 916dd6677d60370b1d62e5d7e708c3ee966bda23..85432d04f225c30469f3232153ef6bd72bd02bdf 100644
--- a/theodolite-quarkus/config/aggregation-service.yaml
+++ b/theodolite-quarkus/config/aggregation-service.yaml
@@ -14,4 +14,4 @@ spec:
     targetPort: 80
     protocol: TCP
   - name: metrics
-    port: 9980
+    port: 5556
diff --git a/theodolite-quarkus/config/example-benchmark-yaml-resource.yaml b/theodolite-quarkus/config/example-benchmark-yaml-resource.yaml
index 4d3d6ef395fb2c3fda2154aef2bf5f9ffb1c0ed7..ebc2fe9e44fe303e342cabee301cb63664867cfb 100644
--- a/theodolite-quarkus/config/example-benchmark-yaml-resource.yaml
+++ b/theodolite-quarkus/config/example-benchmark-yaml-resource.yaml
@@ -19,9 +19,13 @@ loadTypes:
         resource: "uc1-load-generator-deployment.yaml"
         container: "workload-generator"
         variableName: "NUM_SENSORS"
+      - type: "NumSensorsLoadGeneratorReplicaPatcher"
+        resource: "uc1-load-generator-deployment.yaml"
 kafkaConfig:
-  bootstrapServer: "localhost:31290" # "my-confluent-cp-kafka:9092"
+  bootstrapServer: "theodolite-cp-kafka:9092"
   topics:
     - name: "input"
       numPartitions: 40
-      replicationFactor: 1
\ No newline at end of file
+      replicationFactor: 1
+    - name: "theodolite-.*"
+      removeOnly: True
\ No newline at end of file
diff --git a/theodolite-quarkus/config/example-execution-yaml-resource.yaml b/theodolite-quarkus/config/example-execution-yaml-resource.yaml
index 80e4575d653bff8d67d8f66cf4ae9806a1fe102e..23c1587ec1e5c2a88fcf69d7127edbcc1ffdb00f 100644
--- a/theodolite-quarkus/config/example-execution-yaml-resource.yaml
+++ b/theodolite-quarkus/config/example-execution-yaml-resource.yaml
@@ -2,36 +2,34 @@ name: example-execution
 benchmark: "uc1-kstreams"
 load:
   loadType: "NumSensors"
-  loadValues:
-    - 50000
+  loadValues: [25000, 50000, 75000, 100000, 125000, 150000]
 resources:
   resourceType: "Instances"
-  resourceValues:
-    - 1
+  resourceValues: [1, 2, 3, 4, 5]
 slos:
   - sloType: "lag trend"
-    threshold: 1000
+    threshold: 2000
     prometheusUrl: "http://prometheus-operated:9090"
     externalSloUrl: "http://localhost:80/evaluate-slope"
     offset: 0
-    warmup: 0
+    warmup: 60 # in seconds
 execution:
   strategy: "LinearSearch"
-  duration: 60
+  duration: 300 # in seconds
   repetitions: 1
   restrictions:
     - "LowerBound"
-configOverrides:
-  - patcher:
-      type: "NodeSelectorPatcher"
-      resource: "uc1-load-generator-deployment.yaml"
-      variableName: "env"
-    value: "prod"
-  - patcher:
-      type: "NodeSelectorPatcher"
-      resource: "uc1-kstreams-deployment.yaml"
-      variableName: "env"
-    value: "prod"
+configOverrides: []
+#  - patcher:
+#      type: "NodeSelectorPatcher"
+#      resource: "uc1-load-generator-deployment.yaml"
+#      variableName: "env"
+#    value: "prod"
+#  - patcher:
+#      type: "NodeSelectorPatcher"
+#      resource: "uc1-kstreams-deployment.yaml"
+#      variableName: "env"
+#    value: "prod"
 #  - patcher:
 #      type: "ResourceLimitPatcher"
 #      resource: "uc1-kstreams-deployment.yaml"
diff --git a/theodolite-quarkus/config/example-operator-benchmark.yaml b/theodolite-quarkus/config/example-operator-benchmark.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..3ed5218d8a8988b130e8d549c120cbca7329ffe3
--- /dev/null
+++ b/theodolite-quarkus/config/example-operator-benchmark.yaml
@@ -0,0 +1,35 @@
+apiVersion: theodolite.com/v1alpha1
+kind: benchmark
+metadata:
+  name: uc1-kstreams
+#name: "uc1-kstreams"
+appResource:
+  - "uc1-kstreams-deployment.yaml"
+  - "aggregation-service.yaml"
+  - "jmx-configmap.yaml"
+  - "uc1-service-monitor.yaml"
+loadGenResource:
+  - "uc1-load-generator-deployment.yaml"
+  - "uc1-load-generator-service.yaml"
+resourceTypes:
+  - typeName: "Instances"
+    patchers:
+      - type: "ReplicaPatcher"
+        resource: "uc1-kstreams-deployment.yaml"
+loadTypes:
+  - typeName: "NumSensors"
+    patchers:
+      - type: "EnvVarPatcher"
+        resource: "uc1-load-generator-deployment.yaml"
+        container: "workload-generator"
+        variableName: "NUM_SENSORS"
+      - 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
new file mode 100644
index 0000000000000000000000000000000000000000..3df1a723dd771453ab1b267335176e4ae74c3ed5
--- /dev/null
+++ b/theodolite-quarkus/config/example-operator-execution.yaml
@@ -0,0 +1,52 @@
+apiVersion: theodolite.com/v1alpha1
+kind: execution
+metadata:
+  name: example-execution
+#name: example-execution
+benchmark: "uc1-kstreams"
+load:
+  loadType: "NumSensors"
+  loadValues: [25000, 50000, 75000, 100000, 125000, 150000]
+resources:
+  resourceType: "Instances"
+  resourceValues: [1, 2, 3, 4, 5]
+slos:
+  - sloType: "lag trend"
+    threshold: 2000
+    prometheusUrl: "http://prometheus-operated:9090"
+    externalSloUrl: "http://localhost:80/evaluate-slope"
+    offset: 0
+    warmup: 60 # in seconds
+execution:
+  strategy: "LinearSearch"
+  duration: 300 # in seconds
+  repetitions: 1
+  restrictions:
+    - "LowerBound"
+configOverrides: []
+#  - patcher:
+#      type: "NodeSelectorPatcher"
+#      resource: "uc1-load-generator-deployment.yaml"
+#      variableName: "env"
+#    value: "prod"
+#  - patcher:
+#      type: "NodeSelectorPatcher"
+#      resource: "uc1-kstreams-deployment.yaml"
+#      variableName: "env"
+#    value: "prod"
+#  - patcher:
+#      type: "ResourceLimitPatcher"
+#      resource: "uc1-kstreams-deployment.yaml"
+#      container: "uc-application"
+#      variableName: "cpu"
+#    value: "1000m"
+#  - patcher:
+#      type: "ResourceLimitPatcher"
+#      resource: "uc1-kstreams-deployment.yaml"
+#      container: "uc-application"
+#      variableName: "memory"
+#    value: "2Gi"
+#  - patcher:
+#      type: "SchedulerNamePatcher"
+#      resource: "uc1-kstreams-deployment.yaml"
+#    value: "random-scheduler"
diff --git a/theodolite-quarkus/config/uc1-kstreams-deployment.yaml b/theodolite-quarkus/config/uc1-kstreams-deployment.yaml
index 85fe243714f9523e760ad827ef697b3dcefdc8e3..171c3446db2719ee91bd8954233015316851fcf9 100644
--- a/theodolite-quarkus/config/uc1-kstreams-deployment.yaml
+++ b/theodolite-quarkus/config/uc1-kstreams-deployment.yaml
@@ -21,9 +21,9 @@ spec:
               name: jmx
           env:
             - name: KAFKA_BOOTSTRAP_SERVERS
-              value: "my-confluent-cp-kafka:9092"
+              value: "theodolite-cp-kafka:9092"
             - name: SCHEMA_REGISTRY_URL
-              value: "http://my-confluent-cp-schema-registry:8081"
+              value: "http://theodolite-cp-schema-registry:8081"
             - name: JAVA_OPTS
               value: "-Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.port=5555"
             - name: COMMIT_INTERVAL_MS # Set as default for the applications
diff --git a/theodolite-quarkus/config/uc1-load-generator-deployment.yaml b/theodolite-quarkus/config/uc1-load-generator-deployment.yaml
index e49c22d6a478fbd7f3f5987222c76ca73a661cb0..374dd60113e133ef0a793149e3786efb38973287 100644
--- a/theodolite-quarkus/config/uc1-load-generator-deployment.yaml
+++ b/theodolite-quarkus/config/uc1-load-generator-deployment.yaml
@@ -31,6 +31,6 @@ spec:
             - name: KUBERNETES_DNS_NAME
               value: "titan-ccp-load-generator.$(KUBERNETES_NAMESPACE).svc.cluster.local"
             - name: KAFKA_BOOTSTRAP_SERVERS
-              value: "my-confluent-cp-kafka:9092"
+              value: "theodolite-cp-kafka:9092"
             - name: SCHEMA_REGISTRY_URL
-              value: "http://my-confluent-cp-schema-registry:8081"
+              value: "http://theodolite-cp-schema-registry:8081"
diff --git a/theodolite-quarkus/src/main/docker/Dockerfile.native b/theodolite-quarkus/src/main/docker/Dockerfile.native
index 37a5a4bd8472a358194dbb14a5fce61df94804d3..29836a7148b573c3051c33341718b06008fa07e2 100644
--- a/theodolite-quarkus/src/main/docker/Dockerfile.native
+++ b/theodolite-quarkus/src/main/docker/Dockerfile.native
@@ -15,12 +15,12 @@
 #
 ###
 FROM registry.access.redhat.com/ubi8/ubi-minimal:8.3
-WORKDIR /work/
-RUN chown 1001 /work \
-    && chmod "g+rwX" /work \
-    && chown 1001:root /work
-COPY --chown=1001:root build/*-runner /work/application
-COPY config/ /work/config/
+WORKDIR /deployments
+RUN chown 1001 /deployments \
+    && chmod "g+rwX" /deployments \
+    && chown 1001:root /deployments
+COPY --chown=1001:root build/*-runner /deployments/application
+COPY config/ /deployments/config/
 
 EXPOSE 8080
 USER 1001
diff --git a/theodolite-quarkus/src/main/kotlin/theodolite/benchmark/Benchmark.kt b/theodolite-quarkus/src/main/kotlin/theodolite/benchmark/Benchmark.kt
index df92ed111206bb58f50a589a612d6f598575244b..db7999b205c61d94fa17791a5d549a2620601b6b 100644
--- a/theodolite-quarkus/src/main/kotlin/theodolite/benchmark/Benchmark.kt
+++ b/theodolite-quarkus/src/main/kotlin/theodolite/benchmark/Benchmark.kt
@@ -5,8 +5,19 @@ import theodolite.util.ConfigurationOverride
 import theodolite.util.LoadDimension
 import theodolite.util.Resource
 
+/**
+ * A Benchmark contains:
+ * - The [Resource]s that can be scaled for the benchmark.
+ * - The [LoadDimension]s that can be scaled the benchmark.
+ * - additional [ConfigurationOverride]s.
+ */
 @RegisterForReflection
 interface Benchmark {
+
+    /**
+     * Builds a Deployment that can be deployed.
+     * @return a BenchmarkDeployment.
+     */
     fun buildDeployment(
         load: LoadDimension,
         res: Resource,
diff --git a/theodolite-quarkus/src/main/kotlin/theodolite/benchmark/BenchmarkDeployment.kt b/theodolite-quarkus/src/main/kotlin/theodolite/benchmark/BenchmarkDeployment.kt
index df01fae2ece683a9bec6ad0d8489987f94249375..92d3f7a012517895fc61531026e4ea4f3e3cfb50 100644
--- a/theodolite-quarkus/src/main/kotlin/theodolite/benchmark/BenchmarkDeployment.kt
+++ b/theodolite-quarkus/src/main/kotlin/theodolite/benchmark/BenchmarkDeployment.kt
@@ -1,6 +1,20 @@
 package theodolite.benchmark
 
+/**
+ *  A BenchmarkDeployment contains the necessary infrastructure to execute a benchmark.
+ *  Therefore it has the capabilities to set up the deployment of a benchmark and to tear it down.
+ */
 interface BenchmarkDeployment {
+
+    /**
+     * Setup a benchmark. This method is responsible for deploying the resources
+     * and organize the needed infrastructure.
+     */
     fun setup()
+
+    /**
+     *  Tears down a benchmark. This method is responsible for deleting the deployed
+     *  resources and to reset the used infrastructure.
+     */
     fun teardown()
 }
diff --git a/theodolite-quarkus/src/main/kotlin/theodolite/benchmark/BenchmarkExecution.kt b/theodolite-quarkus/src/main/kotlin/theodolite/benchmark/BenchmarkExecution.kt
index a2d649ab38f4c0fda31d7c99d687cc253efe1ea8..2d5d15b3389cf723be3a8ceb0fff8b27bd700419 100644
--- a/theodolite-quarkus/src/main/kotlin/theodolite/benchmark/BenchmarkExecution.kt
+++ b/theodolite-quarkus/src/main/kotlin/theodolite/benchmark/BenchmarkExecution.kt
@@ -8,9 +8,26 @@ import io.quarkus.runtime.annotations.RegisterForReflection
 import theodolite.util.ConfigurationOverride
 import kotlin.properties.Delegates
 
+/**
+ * This class represents the configuration for an execution of a benchmark.
+ * An example for this is the BenchmarkExecution.yaml
+ * A BenchmarkExecution consists of:
+ *  - A [name].
+ *  - The [benchmark] that should be executed.
+ *  - The [load] that should be checked in the benchmark.
+ *  - The [resources] that should be checked in the benchmark.
+ *  - A list of [slos] that are used for the evaluation of the experiments.
+ *  - An [execution] that encapsulates: the strategy, the duration, and the restrictions
+ *  for the execution of the benchmark.
+ *  - [configOverrides] additional configurations.
+ *  This class is used for parsing(in [theodolite.execution.TheodoliteYamlExecutor]) and
+ *  for the deserializing in the [theodolite.execution.operator.TheodoliteOperator].
+ *  @constructor construct an empty BenchmarkExecution.
+ */
 @JsonDeserialize
 @RegisterForReflection
 class BenchmarkExecution : CustomResource(), Namespaced {
+    var executionId: Int = 0
     lateinit var name: String
     lateinit var benchmark: String
     lateinit var load: LoadDefinition
@@ -19,6 +36,10 @@ class BenchmarkExecution : CustomResource(), Namespaced {
     lateinit var execution: Execution
     lateinit var configOverrides: List<ConfigurationOverride?>
 
+    /**
+     * This execution encapsulates the [strategy], the [duration], the [repetitions], and the [restrictions]
+     *  which are used for the concrete benchmark experiments.
+     */
     @JsonDeserialize
     @RegisterForReflection
     class Execution : KubernetesResource {
@@ -28,6 +49,15 @@ class BenchmarkExecution : CustomResource(), Namespaced {
         lateinit var restrictions: List<String>
     }
 
+    /**
+     * Measurable metric.
+     * [sloType] determines the type of the metric.
+     * It is evaluated using the [theodolite.evaluation.ExternalSloChecker] by data measured by Prometheus.
+     * The evaluation checks if a [threshold] is reached or not.
+     * [offset] determines the shift in hours by which the start and end timestamps should be shifted.
+     * The [warmup] determines after which time the metric should be evaluated to avoid starting interferences.
+     * The [warmup] time unit depends on the Slo: for the lag trend it is in seconds.
+     */
     @JsonDeserialize
     @RegisterForReflection
     class Slo : KubernetesResource {
@@ -39,7 +69,10 @@ class BenchmarkExecution : CustomResource(), Namespaced {
         var warmup by Delegates.notNull<Int>()
     }
 
-
+    /**
+     * Represents a Load that should be created and checked.
+     * It can be set to [loadValues].
+     */
     @JsonDeserialize
     @RegisterForReflection
     class LoadDefinition : KubernetesResource {
@@ -47,6 +80,9 @@ class BenchmarkExecution : CustomResource(), Namespaced {
         lateinit var loadValues: List<Int>
     }
 
+    /**
+     * Represents a resource that can be scaled to [resourceValues].
+     */
     @JsonDeserialize
     @RegisterForReflection
     class ResourceDefinition : KubernetesResource {
diff --git a/theodolite-quarkus/src/main/kotlin/theodolite/benchmark/KafkaLagExporterRemover.kt b/theodolite-quarkus/src/main/kotlin/theodolite/benchmark/KafkaLagExporterRemover.kt
index 2b86c51ae87c7b26609683b68bf6cfc12003efc8..e8179b42d40e40e7ed45a8f5c48fe26f235be334 100644
--- a/theodolite-quarkus/src/main/kotlin/theodolite/benchmark/KafkaLagExporterRemover.kt
+++ b/theodolite-quarkus/src/main/kotlin/theodolite/benchmark/KafkaLagExporterRemover.kt
@@ -5,8 +5,16 @@ import mu.KotlinLogging
 
 private val logger = KotlinLogging.logger {}
 
+/**
+ * Used to reset the KafkaLagExporter by deleting the pod.
+ * @param client NamespacedKubernetesClient used for the deletion.
+ */
 class KafkaLagExporterRemover(private val client: NamespacedKubernetesClient) {
 
+    /**
+     * Deletes all pods with the selected label.
+     * @param [label] of the pod that should be deleted.
+     */
     fun remove(label: String) {
         this.client.pods().withLabel(label).delete()
         logger.info { "Pod with label: $label deleted" }
diff --git a/theodolite-quarkus/src/main/kotlin/theodolite/benchmark/KubernetesBenchmark.kt b/theodolite-quarkus/src/main/kotlin/theodolite/benchmark/KubernetesBenchmark.kt
index a75cd96efdb1be3999bf040f0a7fc0b1d8bb739e..bbcb8a957fb2f04ca678b231a878be0a23d46748 100644
--- a/theodolite-quarkus/src/main/kotlin/theodolite/benchmark/KubernetesBenchmark.kt
+++ b/theodolite-quarkus/src/main/kotlin/theodolite/benchmark/KubernetesBenchmark.kt
@@ -11,8 +11,25 @@ import theodolite.patcher.PatcherFactory
 import theodolite.util.*
 
 private val logger = KotlinLogging.logger {}
+
 private var DEFAULT_NAMESPACE = "default"
 
+/**
+ * Represents a benchmark in Kubernetes. An example for this is the BenchmarkType.yaml
+ * Contains a of:
+ * - [name] of the benchmark,
+ * - [appResource] list of the resources that have to be deployed for the benchmark,
+ * - [loadGenResource] resource that generates the load,
+ * - [resourceTypes] types of scaling resources,
+ * - [loadTypes] types of loads that can be scaled for the benchmark,
+ * - [kafkaConfig] for the [TopicManager],
+ * - [namespace] for the client,
+ * - [path] under which the resource yamls can be found.
+ *
+ *  This class is used for the parsing(in the [theodolite.execution.TheodoliteYamlExecutor]) and
+ *  for the deserializing in the [theodolite.execution.operator.TheodoliteOperator].
+ * @constructor construct an empty Benchmark.
+ */
 @RegisterForReflection
 class KubernetesBenchmark : Benchmark, CustomResource(), Namespaced {
     lateinit var name: String
@@ -24,9 +41,13 @@ class KubernetesBenchmark : Benchmark, CustomResource(), Namespaced {
     private val namespace = System.getenv("NAMESPACE") ?: DEFAULT_NAMESPACE
     var path = System.getenv("THEODOLITE_APP_RESOURCES") ?: "./config"
 
+    /**
+     * Loads [KubernetesResource]s.
+     * It first loads them via the [YamlParser] to check for their concrete type and afterwards initializes them using
+     * the [K8sResourceLoader]
+     */
     private fun loadKubernetesResources(resources: List<String>): List<Pair<String, KubernetesResource>> {
         val parser = YamlParser()
-
         val loader = K8sResourceLoader(DefaultKubernetesClient().inNamespace(namespace))
         return resources
             .map { resource ->
@@ -37,6 +58,15 @@ class KubernetesBenchmark : Benchmark, CustomResource(), Namespaced {
             }
     }
 
+    /**
+     * Builds a deployment.
+     * First loads all required resources and then patches them to the concrete load and resources for the experiment.
+     * Afterwards patches additional configurations(cluster depending) into the resources.
+     * @param load concrete load that will be benchmarked in this experiment.
+     * @param res concrete resoruce that will be scaled for this experiment.
+     * @param configurationOverrides
+     * @return a [BenchmarkDeployment]
+     */
     override fun buildDeployment(
         load: LoadDimension,
         res: Resource,
@@ -66,7 +96,7 @@ class KubernetesBenchmark : Benchmark, CustomResource(), Namespaced {
             namespace = namespace,
             resources = resources.map { r -> r.second },
             kafkaConfig = hashMapOf("bootstrap.servers" to kafkaConfig.bootstrapServer),
-            topics = kafkaConfig.getKafkaTopics(),
+            topics = kafkaConfig.topics,
             client = DefaultKubernetesClient().inNamespace(namespace)
         )
     }
diff --git a/theodolite-quarkus/src/main/kotlin/theodolite/benchmark/KubernetesBenchmarkDeployment.kt b/theodolite-quarkus/src/main/kotlin/theodolite/benchmark/KubernetesBenchmarkDeployment.kt
index 8e4fce681147ffe7c80b37bab1d0dbb094b4036e..a6bf881d6ded7b0936b400a37b572c77c95bb241 100644
--- a/theodolite-quarkus/src/main/kotlin/theodolite/benchmark/KubernetesBenchmarkDeployment.kt
+++ b/theodolite-quarkus/src/main/kotlin/theodolite/benchmark/KubernetesBenchmarkDeployment.kt
@@ -3,34 +3,60 @@ package theodolite.benchmark
 import io.fabric8.kubernetes.api.model.KubernetesResource
 import io.fabric8.kubernetes.client.NamespacedKubernetesClient
 import io.quarkus.runtime.annotations.RegisterForReflection
+import mu.KotlinLogging
 import org.apache.kafka.clients.admin.NewTopic
 import theodolite.k8s.K8sManager
 import theodolite.k8s.TopicManager
+import theodolite.util.KafkaConfig
 
+private val logger = KotlinLogging.logger {}
+
+/**
+ * Organizes the deployment of benchmarks in Kubernetes.
+ *
+ * @param namespace to operate in.
+ * @param resources List of [KubernetesResource] that are managed.
+ * @param kafkaConfig for the organization of Kafka topics.
+ * @param topics List of topics that are created or deleted.
+ */
 @RegisterForReflection
 class KubernetesBenchmarkDeployment(
     val namespace: String,
     val resources: List<KubernetesResource>,
     private val kafkaConfig: HashMap<String, Any>,
-    private val topics: Collection<NewTopic>,
+    private val topics: List<KafkaConfig.TopicWrapper>,
     private val client: NamespacedKubernetesClient
 ) : BenchmarkDeployment {
     private val kafkaController = TopicManager(this.kafkaConfig)
     private val kubernetesManager = K8sManager(client)
-    private val LABEL = "app.kubernetes.io/name=kafka-lag-exporter"
+    private val LAG_EXPORTER_POD_LABEL = "app.kubernetes.io/name=kafka-lag-exporter"
+    private val SLEEP_AFTER_TEARDOWN = 5000L
 
+    /**
+     * Setup a [KubernetesBenchmark] using the [TopicManager] and the [K8sManager]:
+     *  - Create the needed topics.
+     *  - Deploy the needed resources.
+     */
     override fun setup() {
-        kafkaController.createTopics(this.topics)
-        resources.forEach {
-            kubernetesManager.deploy(it)
-        }
+        val kafkaTopics = this.topics.filter { !it.removeOnly }
+            .map{ NewTopic(it.name, it.numPartitions, it.replicationFactor) }
+        kafkaController.createTopics(kafkaTopics)
+        resources.forEach { kubernetesManager.deploy(it) }
     }
 
+    /**
+     * Tears down a [KubernetesBenchmark]:
+     *  - Reset the Kafka Lag Exporter.
+     *  - Remove the used topics.
+     *  - Remove the [KubernetesResource]s.
+     */
     override fun teardown() {
-        KafkaLagExporterRemover(client).remove(LABEL)
-        kafkaController.removeTopics(this.topics.map { topic -> topic.name() })
         resources.forEach {
             kubernetesManager.remove(it)
         }
+        kafkaController.removeTopics(this.topics.map { topic -> topic.name })
+        KafkaLagExporterRemover(client).remove(LAG_EXPORTER_POD_LABEL)
+        logger.info { "Teardown complete. Wait $SLEEP_AFTER_TEARDOWN ms to let everything come down." }
+        Thread.sleep(SLEEP_AFTER_TEARDOWN)
     }
 }
diff --git a/theodolite-quarkus/src/main/kotlin/theodolite/evaluation/AnalysisExecutor.kt b/theodolite-quarkus/src/main/kotlin/theodolite/evaluation/AnalysisExecutor.kt
index 2fbe81e94232f379834174df2b05b177808088eb..05687a7d6eaca925a68cc618063bbf3a49d66a12 100644
--- a/theodolite-quarkus/src/main/kotlin/theodolite/evaluation/AnalysisExecutor.kt
+++ b/theodolite-quarkus/src/main/kotlin/theodolite/evaluation/AnalysisExecutor.kt
@@ -4,30 +4,60 @@ import mu.KotlinLogging
 import theodolite.benchmark.BenchmarkExecution
 import theodolite.util.LoadDimension
 import theodolite.util.Resource
+import java.text.Normalizer
 import java.time.Duration
 import java.time.Instant
+import java.util.*
+import java.util.regex.Pattern
 
 private val logger = KotlinLogging.logger {}
 
-class AnalysisExecutor(private val slo: BenchmarkExecution.Slo) {
+/**
+ * Contains the analysis. Fetches a metric from Prometheus, documents it, and evaluates it.
+ * @param slo Slo that is used for the analysis.
+ */
+class AnalysisExecutor(
+    private val slo: BenchmarkExecution.Slo,
+    private val executionId: Int
+) {
 
     private val fetcher = MetricFetcher(
         prometheusURL = slo.prometheusUrl,
         offset = Duration.ofHours(slo.offset.toLong())
     )
 
+    /**
+     *  Analyses an experiment via prometheus data.
+     *  First fetches data from prometheus, then documents them and afterwards evaluate it via a [slo].
+     *  @param load of the experiment.
+     *  @param res of the experiment.
+     *  @param executionDuration of the experiment.
+     *  @return true if the experiment succeeded.
+     */
     fun analyze(load: LoadDimension, res: Resource, executionIntervals: List<Pair<Instant, Instant>>): Boolean {
         var result = false
         val exporter = CsvExporter()
-        val prometheusData = executionIntervals.map { interval -> fetcher.fetchMetric( start = interval.first, end = interval.second, query = "sum by(group)(kafka_consumergroup_group_lag >= 0)") }
         var repetitionCounter = 1
-        prometheusData.forEach{ data -> exporter.toCsv(name = "${load.get()}_${res.get()}_${slo.sloType}_rep_${repetitionCounter++}", prom = data) }
-        prometheusData.forEach { logger.info { "prom-data: $it" }}
 
         try {
+            var resultsFolder: String = System.getenv("RESULTS_FOLDER")
+            if (resultsFolder.isNotEmpty()){
+                resultsFolder += "/"
+            }
+
+            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}_rep_${repetitionCounter++}", prom = data) }
+
 
             val sloChecker = SloCheckerFactory().create(
-                slotype = slo.sloType,
+                sloType = slo.sloType,
                 externalSlopeURL = slo.externalSloUrl,
                 threshold = slo.threshold,
                 warmup = slo.warmup
@@ -36,8 +66,18 @@ class AnalysisExecutor(private val slo: BenchmarkExecution.Slo) {
             result = sloChecker.evaluate(prometheusData)
 
         } catch (e: Exception) {
-            logger.error { "Evaluation failed for resource: ${res.get()} and load: ${load.get()} error: $e" }
+            logger.error { "Evaluation failed for resource '${res.get()}' and load '${load.get()}'. Error: $e" }
         }
         return result
     }
+
+    private val NONLATIN: Pattern = Pattern.compile("[^\\w-]")
+    private val WHITESPACE: Pattern = Pattern.compile("[\\s]")
+
+    fun String.toSlug(): String {
+        val noWhitespace: String = WHITESPACE.matcher(this).replaceAll("-")
+        val normalized: String = Normalizer.normalize(noWhitespace, Normalizer.Form.NFD)
+        val slug: String = NONLATIN.matcher(normalized).replaceAll("")
+        return slug.toLowerCase(Locale.ENGLISH)
+    }
 }
diff --git a/theodolite-quarkus/src/main/kotlin/theodolite/evaluation/CsvExporter.kt b/theodolite-quarkus/src/main/kotlin/theodolite/evaluation/CsvExporter.kt
index e2f536af6dba838b2b4027d6cfafb032ebd3d04d..4ef78cd58085c4ff5fed1477fa08ae2e6342aa66 100644
--- a/theodolite-quarkus/src/main/kotlin/theodolite/evaluation/CsvExporter.kt
+++ b/theodolite-quarkus/src/main/kotlin/theodolite/evaluation/CsvExporter.kt
@@ -8,26 +8,32 @@ 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 Csv
+     * 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("name", "time", "value").joinToString())
+            pw.println(listOf("group", "timestamp", "value").joinToString())
             responseArray.forEach {
                 pw.println(it.joinToString())
             }
         }
-        logger.info { "Wrote csv file: $name to ${csvOutputFile.absolutePath}" }
+        logger.info { "Wrote CSV file: $name to ${csvOutputFile.absolutePath}." }
     }
 
     /**
-     * Converts a PrometheusResponse into a List of List of Strings
+     * 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()
@@ -35,9 +41,11 @@ class CsvExporter {
         val dataList = mutableListOf<List<String>>()
 
         if (values != null) {
-            for (x in values) {
-                val y = x as List<*>
-                dataList.add(listOf(name, "${y[0]}", "${y[1]}"))
+            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/evaluation/ExternalSloChecker.kt b/theodolite-quarkus/src/main/kotlin/theodolite/evaluation/ExternalSloChecker.kt
index e1952735937fc1c7e7e4f69d6a07aaa70ba3e0b8..f7ebee8faf740583dbe6a37381a599e9bde19280 100644
--- a/theodolite-quarkus/src/main/kotlin/theodolite/evaluation/ExternalSloChecker.kt
+++ b/theodolite-quarkus/src/main/kotlin/theodolite/evaluation/ExternalSloChecker.kt
@@ -7,23 +7,38 @@ import theodolite.util.PrometheusResponse
 import java.net.ConnectException
 import java.time.Instant
 
+/**
+ * [SloChecker] that uses an external source for the concrete evaluation.
+ * @param externalSlopeURL The url under which the external evaluation can be reached.
+ * @param threshold threshold that should not be exceeded to evaluate to true.
+ * @param warmup time that is not taken into consideration for the evaluation.
+ */
 class ExternalSloChecker(
     private val externalSlopeURL: String,
     private val threshold: Int,
     private val warmup: Int
-) :
-    SloChecker {
+) : SloChecker {
 
     private val RETRIES = 2
     private val TIMEOUT = 60.0
 
     private val logger = KotlinLogging.logger {}
 
+    /**
+     * Evaluates an experiment using an external service.
+     * Will try to reach the external service until success or [RETRIES] times.
+     * Each request will timeout after [TIMEOUT].
+     *
+     * @param start point of the experiment.
+     * @param end point of the experiment.
+     * @param fetchedData that should be evaluated
+     * @return true if the experiment was successful(the threshold was not exceeded.
+     * @throws ConnectException if the external service could not be reached.
+     */
     override fun evaluate(fetchedData: List<PrometheusResponse>): Boolean {
         var counter = 0
-
         val data = Gson().toJson(mapOf(
-            "total_lags" to fetchedData.map { it.data?.result },
+            "total_lags" to fetchedData.map { it.data?.result},
             "threshold" to threshold,
             "warmup" to warmup))
 
@@ -31,12 +46,14 @@ class ExternalSloChecker(
             val result = post(externalSlopeURL, data = data, timeout = TIMEOUT)
             if (result.statusCode != 200) {
                 counter++
-                logger.error { "Could not reach external slope analysis" }
+                logger.error { "Could not reach external SLO checker" }
             } else {
-                return result.text.toBoolean()
+                val booleanResult = result.text.toBoolean()
+                logger.info { "SLO checker result is: $booleanResult" }
+                return booleanResult
             }
         }
 
-        throw ConnectException("Could not reach slope evaluation")
+        throw ConnectException("Could not reach external SLO checker")
     }
 }
diff --git a/theodolite-quarkus/src/main/kotlin/theodolite/evaluation/MetricFetcher.kt b/theodolite-quarkus/src/main/kotlin/theodolite/evaluation/MetricFetcher.kt
index 19a8bbe9ba0bdd8a694eb37b9db42de6fdf3d620..833d7d1e16c2fbc91b58817b319a7d02af7f5b2b 100644
--- a/theodolite-quarkus/src/main/kotlin/theodolite/evaluation/MetricFetcher.kt
+++ b/theodolite-quarkus/src/main/kotlin/theodolite/evaluation/MetricFetcher.kt
@@ -11,10 +11,26 @@ import java.time.Instant
 
 private val logger = KotlinLogging.logger {}
 
+/**
+ * Used to fetch metrics from Prometheus.
+ * @param prometheusURL URL to the Prometheus server.
+ * @param offset Duration of time that the start and end points of the queries
+ * should be shifted. (for different timezones, etc..)
+ */
 class MetricFetcher(private val prometheusURL: String, private val offset: Duration) {
     private val RETRIES = 2
     private val TIMEOUT = 60.0
 
+    /**
+     * Tries to fetch a metric by a query to a Prometheus server.
+     * Retries to fetch the metric [RETRIES] times.
+     * Connects to the server via [prometheusURL].
+     *
+     * @param start start point of the query.
+     * @param end end point of the query.
+     * @param query query for the prometheus server.
+     * @throws ConnectException - if the prometheus server timed out/was not reached.
+     */
     fun fetchMetric(start: Instant, end: Instant, query: String): PrometheusResponse {
 
         val offsetStart = start.minus(offset)
@@ -32,20 +48,25 @@ class MetricFetcher(private val prometheusURL: String, private val offset: Durat
             val response = get("$prometheusURL/api/v1/query_range", params = parameter, timeout = TIMEOUT)
             if (response.statusCode != 200) {
                 val message = response.jsonObject.toString()
-                logger.warn { "Could not connect to Prometheus: $message, retrying now" }
+                logger.warn { "Could not connect to Prometheus: $message. Retrying now." }
                 counter++
             } else {
                 val values = parseValues(response)
                 if (values.data?.result.isNullOrEmpty()) {
-                    logger.error { "Empty query result: $values between $start and $end for querry $query" }
+                    logger.error { "Empty query result: $values between $start and $end for query $query." }
                     throw NoSuchFieldException()
                 }
                 return parseValues(response)
             }
         }
-        throw ConnectException("No answer from Prometheus received")
+        throw ConnectException("No answer from Prometheus received.")
     }
 
+    /**
+     * Deserializes a response from Prometheus.
+     * @param values Response from Prometheus.
+     * @return a [PrometheusResponse]
+     */
     private fun parseValues(values: Response): PrometheusResponse {
         return Gson().fromJson<PrometheusResponse>(
             values.jsonObject.toString(),
diff --git a/theodolite-quarkus/src/main/kotlin/theodolite/evaluation/SloChecker.kt b/theodolite-quarkus/src/main/kotlin/theodolite/evaluation/SloChecker.kt
index d3948533d21ef195007e481de34416b578b52526..6d64c37f980e98b360e5cdb0dc5130dbfa2aefbe 100644
--- a/theodolite-quarkus/src/main/kotlin/theodolite/evaluation/SloChecker.kt
+++ b/theodolite-quarkus/src/main/kotlin/theodolite/evaluation/SloChecker.kt
@@ -3,6 +3,19 @@ package theodolite.evaluation
 import theodolite.util.PrometheusResponse
 import java.time.Instant
 
+/**
+ * A SloChecker can be used to evaluate data from Promehteus.
+ * @constructor Creates an empty SloChecker
+ */
 interface SloChecker {
+    /**
+     * Evaluates [fetchedData] and returns if the experiment was successful.
+     * Returns if the evaluated experiment was successful.
+     *
+     * @param start of the experiment
+     * @param end of the experiment
+     * @param fetchedData from Prometheus that will be evaluated.
+     * @return true if experiment was successful. Otherwise false.
+     */
     fun evaluate(fetchedData: List<PrometheusResponse>): Boolean
 }
diff --git a/theodolite-quarkus/src/main/kotlin/theodolite/evaluation/SloCheckerFactory.kt b/theodolite-quarkus/src/main/kotlin/theodolite/evaluation/SloCheckerFactory.kt
index 50b7b0aec3c5d48146d4f9423b06fe62f55e3c56..20c421acdfcd76f5d2ebc2ab2c30142bcca3841a 100644
--- a/theodolite-quarkus/src/main/kotlin/theodolite/evaluation/SloCheckerFactory.kt
+++ b/theodolite-quarkus/src/main/kotlin/theodolite/evaluation/SloCheckerFactory.kt
@@ -1,23 +1,36 @@
 package theodolite.evaluation
 
-import java.time.Duration
-
+/**
+ * Factory used to potentially create different [SloChecker]s.
+ * Supports: lag type.
+ */
 class SloCheckerFactory {
 
+    /**
+     * Creates different [SloChecker]s.
+     * Supports: lag type.
+     *
+     * @param sloType Type of the [SloChecker].
+     * @param externalSlopeURL Url to the concrete [SloChecker].
+     * @param threshold for the [SloChecker].
+     * @param warmup for the [SloChecker].
+     *
+     * @return A [SloChecker]
+     * @throws IllegalArgumentException If [sloType] not supported.
+     */
     fun create(
-        slotype: String,
+        sloType: String,
         externalSlopeURL: String,
         threshold: Int,
         warmup: Int
     ): SloChecker {
-
-        return when (slotype) {
+        return when (sloType) {
             "lag trend" -> ExternalSloChecker(
                 externalSlopeURL = externalSlopeURL,
                 threshold = threshold,
                 warmup = warmup
             )
-            else -> throw IllegalArgumentException("Slotype $slotype not found.")
+            else -> throw IllegalArgumentException("Slotype $sloType not found.")
         }
     }
 }
diff --git a/theodolite-quarkus/src/main/kotlin/theodolite/execution/BenchmarkExecutor.kt b/theodolite-quarkus/src/main/kotlin/theodolite/execution/BenchmarkExecutor.kt
index f76dc72df1d9dbfaff219dae85d9797ac45ac71e..4aa018b6c5f3d451b4c572c5fa1cc7b32e42b531 100644
--- a/theodolite-quarkus/src/main/kotlin/theodolite/execution/BenchmarkExecutor.kt
+++ b/theodolite-quarkus/src/main/kotlin/theodolite/execution/BenchmarkExecutor.kt
@@ -26,17 +26,20 @@ abstract class BenchmarkExecutor(
     val executionDuration: Duration,
     configurationOverrides: List<ConfigurationOverride?>,
     val slo: BenchmarkExecution.Slo,
-    val repetitions: Int
+    val repetitions: Int,
+    val executionId: Int
 ) {
 
     var run: AtomicBoolean = AtomicBoolean(true)
 
     /**
-     * Run a experiment for the given parametrization, evaluate the experiment and save the result.
+     * Run a experiment for the given parametrization, evaluate the
+     * experiment and save the result.
      *
      * @param load load to be tested.
      * @param res resources to be tested.
-     * @return True, if the number of resources are suitable for the given load, false otherwise.
+     * @return True, if the number of resources are suitable for the
+     *     given load, false otherwise.
      */
     abstract fun runExperiment(load: LoadDimension, res: Resource): Boolean
     
@@ -45,7 +48,7 @@ abstract class BenchmarkExecutor(
      *
      */
     fun waitAndLog() {
-        logger.info { "Execution of a new benchmark started." }
+        logger.info { "Execution of a new experiment started." }
 
         var secondsRunning = 0L
 
diff --git a/theodolite-quarkus/src/main/kotlin/theodolite/execution/BenchmarkExecutorImpl.kt b/theodolite-quarkus/src/main/kotlin/theodolite/execution/BenchmarkExecutorImpl.kt
index e16fd0e6b4e80ab65063490f2efdc35244f5c502..5afce3c9d78da6db9a52e6545bd10fae0eac8d0a 100644
--- a/theodolite-quarkus/src/main/kotlin/theodolite/execution/BenchmarkExecutorImpl.kt
+++ b/theodolite-quarkus/src/main/kotlin/theodolite/execution/BenchmarkExecutorImpl.kt
@@ -18,8 +18,11 @@ class BenchmarkExecutorImpl(
     executionDuration: Duration,
     private val configurationOverrides: List<ConfigurationOverride?>,
     slo: BenchmarkExecution.Slo,
-    repetitions: Int
-) : BenchmarkExecutor(benchmark, results, executionDuration, configurationOverrides, slo, repetitions) {
+    repetitions: Int,
+    executionId: Int
+
+) : BenchmarkExecutor(benchmark, results, executionDuration, configurationOverrides, slo, repetitions, executionId) {
+
     override fun runExperiment(load: LoadDimension, res: Resource): Boolean {
         var result = false
         val executionIntervals: MutableList<Pair<Instant, Instant>> = ArrayList(repetitions)
@@ -33,9 +36,15 @@ class BenchmarkExecutorImpl(
             }
         }
 
+        /**
+         * Analyse the experiment, if [run] is true, otherwise the experiment was canceled by the user.
+         */
         if (this.run.get()) {
-            result =AnalysisExecutor(slo = slo)
-                    .analyze(load = load, res = res, executionIntervals = executionIntervals)
+            result =AnalysisExecutor(slo = slo, executionId = executionId)
+                    .analyze(
+                        load = load,
+                        res = res,
+                        executionIntervals = executionIntervals)
             this.results.setResult(Pair(load, res), result)
         }
         return result
@@ -53,7 +62,12 @@ class BenchmarkExecutorImpl(
             this.run.set(false)
         }
         val to = Instant.now()
-        benchmarkDeployment.teardown()
+        try {
+            benchmarkDeployment.teardown()
+        } catch (e: Exception) {
+            logger.warn { "Error while tearing down the benchmark deployment." }
+            logger.debug { "Teardown failed, caused by: $e" }
+        }
         return Pair(from,to)
     }
 }
diff --git a/theodolite-quarkus/src/main/kotlin/theodolite/execution/Main.kt b/theodolite-quarkus/src/main/kotlin/theodolite/execution/Main.kt
index 4518ef7957104819b26eae95cf4e6e9b35c4e995..64a40c0b11854d61900ab1fde3797e17427cac15 100644
--- a/theodolite-quarkus/src/main/kotlin/theodolite/execution/Main.kt
+++ b/theodolite-quarkus/src/main/kotlin/theodolite/execution/Main.kt
@@ -13,13 +13,17 @@ object Main {
     @JvmStatic
     fun main(args: Array<String>) {
 
-        val mode = System.getenv("MODE") ?: "yaml-executor"
+        val mode = System.getenv("MODE") ?: "standalone"
         logger.info { "Start Theodolite with mode $mode" }
 
         when(mode) {
-           "yaml-executor" -> TheodoliteYamlExecutor().start()
+            "standalone" -> TheodoliteYamlExecutor().start()
+            "yaml-executor" -> TheodoliteYamlExecutor().start() // TODO remove (#209)
             "operator" -> TheodoliteOperator().start()
-            else ->  {logger.error { "MODE $mode not found" }; exitProcess(1)}
+            else ->  {
+                logger.error { "MODE $mode not found" }
+                exitProcess(1)
+            }
         }
     }
 }
\ No newline at end of file
diff --git a/theodolite-quarkus/src/main/kotlin/theodolite/execution/Shutdown.kt b/theodolite-quarkus/src/main/kotlin/theodolite/execution/Shutdown.kt
index 92aaef2923ed87e2bb2c3706ac4fc862538a222f..a50a38e79b52a72fa68eb9eda70cf1072f80df74 100644
--- a/theodolite-quarkus/src/main/kotlin/theodolite/execution/Shutdown.kt
+++ b/theodolite-quarkus/src/main/kotlin/theodolite/execution/Shutdown.kt
@@ -5,13 +5,26 @@ import theodolite.benchmark.BenchmarkExecution
 import theodolite.benchmark.KubernetesBenchmark
 import theodolite.util.LoadDimension
 import theodolite.util.Resource
+import java.lang.Exception
 
 private val logger = KotlinLogging.logger {}
 
-class Shutdown(private val benchmarkExecution: BenchmarkExecution, private val benchmark: KubernetesBenchmark) {
+/**
+ * This Shutdown Hook can be used to delete all Kubernetes resources which are related to the given execution and benchmark.
+ *
+ * @property benchmarkExecution
+ * @property benchmark
+ */
+class Shutdown(private val benchmarkExecution: BenchmarkExecution, private val benchmark: KubernetesBenchmark) :
+    Thread() {
 
-    fun run() {
+    /**
+     * Run
+     * Delete all Kubernetes resources which are related to the execution and the benchmark.
+     */
+    override fun run() {
         // Build Configuration to teardown
+        try {
         logger.info { "Received shutdown signal -> Shutting down" }
         val deployment =
             benchmark.buildDeployment(
@@ -19,8 +32,13 @@ class Shutdown(private val benchmarkExecution: BenchmarkExecution, private val b
                 res = Resource(0, emptyList()),
                 configurationOverrides = benchmarkExecution.configOverrides
             )
+            deployment.teardown()
+        } catch (e: Exception) {
+            logger.warn { "Could not delete all specified resources from Kubernetes. " +
+                    "This could be the case, if not all resources are deployed and running." }
+
+        }
         logger.info { "Teardown everything deployed" }
-        deployment.teardown()
         logger.info { "Teardown completed" }
     }
 }
diff --git a/theodolite-quarkus/src/main/kotlin/theodolite/execution/TheodoliteExecutor.kt b/theodolite-quarkus/src/main/kotlin/theodolite/execution/TheodoliteExecutor.kt
index 9e1e8e1d1e217bd36b7ad6b5a07bf4d4084baac0..d926bb22a227f66918a17c8f6c50a2f9908a8b5c 100644
--- a/theodolite-quarkus/src/main/kotlin/theodolite/execution/TheodoliteExecutor.kt
+++ b/theodolite-quarkus/src/main/kotlin/theodolite/execution/TheodoliteExecutor.kt
@@ -1,5 +1,7 @@
 package theodolite.execution
 
+import com.google.gson.GsonBuilder
+import mu.KotlinLogging
 import theodolite.benchmark.BenchmarkExecution
 import theodolite.benchmark.KubernetesBenchmark
 import theodolite.patcher.PatcherDefinitionFactory
@@ -9,14 +11,41 @@ import theodolite.util.Config
 import theodolite.util.LoadDimension
 import theodolite.util.Resource
 import theodolite.util.Results
+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
 
+
+private val logger = KotlinLogging.logger {}
+
+/**
+ * The Theodolite executor runs all the experiments defined with the given execution and benchmark configuration.
+ *
+ * @property config Configuration of a execution
+ * @property kubernetesBenchmark Configuration of a benchmark
+ * @constructor Create empty Theodolite executor
+ */
 class TheodoliteExecutor(
     private val config: BenchmarkExecution,
     private val kubernetesBenchmark: KubernetesBenchmark
 ) {
+    /**
+     * An executor object, configured with the specified benchmark, evaluation method, experiment duration
+     * and overrides which are given in the execution.
+     */
     lateinit var executor: BenchmarkExecutor
 
+    /**
+     * Creates all required components to start Theodolite.
+     *
+     * @return a [Config], that contains a list of [LoadDimension]s,
+     *          a list of [Resource]s , and the [CompositeStrategy].
+     * The [CompositeStrategy] is configured and able to find the minimum required resource for the given load.
+     */
     private fun buildConfig(): Config {
         val results = Results()
         val strategyFactory = StrategyFactory()
@@ -37,13 +66,14 @@ class TheodoliteExecutor(
 
         executor =
             BenchmarkExecutorImpl(
-                kubernetesBenchmark,
-                results,
-                executionDuration,
-                config.configOverrides,
-                config.slos[0],
-                config.execution.repetitions
-            )
+                benchmark = kubernetesBenchmark,
+                results = results,
+                executionDuration = executionDuration,
+                configurationOverrides = config.configOverrides,
+                slo = config.slos[0],
+                repetitions = config.execution.repetitions,
+                executionId = config.executionId
+                )
 
         return Config(
             loads = config.load.loadValues.map { load -> LoadDimension(load, loadDimensionPatcherDefinition) },
@@ -72,7 +102,35 @@ 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 config = buildConfig()
         // execute benchmarks for each load
         for (load in config.loads) {
@@ -80,5 +138,14 @@ class TheodoliteExecutor(
                 config.compositeStrategy.findSuitableResource(load, config.resources)
             }
         }
+        storeAsFile(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))
+        }
     }
 }
diff --git a/theodolite-quarkus/src/main/kotlin/theodolite/execution/TheodoliteYamlExecutor.kt b/theodolite-quarkus/src/main/kotlin/theodolite/execution/TheodoliteYamlExecutor.kt
index cfd49eb83e6885118dd1e18604954e980686aea9..b9977029703c8012ada7fb3d7766bfa321a836c3 100644
--- a/theodolite-quarkus/src/main/kotlin/theodolite/execution/TheodoliteYamlExecutor.kt
+++ b/theodolite-quarkus/src/main/kotlin/theodolite/execution/TheodoliteYamlExecutor.kt
@@ -9,6 +9,22 @@ import kotlin.system.exitProcess
 
 private val logger = KotlinLogging.logger {}
 
+
+/**
+ * The Theodolite yaml executor loads the required configurations
+ * of the executions and the benchmark from yaml files and run the
+ * corresponding experiments.
+ *
+ * The location of the execution, benchmarks and Kubernetes resource
+ * files can be configured via the following environment variables:
+ * `THEODOLITE_EXECUTION`
+ *
+ * `THEODOLITE_BENCHMARK`
+ *
+ * `THEODOLITE_APP_RESOURCES`
+ *
+ * @constructor Create empty Theodolite yaml executor
+ */
 class TheodoliteYamlExecutor {
     private val parser = YamlParser()
 
diff --git a/theodolite-quarkus/src/main/kotlin/theodolite/execution/operator/BenchmarkEventHandler.kt b/theodolite-quarkus/src/main/kotlin/theodolite/execution/operator/BenchmarkEventHandler.kt
index 2b4a784315c2961c5782264c44f2c7e4e8f0d2e8..69c53a3792d86d0ad1c3e973b1d53ea5defff8d9 100644
--- a/theodolite-quarkus/src/main/kotlin/theodolite/execution/operator/BenchmarkEventHandler.kt
+++ b/theodolite-quarkus/src/main/kotlin/theodolite/execution/operator/BenchmarkEventHandler.kt
@@ -5,13 +5,37 @@ 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
@@ -23,6 +47,13 @@ class BenchmarkEventHandler(private val controller: TheodoliteController): Resou
         }
     }
 
+    /**
+     * 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)
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 1752ac112ea84ea179e238f7ab8d808779014d1b..971d3428ffde9cf776711bbd68bae68f66597823 100644
--- a/theodolite-quarkus/src/main/kotlin/theodolite/execution/operator/ExecutionEventHandler.kt
+++ b/theodolite-quarkus/src/main/kotlin/theodolite/execution/operator/ExecutionEventHandler.kt
@@ -3,30 +3,64 @@ package theodolite.execution.operator
 import io.fabric8.kubernetes.client.informers.ResourceEventHandler
 import mu.KotlinLogging
 import theodolite.benchmark.BenchmarkExecution
+import java.lang.NullPointerException
 
 private val logger = KotlinLogging.logger {}
 
+/**
+ * Handles adding, updating and deleting BenchmarkExecutions.
+ *
+ * @param controller The TheodoliteController that handles the application state
+ *
+ * @see TheodoliteController
+ * @see BenchmarkExecution
+ */
 class ExecutionHandler(private val controller: TheodoliteController): ResourceEventHandler<BenchmarkExecution> {
+
+    /**
+     * Add an execution to the end of the queue of the TheodoliteController.
+     *
+     * @param execution 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)
     }
 
+    /**
+     * Update an execution. If this execution is running at the time this function is called, it is stopped and added to
+     * the beginning of the queue of the TheodoliteController. Otherwise, it is just added to the beginning of the queue.
+     *
+     * @param execution the execution to update
+     */
     override fun onUpdate(oldExecution: BenchmarkExecution, newExecution: BenchmarkExecution) {
         logger.info { "Add updated execution to queue." }
         newExecution.name = newExecution.metadata.name
-        this.controller.executionsQueue.removeIf { e -> e.name == newExecution.metadata.name }
+        try {
+            this.controller.executionsQueue.removeIf { e -> e.name == newExecution.metadata.name }
+        } catch(e: NullPointerException) {
+            logger.warn { "No execution found for deletion" }
+        }
         this.controller.executionsQueue.addFirst(newExecution)
-        if (this.controller.isInitialized() &&  this.controller.executor.getExecution().name == newExecution.metadata.name) {
+        if (this.controller.isInitialized() && this.controller.executor.getExecution().name == newExecution.metadata.name) {
             this.controller.isUpdated.set(true)
             this.controller.executor.executor.run.compareAndSet(true, false)
         }
     }
 
+    /**
+     * Delete an execution from the queue of the TheodoliteController.
+     *
+     * @param execution the execution to delete
+     */
     override fun onDelete(execution: BenchmarkExecution, b: Boolean) {
-        logger.info { "Delete execution ${execution.metadata.name} from queue." }
-        this.controller.executionsQueue.removeIf { e -> e.name == execution.metadata.name }
+        try {
+            this.controller.executionsQueue.removeIf { e -> e.name == execution.metadata.name }
+            logger.info { "Delete execution ${execution.metadata.name} from queue." }
+        } catch(e: NullPointerException) {
+            logger.warn { "No execution found for deletion" }
+        }
         if (this.controller.isInitialized() && this.controller.executor.getExecution().name == execution.metadata.name) {
             this.controller.isUpdated.set(true)
             this.controller.executor.executor.run.compareAndSet(true, false)
diff --git a/theodolite-quarkus/src/main/kotlin/theodolite/execution/operator/TheodoliteController.kt b/theodolite-quarkus/src/main/kotlin/theodolite/execution/operator/TheodoliteController.kt
index c966739a4744f448f60223738d379753b098c060..532185841a7a8ee000722c1dc513219177f00cae 100644
--- a/theodolite-quarkus/src/main/kotlin/theodolite/execution/operator/TheodoliteController.kt
+++ b/theodolite-quarkus/src/main/kotlin/theodolite/execution/operator/TheodoliteController.kt
@@ -2,6 +2,7 @@ package theodolite.execution.operator
 
 import io.fabric8.kubernetes.client.NamespacedKubernetesClient
 import io.fabric8.kubernetes.client.dsl.base.CustomResourceDefinitionContext
+import khttp.patch
 import mu.KotlinLogging
 import theodolite.benchmark.BenchmarkExecution
 import theodolite.benchmark.KubernetesBenchmark
@@ -10,18 +11,38 @@ 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 executionContext: CustomResourceDefinitionContext,
+    val path: String
 ) {
     lateinit var executor: TheodoliteExecutor
     val executionsQueue: ConcurrentLinkedDeque<BenchmarkExecution> = ConcurrentLinkedDeque()
     val benchmarks: ConcurrentHashMap<String, KubernetesBenchmark> = ConcurrentHashMap()
     var isUpdated = AtomicBoolean(false)
+    var executionID = AtomicInteger(0)
 
+    /**
+     * Runs the TheodoliteController forever.
+     */
     fun run() {
         while (true) {
             try {
@@ -42,6 +63,12 @@ class TheodoliteController(
         }
     }
 
+    /**
+     * Ensures that the application state corresponds to the defined KubernetesBenchmarks and BenchmarkExecutions.
+     *
+     * @see KubernetesBenchmark
+     * @see BenchmarkExecution
+     */
     @Synchronized
     private fun reconcile() {
         while (executionsQueue.isNotEmpty()) {
@@ -57,19 +84,38 @@ class TheodoliteController(
         }
     }
 
+    /**
+     * 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()
 
-        if (!isUpdated.get()) {
-            client.customResource(executionContext).delete(client.namespace, execution.metadata.name)
+        try {
+            if (!isUpdated.get()) {
+                this.executionsQueue.removeIf { e -> e.name == execution.name }
+                client.customResource(executionContext).delete(client.namespace, execution.metadata.name)
+            }
+        } catch (e: Exception) {
+            logger.warn { "Deletion skipped." }
         }
+
         logger.info { "Execution of ${execution.name} is finally stopped." }
     }
 
+    /**
+     * @return true if the TheodoliteExecutor of this controller is initialized. Else returns false.
+     *
+     * @see TheodoliteExecutor
+     */
     @Synchronized
     fun isInitialized(): Boolean {
         return ::executor.isInitialized
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 4a34f26c29f4dc087d952b030002e26a35efc523..5e15a4a80e67f47a42d605d4af39102927139331 100644
--- a/theodolite-quarkus/src/main/kotlin/theodolite/execution/operator/TheodoliteOperator.kt
+++ b/theodolite-quarkus/src/main/kotlin/theodolite/execution/operator/TheodoliteOperator.kt
@@ -21,9 +21,17 @@ private const val RESYNC_PERIOD = 10 * 60 * 1000.toLong()
 private const val GROUP = "theodolite.com"
 private val logger = KotlinLogging.logger {}
 
+/**
+ * Implementation of the Operator pattern for K8s.
+ *
+ * **See Also:** [Kubernetes Operator Pattern](https://kubernetes.io/docs/concepts/extend-kubernetes/operator/)
+ */
 class TheodoliteOperator {
     private val namespace = System.getenv("NAMESPACE") ?: DEFAULT_NAMESPACE
 
+    /**
+     * Start the operator.
+     */
     fun start() {
         logger.info { "Using $namespace as namespace." }
         val client = DefaultKubernetesClient().inNamespace(namespace)
@@ -44,7 +52,8 @@ class TheodoliteOperator {
         val executionContext = contextFactory.create(API_VERSION, SCOPE, GROUP, EXECUTION_PLURAL)
         val benchmarkContext = contextFactory.create(API_VERSION, SCOPE, GROUP, BENCHMARK_PLURAL)
 
-        val controller = TheodoliteController(client = client, executionContext = executionContext)
+        val appResource = System.getenv("THEODOLITE_APP_RESOURCES") ?: "./config"
+        val controller = TheodoliteController(client = client, executionContext = executionContext, path = appResource)
 
         val informerFactory = client.informers()
         val informerExecution = informerFactory.sharedIndexInformerForCustomResource(
diff --git a/theodolite-quarkus/src/main/kotlin/theodolite/k8s/K8sContextFactory.kt b/theodolite-quarkus/src/main/kotlin/theodolite/k8s/K8sContextFactory.kt
index 8fd822c615da4fab37dafb6927032f64db6c4462..c0e07610171b40c6704602ffa86ec15accb14c19 100644
--- a/theodolite-quarkus/src/main/kotlin/theodolite/k8s/K8sContextFactory.kt
+++ b/theodolite-quarkus/src/main/kotlin/theodolite/k8s/K8sContextFactory.kt
@@ -2,9 +2,26 @@ package theodolite.k8s
 
 import io.fabric8.kubernetes.client.dsl.base.CustomResourceDefinitionContext
 
+/**
+ * Factory for CustomResourceDefinitionContext
+ *
+ * @see CustomResourceDefinitionContext
+ */
 class K8sContextFactory {
 
-    fun create(api: String, scope: String, group: String, plural: String  ) : CustomResourceDefinitionContext{
+    /**
+     * Create a CustomResourceDefinitionContext.
+     *
+     * @param api The K8s API version
+     * @param scope The scope of the CRD
+     * @param group The group of the CRD
+     * @param plural The plural name (kind) of the CRD
+     *
+     * @return a new CustomResourceDefinitionContext
+     *
+     * @see CustomResourceDefinitionContext
+     */
+    fun create(api: String, scope: String, group: String, plural: String  ) : CustomResourceDefinitionContext {
        return CustomResourceDefinitionContext.Builder()
             .withVersion(api)
             .withScope(scope)
diff --git a/theodolite-quarkus/src/main/kotlin/theodolite/k8s/K8sManager.kt b/theodolite-quarkus/src/main/kotlin/theodolite/k8s/K8sManager.kt
index 029f18c30b79bbbf36c3761ced107e23f39682d8..c58225bf855c70a5a7057132617418a89b0816a8 100644
--- a/theodolite-quarkus/src/main/kotlin/theodolite/k8s/K8sManager.kt
+++ b/theodolite-quarkus/src/main/kotlin/theodolite/k8s/K8sManager.kt
@@ -6,9 +6,21 @@ 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.NamespacedKubernetesClient
+import mu.KotlinLogging
 
+private val logger = KotlinLogging.logger {}
 
+/**
+ * This class is used to deploy or remove different Kubernetes resources.
+ * Supports: Deployments, Services, ConfigMaps, StatefulSets, and CustomResources.
+ * @param client KubernetesClient used to deploy or remove.
+ */
 class K8sManager(private val client: NamespacedKubernetesClient) {
+
+    /**
+     * Deploys different k8s resources using the client.
+     * @throws IllegalArgumentException if KubernetesResource not supported.
+     */
     fun deploy(resource: KubernetesResource) {
         when (resource) {
             is Deployment ->
@@ -24,18 +36,38 @@ class K8sManager(private val client: NamespacedKubernetesClient) {
         }
     }
 
+    /**
+     * Removes different k8s resources using the client.
+     * @throws IllegalArgumentException if KubernetesResource not supported.
+     */
     fun remove(resource: KubernetesResource) {
         when (resource) {
-            is Deployment ->
+            is Deployment -> {
+                val label = resource.spec.selector.matchLabels["app"]!!
                 this.client.apps().deployments().delete(resource)
+                blockUntilPodsDeleted(label)
+                logger.info { "Deployment '${resource.metadata.name}' deleted." }
+            }
             is Service ->
                 this.client.services().delete(resource)
             is ConfigMap ->
                 this.client.configMaps().delete(resource)
-            is StatefulSet ->
+            is StatefulSet -> {
+                val label = resource.spec.selector.matchLabels["app"]!!
                 this.client.apps().statefulSets().delete(resource)
+                blockUntilPodsDeleted(label)
+                logger.info { "StatefulSet '$resource.metadata.name' deleted." }
+            }
             is ServiceMonitorWrapper -> resource.delete(client)
             else -> throw IllegalArgumentException("Unknown Kubernetes resource.")
         }
     }
+
+    private fun blockUntilPodsDeleted(podLabel: String) {
+        while (!this.client.pods().withLabel(podLabel).list().items.isNullOrEmpty()) {
+            logger.info { "Wait for pods with label '$podLabel' to be deleted." }
+            Thread.sleep(1000)
+        }
+    }
+
 }
diff --git a/theodolite-quarkus/src/main/kotlin/theodolite/k8s/K8sResourceLoader.kt b/theodolite-quarkus/src/main/kotlin/theodolite/k8s/K8sResourceLoader.kt
index bf4af6c531064d42a9fcee1b22058850a560427d..324b02b74b2c53eb1292667f037f3fdbcc114b73 100644
--- a/theodolite-quarkus/src/main/kotlin/theodolite/k8s/K8sResourceLoader.kt
+++ b/theodolite-quarkus/src/main/kotlin/theodolite/k8s/K8sResourceLoader.kt
@@ -10,12 +10,17 @@ import theodolite.util.YamlParser
 
 private val logger = KotlinLogging.logger {}
 
+/**
+ * Used to load different Kubernetes resources.
+ * Supports: Deployments, Services, ConfigMaps, and CustomResources.
+ * @param client KubernetesClient used to deploy or remove.
+ */
 class K8sResourceLoader(private val client: NamespacedKubernetesClient) {
 
     /**
      * Parses a Service from a service yaml
      * @param path of the yaml file
-     * @return service from fabric8
+     * @return Service from fabric8
      */
     private fun loadService(path: String): Service {
         return loadGenericResource(path) { x: String -> client.services().load(x).get() }
@@ -24,7 +29,7 @@ class K8sResourceLoader(private val client: NamespacedKubernetesClient) {
     /**
      * Parses a CustomResource from a yaml
      * @param path of the yaml file
-     * @return customResource from fabric8
+     * @return CustomResource from fabric8
      */
     private fun loadServiceMonitor(path: String): ServiceMonitorWrapper {
         return loadGenericResource(path) { x: String -> ServiceMonitorWrapper(YamlParser().parse(path, HashMap<String, String>()::class.java)!!) }
@@ -51,7 +56,8 @@ class K8sResourceLoader(private val client: NamespacedKubernetesClient) {
     /**
      * Generic helper function to load a resource.
      * @param path of the resource
-     * @param f function that shall be applied to the resource.
+     * @param f function that is applied to the resource.
+     * @throws IllegalArgumentException If the resource could not be loaded.
      */
     private fun <T> loadGenericResource(path: String, f: (String) -> T): T {
         var resource: T? = null
@@ -69,6 +75,14 @@ class K8sResourceLoader(private val client: NamespacedKubernetesClient) {
         return resource
     }
 
+    /**
+     * Factory function used to load different k8s resources from a path.
+     * Supported kinds are: Deployments, Services, ServiceMonitors, ConfigMaps and CustomResources.
+     * Uses CustomResource as default if Kind is not supported.
+     * @param kind of the resource. CustomResource as default.
+     * @param path of the resource to be loaded.
+     * @throws Exception if the resource could not be loaded.
+     */
     fun loadK8sResource(kind: String, path: String): KubernetesResource {
         return when (kind) {
             "Deployment" -> loadDeployment(path)
diff --git a/theodolite-quarkus/src/main/kotlin/theodolite/k8s/TopicManager.kt b/theodolite-quarkus/src/main/kotlin/theodolite/k8s/TopicManager.kt
index 390974cd247645197ebe6044bf785710164155aa..ef5715e248ae6c64df0035a94d57fea12202787e 100644
--- a/theodolite-quarkus/src/main/kotlin/theodolite/k8s/TopicManager.kt
+++ b/theodolite-quarkus/src/main/kotlin/theodolite/k8s/TopicManager.kt
@@ -2,45 +2,111 @@ package theodolite.k8s
 
 import mu.KotlinLogging
 import org.apache.kafka.clients.admin.AdminClient
-import org.apache.kafka.clients.admin.ListTopicsResult
+import org.apache.kafka.clients.admin.CreateTopicsResult
 import org.apache.kafka.clients.admin.NewTopic
-import java.util.*
+import org.apache.kafka.common.errors.TopicExistsException
+import java.lang.Thread.sleep
 
 private val logger = KotlinLogging.logger {}
+private const val RETRY_TIME = 2000L
 
 /**
  * Manages the topics related tasks
- * @param kafkaConfig Kafka Configuration as HashMap
+ * @param kafkaConfig Kafka configuration as a Map
+ * @constructor Creates a KafkaAdminClient
  */
-class TopicManager(private val kafkaConfig: HashMap<String, Any>) {
+class TopicManager(private val kafkaConfig: Map<String, Any>) {
 
     /**
-     * Creates topics.
-     * @param newTopics List of all Topic which should be created
+     * Create topics.
+     * @param newTopics Collection of all topic that should be created
      */
     fun createTopics(newTopics: Collection<NewTopic>) {
-        var kafkaAdmin: AdminClient = AdminClient.create(this.kafkaConfig)
-        kafkaAdmin.createTopics(newTopics)
+        val kafkaAdmin: AdminClient = AdminClient.create(this.kafkaConfig)
+        lateinit var result: CreateTopicsResult
+
+        do {
+            var retryCreation = false
+            try {
+                result = kafkaAdmin.createTopics(newTopics)
+                result.all().get() // wait for the future to be completed
+            } catch (e: Exception) { // TopicExistsException
+                logger.warn(e) { "Error during topic creation." }
+                logger.debug { e } // TODO remove due to attached exception to warn log?
+                logger.info { "Remove existing topics." }
+                delete(newTopics.map { topic -> topic.name() }, kafkaAdmin)
+                logger.info { "Will retry the topic creation in ${RETRY_TIME/1000} seconds." }
+                sleep(RETRY_TIME)
+                retryCreation = true
+            }
+        } while (retryCreation)
+
+        logger.info {
+            "Topics creation finished with result: ${
+                result
+                    .values()
+                    .map { it -> it.key + ": " + it.value.isDone }
+                    .joinToString(separator = ",")
+            } "
+        }
         kafkaAdmin.close()
-        logger.info { "Topics created" }
     }
 
-
     /**
-     * Removes topics.
-     * @param topics
+     * Remove topics.
+     * @param topics Collection of names for the topics to remove.
      */
     fun removeTopics(topics: List<String>) {
-        var kafkaAdmin: AdminClient = AdminClient.create(this.kafkaConfig)
-        val result = kafkaAdmin.deleteTopics(topics)
-
-        try {
-            result.all().get()
-        } catch (e: Exception) {
-            logger.error { "Error while removing topics: $e"  }
-            logger.debug { "Existing topics are: ${kafkaAdmin.listTopics()}."  }
-        }
+        val kafkaAdmin: AdminClient = AdminClient.create(this.kafkaConfig)
+        val currentTopics = kafkaAdmin.listTopics().names().get()
+        delete(currentTopics.filter{ matchRegex(it, topics) }, kafkaAdmin)
         kafkaAdmin.close()
-        logger.info { "Topics removed" }
     }
+
+    /**
+     * This function checks whether one string in `topics` can be used as prefix of a regular expression to create the string `existingTopic`
+     *
+     * @param existingTopic string for which should be checked if it could be created
+     * @param topics list of string which are used as possible prefixes to create `existingTopic`
+     * @return true, `existingTopics` matches a created regex, else false
+     */
+    private fun matchRegex(existingTopic: String, topics: List<String>): Boolean {
+        for (t in topics) {
+            val regex = t.toRegex()
+            if (regex.matches(existingTopic)) {
+                return true
+            }
+        }
+        return false
+    }
+
+    private fun delete(topics: List<String>, kafkaAdmin: AdminClient) {
+        var deleted = false
+
+        while (!deleted) {
+            try {
+                val result = kafkaAdmin.deleteTopics(topics)
+                result.all().get() // wait for the future to be completed
+                logger.info {
+                    "Topics deletion finished with result: ${
+                        result.values().map { it -> it.key + ": " + it.value.isDone }
+                            .joinToString(separator = ",")
+                    }"
+                }
+            } catch (e: Exception) {
+                logger.error(e) { "Error while removing topics: $e" }
+                logger.info { "Existing topics are: ${kafkaAdmin.listTopics().names().get()}." }
+            }
+
+            val toDelete = topics.filter { kafkaAdmin.listTopics().names().get().contains(it) }
+
+            if (toDelete.isNullOrEmpty()) {
+                deleted = true
+            } else {
+                logger.info { "Deletion of Kafka topics failed, will retry in ${RETRY_TIME/1000} seconds." }
+                sleep(RETRY_TIME)
+            }
+        }
+    }
+
 }
diff --git a/theodolite-quarkus/src/main/kotlin/theodolite/patcher/AbstractPatcher.kt b/theodolite-quarkus/src/main/kotlin/theodolite/patcher/AbstractPatcher.kt
index 2757d106868637978443d1562306ae4603a5b7e3..c0d17244b6a7a3f37b8d8a57713659b85b9b65b1 100644
--- a/theodolite-quarkus/src/main/kotlin/theodolite/patcher/AbstractPatcher.kt
+++ b/theodolite-quarkus/src/main/kotlin/theodolite/patcher/AbstractPatcher.kt
@@ -2,6 +2,23 @@ package theodolite.patcher
 
 import io.fabric8.kubernetes.api.model.KubernetesResource
 
+/**
+ * A Patcher is able to modify values of a Kubernetes resource, see [Patcher].
+ *
+ * An AbstractPatcher is created with up to three parameters.
+ *
+ * @param k8sResource The Kubernetes resource to be patched.
+ * @param container *(optional)* The name of the container to be patched
+ * @param variableName *(optional)* The variable name to be patched
+ *
+ *
+ * **For example** to patch the load dimension of a load generator, the patcher should be created as follow:
+ *
+ * k8sResource: `uc-1-workload-generator.yaml`
+ * container: `workload`
+ * variableName: `NUM_SENSORS`
+ *
+ */
 abstract class AbstractPatcher(
     k8sResource: KubernetesResource,
     container: String? = null,
diff --git a/theodolite-quarkus/src/main/kotlin/theodolite/patcher/EnvVarPatcher.kt b/theodolite-quarkus/src/main/kotlin/theodolite/patcher/EnvVarPatcher.kt
index 5bdf66f183e3ad553e29f8754a4d3ef7ac0ccecd..b640df1da2ca1c139bb5b02e9e42bad9e7d08d74 100644
--- a/theodolite-quarkus/src/main/kotlin/theodolite/patcher/EnvVarPatcher.kt
+++ b/theodolite-quarkus/src/main/kotlin/theodolite/patcher/EnvVarPatcher.kt
@@ -2,10 +2,16 @@ package theodolite.patcher
 
 import io.fabric8.kubernetes.api.model.Container
 import io.fabric8.kubernetes.api.model.EnvVar
-import io.fabric8.kubernetes.api.model.EnvVarSource
 import io.fabric8.kubernetes.api.model.KubernetesResource
 import io.fabric8.kubernetes.api.model.apps.Deployment
 
+/**
+ * The EnvVarPatcher allows to modify the value of an environment variable
+ *
+ * @property k8sResource Kubernetes resource to be patched.
+ * @property container Container to be patched.
+ * @property variableName Name of the environment variable to be patched.
+ */
 class EnvVarPatcher(
     private val k8sResource: KubernetesResource,
     private val container: String,
@@ -32,7 +38,9 @@ class EnvVarPatcher(
             val x = container.env.filter { envVar -> envVar.name == k }
 
             if (x.isEmpty()) {
-                val newVar = EnvVar(k, v, EnvVarSource())
+                val newVar = EnvVar()
+                newVar.name = k
+                newVar.value = v
                 container.env.add(newVar)
             } else {
                 x.forEach {
diff --git a/theodolite-quarkus/src/main/kotlin/theodolite/patcher/ImagePatcher.kt b/theodolite-quarkus/src/main/kotlin/theodolite/patcher/ImagePatcher.kt
index 960ed6d6f1eb67705da77d14d280c246a5fa6fa2..e5e5f6cb67641c71ad0fd31375752cbb03fa62db 100644
--- a/theodolite-quarkus/src/main/kotlin/theodolite/patcher/ImagePatcher.kt
+++ b/theodolite-quarkus/src/main/kotlin/theodolite/patcher/ImagePatcher.kt
@@ -4,9 +4,15 @@ import io.fabric8.kubernetes.api.model.KubernetesResource
 import io.fabric8.kubernetes.api.model.apps.Deployment
 import io.fabric8.kubernetes.api.model.apps.StatefulSet
 
+/**
+ * The Image patcher allows to change the image of a container.
+ *
+ * @param k8sResource Kubernetes resource to be patched.
+ * @param container Container to be patched.
+ */
 class ImagePatcher(private val k8sResource: KubernetesResource, private val container: String) :
     AbstractPatcher(k8sResource, container) {
-
+    
     override fun <String> patch(imagePath: String) {
         if (k8sResource is Deployment) {
             k8sResource.spec.template.spec.containers.filter { it.name == container }.forEach {
diff --git a/theodolite-quarkus/src/main/kotlin/theodolite/patcher/NodeSelectorPatcher.kt b/theodolite-quarkus/src/main/kotlin/theodolite/patcher/NodeSelectorPatcher.kt
index ad0b8d411dd7c3b743177bd1f3e5862d55c7fe65..0a668a908e66577f96ea1268b85a38ad73bb16a7 100644
--- a/theodolite-quarkus/src/main/kotlin/theodolite/patcher/NodeSelectorPatcher.kt
+++ b/theodolite-quarkus/src/main/kotlin/theodolite/patcher/NodeSelectorPatcher.kt
@@ -3,6 +3,12 @@ package theodolite.patcher
 import io.fabric8.kubernetes.api.model.KubernetesResource
 import io.fabric8.kubernetes.api.model.apps.Deployment
 
+/**
+ * The Node selector patcher make it possible to set the NodeSelector of a Kubernetes deployment.
+ *
+ * @param k8sResource Kubernetes resource to be patched.
+ * @param variableName The `label-key` of the node for which the `label-value` is to be patched.
+ */
 class NodeSelectorPatcher(private val k8sResource: KubernetesResource, private val variableName: String) :
     AbstractPatcher(k8sResource, variableName) {
     override fun <String> patch(value: String) {
diff --git a/theodolite-quarkus/src/main/kotlin/theodolite/patcher/NumNestedGroupsLoadGeneratorReplicaPatcher.kt b/theodolite-quarkus/src/main/kotlin/theodolite/patcher/NumNestedGroupsLoadGeneratorReplicaPatcher.kt
new file mode 100644
index 0000000000000000000000000000000000000000..7cf56f8452949e387a186aa8f8c962e1ee1aad15
--- /dev/null
+++ b/theodolite-quarkus/src/main/kotlin/theodolite/patcher/NumNestedGroupsLoadGeneratorReplicaPatcher.kt
@@ -0,0 +1,20 @@
+package theodolite.patcher
+
+import io.fabric8.kubernetes.api.model.KubernetesResource
+import io.fabric8.kubernetes.api.model.apps.Deployment
+import kotlin.math.pow
+
+private const val NUM_SENSORS = 4.0
+private const val LOAD_GEN_MAX_RECORDS = 150000
+
+class NumNestedGroupsLoadGeneratorReplicaPatcher(private val k8sResource: KubernetesResource) : AbstractPatcher(k8sResource) {
+    override fun <String> patch(value: String) {
+        if (k8sResource is Deployment) {
+            if (value is kotlin.String) {
+                val approxNumSensors = NUM_SENSORS.pow(Integer.parseInt(value).toDouble())
+                val loadGenInstances = (approxNumSensors + LOAD_GEN_MAX_RECORDS -1) / LOAD_GEN_MAX_RECORDS
+                this.k8sResource.spec.replicas = loadGenInstances.toInt()
+            }
+        }
+    }
+}
diff --git a/theodolite-quarkus/src/main/kotlin/theodolite/patcher/NumSensorsLoadGeneratorReplicaPatcher.kt b/theodolite-quarkus/src/main/kotlin/theodolite/patcher/NumSensorsLoadGeneratorReplicaPatcher.kt
new file mode 100644
index 0000000000000000000000000000000000000000..6f2ebcb8b1eb37801c7f6bb2f28c251a07ae44e8
--- /dev/null
+++ b/theodolite-quarkus/src/main/kotlin/theodolite/patcher/NumSensorsLoadGeneratorReplicaPatcher.kt
@@ -0,0 +1,17 @@
+package theodolite.patcher
+
+import io.fabric8.kubernetes.api.model.KubernetesResource
+import io.fabric8.kubernetes.api.model.apps.Deployment
+
+private const val LOAD_GEN_MAX_RECORDS = 150000
+
+class NumSensorsLoadGeneratorReplicaPatcher(private val k8sResource: KubernetesResource) : AbstractPatcher(k8sResource) {
+    override fun <String> patch(value: String) {
+        if (k8sResource is Deployment) {
+            if (value is kotlin.String) {
+                val loadGenInstances = (Integer.parseInt(value) + LOAD_GEN_MAX_RECORDS - 1) / LOAD_GEN_MAX_RECORDS
+                this.k8sResource.spec.replicas = loadGenInstances
+            }
+        }
+    }
+}
diff --git a/theodolite-quarkus/src/main/kotlin/theodolite/patcher/Patcher.kt b/theodolite-quarkus/src/main/kotlin/theodolite/patcher/Patcher.kt
index 55cde39f5a6d7bb4cacd1e539593fc0538d108af..84b886cb4f06b3e667eb8b8aeaa622e1ee54852e 100644
--- a/theodolite-quarkus/src/main/kotlin/theodolite/patcher/Patcher.kt
+++ b/theodolite-quarkus/src/main/kotlin/theodolite/patcher/Patcher.kt
@@ -2,7 +2,19 @@ package theodolite.patcher
 
 import io.quarkus.runtime.annotations.RegisterForReflection
 
+/**
+ * A patcher can be used to modify values of Kubernetes resource.
+ *
+ * @constructor Create empty Patcher
+ */
 @RegisterForReflection
 interface Patcher {
+    /**
+     * The patch method modifies a value in the definition of a
+     * Kubernetes resource.
+     *
+     * @param T The type of value
+     * @param value The value to be used.
+     */
     fun <T> patch(value: T)
 }
diff --git a/theodolite-quarkus/src/main/kotlin/theodolite/patcher/PatcherDefinitionFactory.kt b/theodolite-quarkus/src/main/kotlin/theodolite/patcher/PatcherDefinitionFactory.kt
index 096d19e7c54ce3ac308ca59edee7861a7041dde0..bcb568f716449cb1981112ab23a81411a0f7c54d 100644
--- a/theodolite-quarkus/src/main/kotlin/theodolite/patcher/PatcherDefinitionFactory.kt
+++ b/theodolite-quarkus/src/main/kotlin/theodolite/patcher/PatcherDefinitionFactory.kt
@@ -3,8 +3,24 @@ package theodolite.patcher
 import theodolite.util.PatcherDefinition
 import theodolite.util.TypeName
 
+/**
+ * The PatcherDefinition Factory creates a [PatcherDefinition]s.
+ *
+ * @constructor Create empty Patcher definition factory.
+ */
 class PatcherDefinitionFactory {
-    fun createPatcherDefinition(requiredType: String, patcherTypes: List<TypeName>) : List<PatcherDefinition> {
+    /**
+     * Creates a list of PatcherDefinitions
+     *
+     * @param requiredType indicates the required PatcherDefinitions
+     *     (for example `NumSensors`)
+     * @param patcherTypes list of TypeNames. A TypeName contains a type
+     *     (for example `NumSensors`) and a list of
+     *     PatcherDefinitions, which are related to this type.
+     * @return A list of PatcherDefinitions which corresponds to the
+     *     value of the requiredType.
+     */
+    fun createPatcherDefinition(requiredType: String, patcherTypes: List<TypeName>): List<PatcherDefinition> {
         return patcherTypes
             .filter { type -> type.typeName == requiredType }
             .flatMap { type -> type.patchers }
diff --git a/theodolite-quarkus/src/main/kotlin/theodolite/patcher/PatcherFactory.kt b/theodolite-quarkus/src/main/kotlin/theodolite/patcher/PatcherFactory.kt
index dd391c599ad33fa0f6990ecc86ed1af5430cb695..2ee1f6c7b46322cb0f8de03c37aabe64ccf0ba5a 100644
--- a/theodolite-quarkus/src/main/kotlin/theodolite/patcher/PatcherFactory.kt
+++ b/theodolite-quarkus/src/main/kotlin/theodolite/patcher/PatcherFactory.kt
@@ -3,13 +3,35 @@ package theodolite.patcher
 import io.fabric8.kubernetes.api.model.KubernetesResource
 import theodolite.util.PatcherDefinition
 
+/**
+ * The Patcher factory creates [Patcher]s
+ *
+ * @constructor Creates an empty PatcherFactory.
+ */
 class PatcherFactory {
-    fun createPatcher(patcherDefinition: PatcherDefinition,
-                      k8sResources: List<Pair<String, KubernetesResource>>) : Patcher {
+    /**
+     * Create patcher based on the given [PatcherDefinition] and
+     * the list of KubernetesResources.
+     *
+     * @param patcherDefinition The [PatcherDefinition] for which are
+     *     [Patcher] should be created.
+     * @param k8sResources List of all available Kubernetes resources.
+     *     This is a list of pairs<String, KubernetesResource>:
+     *     The frist corresponds to the filename where the resource is defined.
+     *     The second corresponds to the concrete [KubernetesResource] that should be patched.
+     * @return The created [Patcher].
+     * @throws IllegalArgumentException if no patcher can be created.
+     */
+    fun createPatcher(
+        patcherDefinition: PatcherDefinition,
+        k8sResources: List<Pair<String, KubernetesResource>>
+    ): Patcher {
         val resource =
             k8sResources.filter { it.first == patcherDefinition.resource }.map { resource -> resource.second }[0]
         return when (patcherDefinition.type) {
             "ReplicaPatcher" -> ReplicaPatcher(resource)
+            "NumNestedGroupsLoadGeneratorReplicaPatcher" -> NumNestedGroupsLoadGeneratorReplicaPatcher(resource)
+            "NumSensorsLoadGeneratorReplicaPatcher" -> NumSensorsLoadGeneratorReplicaPatcher(resource)
             "EnvVarPatcher" -> EnvVarPatcher(resource, patcherDefinition.container, patcherDefinition.variableName)
             "NodeSelectorPatcher" -> NodeSelectorPatcher(resource, patcherDefinition.variableName)
             "ResourceLimitPatcher" -> ResourceLimitPatcher(
@@ -26,4 +48,4 @@ class PatcherFactory {
             else -> throw IllegalArgumentException("Patcher type ${patcherDefinition.type} not found")
         }
     }
-}
\ No newline at end of file
+}
diff --git a/theodolite-quarkus/src/main/kotlin/theodolite/patcher/ReplicaPatcher.kt b/theodolite-quarkus/src/main/kotlin/theodolite/patcher/ReplicaPatcher.kt
index b4b33fabc5a27e3cc52f5cda889f63bc61adbf5f..4cc35f2ed74f9e366c266c3f98f1b3d36d4ba1b8 100644
--- a/theodolite-quarkus/src/main/kotlin/theodolite/patcher/ReplicaPatcher.kt
+++ b/theodolite-quarkus/src/main/kotlin/theodolite/patcher/ReplicaPatcher.kt
@@ -3,6 +3,11 @@ package theodolite.patcher
 import io.fabric8.kubernetes.api.model.KubernetesResource
 import io.fabric8.kubernetes.api.model.apps.Deployment
 
+/**
+ * The Replica [Patcher] modifies the number of replicas for the given Kubernetes deployment.
+ *
+ * @param k8sResource  Kubernetes resource to be patched.
+ */
 class ReplicaPatcher(private val k8sResource: KubernetesResource) : AbstractPatcher(k8sResource) {
     override fun <String> patch(value: String) {
         if (k8sResource is Deployment) {
diff --git a/theodolite-quarkus/src/main/kotlin/theodolite/patcher/ResourceLimitPatcher.kt b/theodolite-quarkus/src/main/kotlin/theodolite/patcher/ResourceLimitPatcher.kt
index 533489a44457ba7452f3d0ca1eb88eb6f3ada7d9..eab82effbc084e91ba57c1bea7103b2a3239c922 100644
--- a/theodolite-quarkus/src/main/kotlin/theodolite/patcher/ResourceLimitPatcher.kt
+++ b/theodolite-quarkus/src/main/kotlin/theodolite/patcher/ResourceLimitPatcher.kt
@@ -8,6 +8,13 @@ import io.fabric8.kubernetes.api.model.apps.Deployment
 import io.fabric8.kubernetes.api.model.apps.StatefulSet
 import java.lang.IllegalArgumentException
 
+/**
+ * The Resource limit [Patcher] set resource limits for deployments and statefulSets.
+ *
+ * @param k8sResource Kubernetes resource to be patched.
+ * @param container Container to be patched.
+ * @param limitedResource The resource to be limited (e.g. **cpu or memory**)
+ */
 class ResourceLimitPatcher(
     private val k8sResource: KubernetesResource,
     private val container: String,
diff --git a/theodolite-quarkus/src/main/kotlin/theodolite/patcher/ResourceRequestPatcher.kt b/theodolite-quarkus/src/main/kotlin/theodolite/patcher/ResourceRequestPatcher.kt
index 39e8fafb513a60e8a8cd445227160025fe6d8c42..f4ef38edebab67022066394e149716ab9ffbce00 100644
--- a/theodolite-quarkus/src/main/kotlin/theodolite/patcher/ResourceRequestPatcher.kt
+++ b/theodolite-quarkus/src/main/kotlin/theodolite/patcher/ResourceRequestPatcher.kt
@@ -8,6 +8,13 @@ import io.fabric8.kubernetes.api.model.apps.Deployment
 import io.fabric8.kubernetes.api.model.apps.StatefulSet
 import java.lang.IllegalArgumentException
 
+/**
+ * The Resource request [Patcher] set resource limits for deployments and statefulSets.
+ *
+ * @param k8sResource Kubernetes resource to be patched.
+ * @param container Container to be patched.
+ * @param requestedResource The resource to be requested (e.g. **cpu or memory**)
+ */
 class ResourceRequestPatcher(
     private val k8sResource: KubernetesResource,
     private val container: String,
diff --git a/theodolite-quarkus/src/main/kotlin/theodolite/patcher/SchedulerNamePatcher.kt b/theodolite-quarkus/src/main/kotlin/theodolite/patcher/SchedulerNamePatcher.kt
index 6d6505192daac3331e5c99b9706ca7413e98ea7d..589bceff78158a422d923169bd35a1e11e2f4caa 100644
--- a/theodolite-quarkus/src/main/kotlin/theodolite/patcher/SchedulerNamePatcher.kt
+++ b/theodolite-quarkus/src/main/kotlin/theodolite/patcher/SchedulerNamePatcher.kt
@@ -3,7 +3,12 @@ package theodolite.patcher
 import io.fabric8.kubernetes.api.model.KubernetesResource
 import io.fabric8.kubernetes.api.model.apps.Deployment
 
-class SchedulerNamePatcher(private val k8sResource: KubernetesResource) : Patcher {
+/**
+ * The Scheduler name [Patcher] make it possible to set the scheduler which should be used to deploy the given deployment.
+ *
+ * @param k8sResource Kubernetes resource to be patched.
+ */
+class SchedulerNamePatcher(private val k8sResource: KubernetesResource): Patcher {
     override fun <String> patch(value: String) {
         if (k8sResource is Deployment) {
             k8sResource.spec.template.spec.schedulerName = value as kotlin.String
diff --git a/theodolite-quarkus/src/main/kotlin/theodolite/strategies/StrategyFactory.kt b/theodolite-quarkus/src/main/kotlin/theodolite/strategies/StrategyFactory.kt
index 3d0135a8884e581bd8caa61fb5c0632057812150..829370e8ce1c181c1a4cb9fdd8ccf0ecefd48d3d 100644
--- a/theodolite-quarkus/src/main/kotlin/theodolite/strategies/StrategyFactory.kt
+++ b/theodolite-quarkus/src/main/kotlin/theodolite/strategies/StrategyFactory.kt
@@ -4,20 +4,44 @@ import theodolite.execution.BenchmarkExecutor
 import theodolite.strategies.restriction.LowerBoundRestriction
 import theodolite.strategies.restriction.RestrictionStrategy
 import theodolite.strategies.searchstrategy.BinarySearch
+import theodolite.strategies.searchstrategy.FullSearch
 import theodolite.strategies.searchstrategy.LinearSearch
 import theodolite.strategies.searchstrategy.SearchStrategy
 import theodolite.util.Results
 
+/**
+ * Factory for creating [SearchStrategy] and [RestrictionStrategy] strategies.
+ */
 class StrategyFactory {
 
+    /**
+     * Create a [SearchStrategy].
+     *
+     * @param executor The [theodolite.execution.BenchmarkExecutor] that executes individual experiments.
+     * @param searchStrategyString Specifies the [SearchStrategy]. Must either be the string 'LinearSearch',
+     * or 'BinarySearch'.
+     *
+     * @throws IllegalArgumentException if the [SearchStrategy] was not one of the allowed options.
+     */
     fun createSearchStrategy(executor: BenchmarkExecutor, searchStrategyString: String): SearchStrategy {
         return when (searchStrategyString) {
+            "FullSearch" -> FullSearch(executor)
             "LinearSearch" -> LinearSearch(executor)
             "BinarySearch" -> BinarySearch(executor)
             else -> throw IllegalArgumentException("Search Strategy $searchStrategyString not found")
         }
     }
 
+    /**
+     * Create a [RestrictionStrategy].
+     *
+     * @param results The [Results] saves the state of the Theodolite benchmark run.
+     * @param restrictionStrings Specifies the list of [RestrictionStrategy] that are used to restrict the amount
+     * of [theodolite.util.Resource] for a fixed LoadDimension. Must equal the string
+     * 'LowerBound'.
+     *
+     * @throws IllegalArgumentException if param searchStrategyString was not one of the allowed options.
+     */
     fun createRestrictionStrategy(results: Results, restrictionStrings: List<String>): Set<RestrictionStrategy> {
         return restrictionStrings
             .map { restriction ->
diff --git a/theodolite-quarkus/src/main/kotlin/theodolite/strategies/restriction/LowerBoundRestriction.kt b/theodolite-quarkus/src/main/kotlin/theodolite/strategies/restriction/LowerBoundRestriction.kt
index 6fed9b5d808405b42ad374346862f050ce192141..13bfedfe055f2bd428137f89b2986f3967ec797c 100644
--- a/theodolite-quarkus/src/main/kotlin/theodolite/strategies/restriction/LowerBoundRestriction.kt
+++ b/theodolite-quarkus/src/main/kotlin/theodolite/strategies/restriction/LowerBoundRestriction.kt
@@ -5,13 +5,14 @@ import theodolite.util.Resource
 import theodolite.util.Results
 
 /**
- * The Lower Bound Restriction sets the lower bound of the resources to be examined to the value
+ * The [LowerBoundRestriction] sets the lower bound of the resources to be examined to the value
  * needed to successfully execute the next smaller load.
  *
- * @param results Result object used as a basis to restrict the resources.
+ * @param results [Result] object used as a basis to restrict the resources.
  */
 class LowerBoundRestriction(results: Results) : RestrictionStrategy(results) {
-    override fun next(load: LoadDimension, resources: List<Resource>): List<Resource> {
+
+    override fun apply(load: LoadDimension, resources: List<Resource>): List<Resource> {
         val maxLoad: LoadDimension? = this.results.getMaxBenchmarkedLoad(load)
         var lowerBound: Resource? = this.results.getMinRequiredInstances(maxLoad)
         if (lowerBound == null) {
@@ -19,4 +20,5 @@ class LowerBoundRestriction(results: Results) : RestrictionStrategy(results) {
         }
         return resources.filter { x -> x.get() >= lowerBound.get() }
     }
+
 }
diff --git a/theodolite-quarkus/src/main/kotlin/theodolite/strategies/restriction/RestrictionStrategy.kt b/theodolite-quarkus/src/main/kotlin/theodolite/strategies/restriction/RestrictionStrategy.kt
index 75c7e86b57e3b3afb0121eab628f2872458efe74..1ab7302d7898daad729b1c94c32d97138b5cdcf4 100644
--- a/theodolite-quarkus/src/main/kotlin/theodolite/strategies/restriction/RestrictionStrategy.kt
+++ b/theodolite-quarkus/src/main/kotlin/theodolite/strategies/restriction/RestrictionStrategy.kt
@@ -6,18 +6,20 @@ import theodolite.util.Resource
 import theodolite.util.Results
 
 /**
- * A "Restriction Strategy" restricts a list of resources based on the current
+ * A 'Restriction Strategy' restricts a list of resources based on the current
  * results of all previously performed benchmarks.
+ *
+ * @param results the [Results] object
  */
 @RegisterForReflection
 abstract class RestrictionStrategy(val results: Results) {
     /**
-     * Next Restrict the given resource list for the given load based on the result object.
+     * Apply the restriction of the given resource list for the given load based on the results object.
      *
-     * @param load Load dimension for which a subset of resources are required.
-     * @param resources List of resources to be restricted.
+     * @param load [LoadDimension] for which a subset of resources are required.
+     * @param resources List of [Resource]s to be restricted.
      * @return Returns a list containing only elements that have not been filtered out by the
      * restriction (possibly empty).
      */
-    abstract fun next(load: LoadDimension, resources: List<Resource>): List<Resource>
+    abstract fun apply(load: LoadDimension, resources: List<Resource>): List<Resource>
 }
diff --git a/theodolite-quarkus/src/main/kotlin/theodolite/strategies/searchstrategy/BinarySearch.kt b/theodolite-quarkus/src/main/kotlin/theodolite/strategies/searchstrategy/BinarySearch.kt
index 04f25fd9925d83b2a034536f9116c660dae6377d..28e8194c699cd074026c8cb7e6f3ce4ec347023b 100644
--- a/theodolite-quarkus/src/main/kotlin/theodolite/strategies/searchstrategy/BinarySearch.kt
+++ b/theodolite-quarkus/src/main/kotlin/theodolite/strategies/searchstrategy/BinarySearch.kt
@@ -1,11 +1,14 @@
 package theodolite.strategies.searchstrategy
 
+import mu.KotlinLogging
 import theodolite.execution.BenchmarkExecutor
 import theodolite.util.LoadDimension
 import theodolite.util.Resource
 
+private val logger = KotlinLogging.logger {}
+
 /**
- *  Search for the smallest suitable resource with binary search.
+ *  Binary-search-like implementation for determining the smallest suitable number of instances.
  *
  * @param benchmarkExecutor Benchmark executor which runs the individual benchmarks.
  */
@@ -18,12 +21,22 @@ class BinarySearch(benchmarkExecutor: BenchmarkExecutor) : SearchStrategy(benchm
         return resources[result]
     }
 
+    /**
+     * Apply binary search.
+     *
+     * @param load the load dimension to perform experiments for
+     * @param resources the list in which binary search is performed
+     * @param lower lower bound for binary search (inclusive)
+     * @param upper upper bound for binary search (inclusive)
+     */
     private fun binarySearch(load: LoadDimension, resources: List<Resource>, lower: Int, upper: Int): Int {
         if (lower > upper) {
             throw IllegalArgumentException()
         }
         // special case:  length == 1 or 2
         if (lower == upper) {
+            val res = resources[lower]
+            logger.info { "Running experiment with load '${load.get()}' and resources '${res.get()}'" }
             if (this.benchmarkExecutor.runExperiment(load, resources[lower])) return lower
             else {
                 if (lower + 1 == resources.size) return -1
@@ -33,6 +46,8 @@ class BinarySearch(benchmarkExecutor: BenchmarkExecutor) : SearchStrategy(benchm
             // apply binary search for a list with
             // length > 2 and adjust upper and lower depending on the result for `resources[mid]`
             val mid = (upper + lower) / 2
+            val res = resources[mid]
+            logger.info { "Running experiment with load '${load.get()}' and resources '${res.get()}'" }
             if (this.benchmarkExecutor.runExperiment(load, resources[mid])) {
                 if (mid == lower) {
                     return lower
diff --git a/theodolite-quarkus/src/main/kotlin/theodolite/strategies/searchstrategy/CompositeStrategy.kt b/theodolite-quarkus/src/main/kotlin/theodolite/strategies/searchstrategy/CompositeStrategy.kt
index 01d841c91b2de6fb70208ef4f8da12ab75361818..6ae06d70c9effe0a0a4bbd9abffa665fb08636c9 100644
--- a/theodolite-quarkus/src/main/kotlin/theodolite/strategies/searchstrategy/CompositeStrategy.kt
+++ b/theodolite-quarkus/src/main/kotlin/theodolite/strategies/searchstrategy/CompositeStrategy.kt
@@ -6,6 +6,13 @@ import theodolite.strategies.restriction.RestrictionStrategy
 import theodolite.util.LoadDimension
 import theodolite.util.Resource
 
+/**
+ *  Composite strategy that combines a SearchStrategy and a set of RestrictionStrategy.
+ *
+ * @param searchStrategy the [SearchStrategy] that is executed as part of this [CompositeStrategy].
+ * @param restrictionStrategies the set of [RestrictionStrategy] that are connected conjuntively to restrict the [Resource]
+ * @param benchmarkExecutor Benchmark executor which runs the individual benchmarks.
+ */
 @RegisterForReflection
 class CompositeStrategy(
     benchmarkExecutor: BenchmarkExecutor,
@@ -16,7 +23,7 @@ class CompositeStrategy(
     override fun findSuitableResource(load: LoadDimension, resources: List<Resource>): Resource? {
         var restrictedResources = resources.toList()
         for (strategy in this.restrictionStrategies) {
-            restrictedResources = restrictedResources.intersect(strategy.next(load, resources)).toList()
+            restrictedResources = restrictedResources.intersect(strategy.apply(load, resources)).toList()
         }
         return this.searchStrategy.findSuitableResource(load, restrictedResources)
     }
diff --git a/theodolite-quarkus/src/main/kotlin/theodolite/strategies/searchstrategy/FullSearch.kt b/theodolite-quarkus/src/main/kotlin/theodolite/strategies/searchstrategy/FullSearch.kt
new file mode 100644
index 0000000000000000000000000000000000000000..20290a9477f16c7d479d32ec4435da0c1bb26514
--- /dev/null
+++ b/theodolite-quarkus/src/main/kotlin/theodolite/strategies/searchstrategy/FullSearch.kt
@@ -0,0 +1,31 @@
+package theodolite.strategies.searchstrategy
+
+import mu.KotlinLogging
+import theodolite.execution.BenchmarkExecutor
+import theodolite.util.LoadDimension
+import theodolite.util.Resource
+
+private val logger = KotlinLogging.logger {}
+
+/**
+ * [SearchStrategy] that executes experiment for provides resources in a linear-search-like fashion, but **without
+ * stopping** once a suitable resource amount is found.
+ *
+ * @see LinearSearch for a SearchStrategy that stops once a suitable resource amount is found.
+ *
+ * @param benchmarkExecutor Benchmark executor which runs the individual benchmarks.
+ */
+class FullSearch(benchmarkExecutor: BenchmarkExecutor) : SearchStrategy(benchmarkExecutor) {
+
+    override fun findSuitableResource(load: LoadDimension, resources: List<Resource>): Resource? {
+        var minimalSuitableResources: Resource? = null;
+        for (res in resources) {
+            logger.info { "Running experiment with load '${load.get()}' and resources '${res.get()}'" }
+            val result = this.benchmarkExecutor.runExperiment(load, res)
+            if (result && minimalSuitableResources != null) {
+                minimalSuitableResources = res
+            }
+        }
+        return minimalSuitableResources
+    }
+}
diff --git a/theodolite-quarkus/src/main/kotlin/theodolite/strategies/searchstrategy/LinearSearch.kt b/theodolite-quarkus/src/main/kotlin/theodolite/strategies/searchstrategy/LinearSearch.kt
index f1e8591a0a619d7c3ce59a40505989714f40972c..85deaf6fa75437199bfc560404eb5b40bb4a986a 100644
--- a/theodolite-quarkus/src/main/kotlin/theodolite/strategies/searchstrategy/LinearSearch.kt
+++ b/theodolite-quarkus/src/main/kotlin/theodolite/strategies/searchstrategy/LinearSearch.kt
@@ -1,13 +1,23 @@
 package theodolite.strategies.searchstrategy
 
+import mu.KotlinLogging
 import theodolite.execution.BenchmarkExecutor
 import theodolite.util.LoadDimension
 import theodolite.util.Resource
 
+private val logger = KotlinLogging.logger {}
+
+/**
+ *  Linear-search-like implementation for determining the smallest suitable number of instances.
+ *
+ * @param benchmarkExecutor Benchmark executor which runs the individual benchmarks.
+ */
 class LinearSearch(benchmarkExecutor: BenchmarkExecutor) : SearchStrategy(benchmarkExecutor) {
 
     override fun findSuitableResource(load: LoadDimension, resources: List<Resource>): Resource? {
         for (res in resources) {
+
+            logger.info { "Running experiment with load '${load.get()}' and resources '${res.get()}'" }
             if (this.benchmarkExecutor.runExperiment(load, res)) return res
         }
         return null
diff --git a/theodolite-quarkus/src/main/kotlin/theodolite/strategies/searchstrategy/SearchStrategy.kt b/theodolite-quarkus/src/main/kotlin/theodolite/strategies/searchstrategy/SearchStrategy.kt
index 888a58585b08cd02b116ee1d2e275138f5f690e8..4e304b010d4d56f6b5fe734a6b977361f93e57a1 100644
--- a/theodolite-quarkus/src/main/kotlin/theodolite/strategies/searchstrategy/SearchStrategy.kt
+++ b/theodolite-quarkus/src/main/kotlin/theodolite/strategies/searchstrategy/SearchStrategy.kt
@@ -5,13 +5,19 @@ import theodolite.execution.BenchmarkExecutor
 import theodolite.util.LoadDimension
 import theodolite.util.Resource
 
+/**
+ *  Base class for the implementation for SearchStrategies. SearchStrategies determine the smallest suitable number of instances.
+ *
+ * @param benchmarkExecutor Benchmark executor which runs the individual benchmarks.
+ */
 @RegisterForReflection
 abstract class SearchStrategy(val benchmarkExecutor: BenchmarkExecutor) {
     /**
      * Find smallest suitable resource from the specified resource list for the given load.
      *
-     * @param load Load to be tested.
-     * @param resources List of all possible resources.
+     * @param load the [LoadDimension] to be tested.
+     * @param resources List of all possible [Resource]s.
+     *
      * @return suitable resource for the specified load, or null if no suitable resource exists.
      */
     abstract fun findSuitableResource(load: LoadDimension, resources: List<Resource>): Resource?
diff --git a/theodolite-quarkus/src/main/kotlin/theodolite/util/Config.kt b/theodolite-quarkus/src/main/kotlin/theodolite/util/Config.kt
index 15defa6c7127bc82d6ea2b13abe51076e44bd5a9..afbf784e9d6d72939615e367b54891ecd95a3608 100644
--- a/theodolite-quarkus/src/main/kotlin/theodolite/util/Config.kt
+++ b/theodolite-quarkus/src/main/kotlin/theodolite/util/Config.kt
@@ -3,6 +3,13 @@ package theodolite.util
 import io.quarkus.runtime.annotations.RegisterForReflection
 import theodolite.strategies.searchstrategy.CompositeStrategy
 
+/**
+ * Config class that represents a configuration of a theodolite run.
+ *
+ * @param loads the [LoadDimension] of the execution
+ * @param resources the [Resource] of the execution
+ * @param compositeStrategy the [CompositeStrategy] of the execution
+ */
 @RegisterForReflection
 data class Config(
     val loads: List<LoadDimension>,
diff --git a/theodolite-quarkus/src/main/kotlin/theodolite/util/ConfigurationOverride.kt b/theodolite-quarkus/src/main/kotlin/theodolite/util/ConfigurationOverride.kt
index c4801dfbab0beb01cab025200cceab17efb0053d..537b44721bb344c2cd7af71d29dc4fa3da5a7a33 100644
--- a/theodolite-quarkus/src/main/kotlin/theodolite/util/ConfigurationOverride.kt
+++ b/theodolite-quarkus/src/main/kotlin/theodolite/util/ConfigurationOverride.kt
@@ -3,9 +3,19 @@ package theodolite.util
 import com.fasterxml.jackson.databind.annotation.JsonDeserialize
 import io.quarkus.runtime.annotations.RegisterForReflection
 
+/**
+ * Representation of a configuration override.
+ */
 @JsonDeserialize
 @RegisterForReflection
 class ConfigurationOverride {
+    /**
+     * Patcher of the configuration override.
+     */
     lateinit var patcher: PatcherDefinition
+
+    /**
+     * Value of the patched configuration override.
+     */
     lateinit var value: String
 }
diff --git a/theodolite-quarkus/src/main/kotlin/theodolite/util/KafkaConfig.kt b/theodolite-quarkus/src/main/kotlin/theodolite/util/KafkaConfig.kt
index cb94c8b7bfac4c3bc043e04a67d673ccaddea3c5..f304d8cd06969d4650329b9b9f410a56985a2002 100644
--- a/theodolite-quarkus/src/main/kotlin/theodolite/util/KafkaConfig.kt
+++ b/theodolite-quarkus/src/main/kotlin/theodolite/util/KafkaConfig.kt
@@ -4,21 +4,64 @@ import com.fasterxml.jackson.databind.annotation.JsonDeserialize
 import io.quarkus.runtime.annotations.RegisterForReflection
 import org.apache.kafka.clients.admin.NewTopic
 import kotlin.properties.Delegates
+import kotlin.reflect.KProperty
 
+/**
+ * Configuration of Kafka connection.
+ *
+ * @see TopicWrapper
+ */
 @RegisterForReflection
 @JsonDeserialize
 class KafkaConfig {
+    /**
+     * The bootstrap server connection string
+     */
     lateinit var bootstrapServer: String
-    lateinit var topics: List<TopicWrapper>
 
-    fun getKafkaTopics(): List<NewTopic> {
-        return topics.map { topic -> NewTopic(topic.name, topic.numPartitions, topic.replicationFactor) }
-    }
+    /**
+     * The list of topics
+     */
+    lateinit var topics: List<TopicWrapper>
 
+    /**
+     * Wrapper for a topic definition.
+     */
     @RegisterForReflection
     class TopicWrapper {
+        /**
+         * The topic name
+         */
         lateinit var name: String
+
+        /**
+         * The number of partitions
+         */
         var numPartitions by Delegates.notNull<Int>()
+
+        /**
+         * The replication factor of this topic
+         */
         var replicationFactor by Delegates.notNull<Short>()
+
+        /**
+         * If remove only, this topic would only used to delete all topics, which has the name of the topic as a prefix.
+         */
+        var removeOnly by DelegatesFalse()
     }
 }
+
+/**
+ * Delegates to initialize a lateinit boolean to false
+ */
+@RegisterForReflection
+class DelegatesFalse {
+    private var state = false
+    operator fun getValue(thisRef: Any?, property: KProperty<*>): Boolean {
+        return state
+    }
+    operator fun setValue(thisRef: Any?, property: KProperty<*>, value: Boolean) {
+        state = value
+    }
+
+}
diff --git a/theodolite-quarkus/src/main/kotlin/theodolite/util/LoadDimension.kt b/theodolite-quarkus/src/main/kotlin/theodolite/util/LoadDimension.kt
index 4f092e5f9dd8fed8f4a6229eecf4f8a26a2e7e76..cf26da979b05f0a2bd82289ce371715ea0d67c93 100644
--- a/theodolite-quarkus/src/main/kotlin/theodolite/util/LoadDimension.kt
+++ b/theodolite-quarkus/src/main/kotlin/theodolite/util/LoadDimension.kt
@@ -2,13 +2,24 @@ package theodolite.util
 
 import io.quarkus.runtime.annotations.RegisterForReflection
 
-
+/**
+ * Representation of the load dimensions for a execution of theodolite.
+ *
+ * @param number the value of this [LoadDimension]
+ * @param type [PatcherDefinition] of this [LoadDimension]
+ */
 @RegisterForReflection
 data class LoadDimension(private val number: Int, private val type: List<PatcherDefinition>) {
+    /**
+     * @return the value of this load dimension.
+     */
     fun get(): Int {
         return this.number
     }
 
+    /**
+     * @return the list of [PatcherDefinition]
+     */
     fun getType(): List<PatcherDefinition> {
         return this.type
     }
diff --git a/theodolite-quarkus/src/main/kotlin/theodolite/util/Parser.kt b/theodolite-quarkus/src/main/kotlin/theodolite/util/Parser.kt
index 886fd0b1f7a8a9a2219c74197ebb878f6d87775e..e435b1cbbf18b9f860ceda69f5f7ec66e64c9375 100644
--- a/theodolite-quarkus/src/main/kotlin/theodolite/util/Parser.kt
+++ b/theodolite-quarkus/src/main/kotlin/theodolite/util/Parser.kt
@@ -1,5 +1,16 @@
 package theodolite.util
 
+/**
+ * Interface for parsers.
+ * A parser allows the reading of files and creates a corresponding object from them.
+ */
 interface Parser {
+    /**
+     * Parse a file.
+     *
+     * @param path The path of the file
+     * @param E The class of the type to parse
+     * @param T The type to parse
+     */
     fun <T> parse(path: String, E: Class<T>): T?
 }
diff --git a/theodolite-quarkus/src/main/kotlin/theodolite/util/PatcherDefinition.kt b/theodolite-quarkus/src/main/kotlin/theodolite/util/PatcherDefinition.kt
index e78048cd7691f8bfd14f663d401384ae6c329d4b..b24f887d6ff6e3096a2e740f541861d76804775b 100644
--- a/theodolite-quarkus/src/main/kotlin/theodolite/util/PatcherDefinition.kt
+++ b/theodolite-quarkus/src/main/kotlin/theodolite/util/PatcherDefinition.kt
@@ -3,11 +3,29 @@ package theodolite.util
 import com.fasterxml.jackson.databind.annotation.JsonDeserialize
 import io.quarkus.runtime.annotations.RegisterForReflection
 
+/**
+ * Definition of the structure of a [theodolite.patcher.AbstractPatcher] which implements the [theodolite.patcher.Patcher] interface.
+ */
 @JsonDeserialize
 @RegisterForReflection
 class PatcherDefinition {
+    /**
+     * The type of the patcher
+     */
     lateinit var type: String
+
+    /**
+     * The resource which the patcher is applied to
+     */
     lateinit var resource: String
+
+    /**
+     * The container which the patcher is applied to
+     */
     lateinit var container: String
+
+    /**
+     * The variable name for the patcher
+     */
     lateinit var variableName: String
 }
diff --git a/theodolite-quarkus/src/main/kotlin/theodolite/util/PrometheusResponse.kt b/theodolite-quarkus/src/main/kotlin/theodolite/util/PrometheusResponse.kt
index ca26ad117034b160e48dc950e80062100a1c68d8..d1d59c482e64fd14c4744d8fcd606f286da24fb4 100644
--- a/theodolite-quarkus/src/main/kotlin/theodolite/util/PrometheusResponse.kt
+++ b/theodolite-quarkus/src/main/kotlin/theodolite/util/PrometheusResponse.kt
@@ -2,25 +2,58 @@ package theodolite.util
 
 import io.quarkus.runtime.annotations.RegisterForReflection
 
+/**
+ * This class corresponds to the JSON response format of a Prometheus
+ * [range-query](https://www.prometheus.io/docs/prometheus/latest/querying/api/#range-queries)
+ */
 @RegisterForReflection
 data class PrometheusResponse(
+    /**
+     * Indicates whether the query was successful.
+     */
     var status: String? = null,
+    /**
+     * The data section of the query result contains the information about the resultType and the values itself.
+     */
     var data: PromData? = null
 )
 
+/**
+ * Description of Prometheus data.
+ *
+ * Based on [PromResult]
+ */
 @RegisterForReflection
 data class PromData(
+    /**
+     * Type of the result, either  "matrix" | "vector" | "scalar" | "string"
+     */
     var resultType: String? = null,
+    /**
+     * Result of the range-query. In the case of range-query this corresponds to the [range-vectors result format](https://www.prometheus.io/docs/prometheus/latest/querying/api/#range-vectors)
+     */
     var result: List<PromResult>? = null
 )
 
+/**
+ * PromResult corresponds to the [range-vectors result format](https://www.prometheus.io/docs/prometheus/latest/querying/api/#range-vectors)
+ */
 @RegisterForReflection
 data class PromResult(
+    /**
+     * Label of the metric
+     */
     var metric: PromMetric? = null,
+    /**
+     *  Values of the metric (e.g. [ [ <unix_time>, "<sample_value>" ], ... ])
+     */
     var values: List<Any>? = null
 )
 
+/**
+ * Corresponds to the metric field in the range-vector result format of a Prometheus range-query response.
+ */
 @RegisterForReflection
 data class PromMetric(
     var group: String? = null
-)
+)
\ No newline at end of file
diff --git a/theodolite-quarkus/src/main/kotlin/theodolite/util/Resource.kt b/theodolite-quarkus/src/main/kotlin/theodolite/util/Resource.kt
index 59acf175e31b2707b236b421d2055bb14a49ca1a..1d6410aa4288e19817e3ba48bfd1bc0d85d006a2 100644
--- a/theodolite-quarkus/src/main/kotlin/theodolite/util/Resource.kt
+++ b/theodolite-quarkus/src/main/kotlin/theodolite/util/Resource.kt
@@ -2,13 +2,22 @@ package theodolite.util
 
 import io.quarkus.runtime.annotations.RegisterForReflection
 
+/**
+ * Representation of the resources for an execution of Theodolite.
+ */
 @RegisterForReflection
 data class Resource(private val number: Int, private val type: List<PatcherDefinition>) {
 
+    /**
+     * @return the value of this resource.
+     */
     fun get(): Int {
         return this.number
     }
 
+    /**
+     * @return the list of [PatcherDefinition]
+     */
     fun getType(): List<PatcherDefinition> {
         return this.type
     }
diff --git a/theodolite-quarkus/src/main/kotlin/theodolite/util/Results.kt b/theodolite-quarkus/src/main/kotlin/theodolite/util/Results.kt
index a8a902ff69742bbb617685ac5cd24bf6c419c370..60641ea0248435de53aaaaf362da7be995b391c5 100644
--- a/theodolite-quarkus/src/main/kotlin/theodolite/util/Results.kt
+++ b/theodolite-quarkus/src/main/kotlin/theodolite/util/Results.kt
@@ -2,44 +2,82 @@ package theodolite.util
 
 import io.quarkus.runtime.annotations.RegisterForReflection
 
+/**
+ * Central class that saves the state of a execution of Theodolite. For an execution, it is used to save the result of
+ * individual experiments. Further, it is used by the RestrictionStrategy to
+ * perform the [theodolite.strategies.restriction.RestrictionStrategy].
+ */
 @RegisterForReflection
 class Results {
     private val results: MutableMap<Pair<LoadDimension, Resource>, Boolean> = mutableMapOf()
 
+    /**
+     * Set the result for an experiment.
+     *
+     * @param experiment A pair that identifies the experiment by the [LoadDimension] and [Resource].
+     * @param successful the result of the experiment. Successful == true and Unsuccessful == false.
+     */
     fun setResult(experiment: Pair<LoadDimension, Resource>, successful: Boolean) {
         this.results[experiment] = successful
     }
 
+    /**
+     * Get the result for an experiment.
+     *
+     * @param experiment A pair that identifies the experiment by the [LoadDimension] and [Resource].
+     * @return true if the experiment was successful and false otherwise. If the result has not been reported so far,
+     * null is returned.
+     *
+     * @see Resource
+     */
     fun getResult(experiment: Pair<LoadDimension, Resource>): Boolean? {
         return this.results[experiment]
     }
 
+    /**
+     * Get the smallest suitable number of instances for a specified [LoadDimension].
+     *
+     * @param load the [LoadDimension]
+     *
+     * @return the smallest suitable number of resources. If the experiment was not executed yet,
+     * a @see Resource with the constant Int.MAX_VALUE as value is returned.
+     * If no experiments have been marked as either successful or unsuccessful
+     * yet, a Resource with the constant value Int.MIN_VALUE is returned.
+     */
     fun getMinRequiredInstances(load: LoadDimension?): Resource? {
-        if (this.results.isEmpty()) return Resource(Int.MIN_VALUE, emptyList())
+        if (this.results.isEmpty()) {
+            return Resource(Int.MIN_VALUE, emptyList())
+        }
 
-        var requiredInstances: Resource? = Resource(Int.MAX_VALUE, emptyList())
+        var minRequiredInstances: Resource? = Resource(Int.MAX_VALUE, emptyList())
         for (experiment in results) {
+            // Get all successful experiments for requested load
             if (experiment.key.first == load && experiment.value) {
-                if (requiredInstances == null) {
-                    requiredInstances = experiment.key.second
-                } else if (experiment.key.second.get() < requiredInstances.get()) {
-                    requiredInstances = experiment.key.second
+                if (minRequiredInstances == null || experiment.key.second.get() < minRequiredInstances.get()) {
+                    // Found new smallest resources
+                    minRequiredInstances = experiment.key.second
                 }
             }
         }
-        return requiredInstances
+        return minRequiredInstances
     }
 
+    /**
+     * Get the largest [LoadDimension] that has been reported executed successfully (or unsuccessfully) so far, for a
+     * [LoadDimension] and is smaller than the given [LoadDimension].
+     *
+     * @param load the [LoadDimension]
+     *
+     * @return the largest [LoadDimension] or null, if there is none for this [LoadDimension]
+     */
     fun getMaxBenchmarkedLoad(load: LoadDimension): LoadDimension? {
         var maxBenchmarkedLoad: LoadDimension? = null
         for (experiment in results) {
-            if (experiment.value) {
-                if (experiment.key.first.get() <= load.get()) {
-                    if (maxBenchmarkedLoad == null) {
-                        maxBenchmarkedLoad = experiment.key.first
-                    } else if (maxBenchmarkedLoad.get() < experiment.key.first.get()) {
-                        maxBenchmarkedLoad = experiment.key.first
-                    }
+            if (experiment.key.first.get() <= load.get()) {
+                if (maxBenchmarkedLoad == null) {
+                    maxBenchmarkedLoad = experiment.key.first
+                } else if (maxBenchmarkedLoad.get() < experiment.key.first.get()) {
+                    maxBenchmarkedLoad = experiment.key.first
                 }
             }
         }
diff --git a/theodolite-quarkus/src/main/kotlin/theodolite/util/TypeName.kt b/theodolite-quarkus/src/main/kotlin/theodolite/util/TypeName.kt
index 20758466c5d5efbcb999bf0bf9c4edbe63ea1032..f20fc7c9ce6757be75d9317e76c23a68b09914bd 100644
--- a/theodolite-quarkus/src/main/kotlin/theodolite/util/TypeName.kt
+++ b/theodolite-quarkus/src/main/kotlin/theodolite/util/TypeName.kt
@@ -3,6 +3,9 @@ package theodolite.util
 import com.fasterxml.jackson.databind.annotation.JsonDeserialize
 import io.quarkus.runtime.annotations.RegisterForReflection
 
+/**
+ * The TypeName encapsulates a list of [PatcherDefinition] along with a typeName that specifies for what the [PatcherDefinition] should be used.
+ */
 @RegisterForReflection
 @JsonDeserialize
 class TypeName {
diff --git a/theodolite-quarkus/src/main/kotlin/theodolite/util/YamlParser.kt b/theodolite-quarkus/src/main/kotlin/theodolite/util/YamlParser.kt
index ec91150df6c9999c418660424aa8b74163030e34..ce69894e4145372aef07286ae315d11631a4df3f 100644
--- a/theodolite-quarkus/src/main/kotlin/theodolite/util/YamlParser.kt
+++ b/theodolite-quarkus/src/main/kotlin/theodolite/util/YamlParser.kt
@@ -6,6 +6,9 @@ import java.io.File
 import java.io.FileInputStream
 import java.io.InputStream
 
+/**
+ * The YamlParser parses a YAML file
+ */
 class YamlParser : Parser {
     override fun <T> parse(path: String, E: Class<T>): T? {
         val input: InputStream = FileInputStream(File(path))
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 9fc16f92e303f05a449f7e8b83600c3b299f215d..19ec972be8236fbdcad123e9c9ef63945bb53d16 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
@@ -26,4 +26,6 @@ kafkaConfig:
   topics:
     - name: "input"
       numPartitions: 40
-      replicationFactor: 1
\ No newline at end of file
+      replicationFactor: 1
+    - name: "theodolite-.*"
+      removeOnly: True
\ No newline at end of file
diff --git a/theodolite-quarkus/src/test/kotlin/theodolite/CompositeStrategyTest.kt b/theodolite-quarkus/src/test/kotlin/theodolite/CompositeStrategyTest.kt
index 67c9857220a0d419183644ffaf8c6a6e16a6ce9b..7802529bfda309131cafc0ab3f39fda43285c32f 100644
--- a/theodolite-quarkus/src/test/kotlin/theodolite/CompositeStrategyTest.kt
+++ b/theodolite-quarkus/src/test/kotlin/theodolite/CompositeStrategyTest.kt
@@ -31,7 +31,7 @@ class CompositeStrategyTest {
         val results = Results()
         val benchmark = TestBenchmark()
         val sloChecker: BenchmarkExecution.Slo = BenchmarkExecution.Slo()
-        val benchmarkExecutor = TestBenchmarkExecutorImpl(mockResults, benchmark, results, sloChecker)
+        val benchmarkExecutor = TestBenchmarkExecutorImpl(mockResults, benchmark, results, sloChecker, 0)
         val linearSearch = LinearSearch(benchmarkExecutor)
         val lowerBoundRestriction = LowerBoundRestriction(results)
         val strategy =
@@ -65,7 +65,7 @@ class CompositeStrategyTest {
         val benchmark = TestBenchmark()
         val sloChecker: BenchmarkExecution.Slo = BenchmarkExecution.Slo()
         val benchmarkExecutorImpl =
-            TestBenchmarkExecutorImpl(mockResults, benchmark, results, sloChecker)
+            TestBenchmarkExecutorImpl(mockResults, benchmark, results, sloChecker, 0)
         val binarySearch = BinarySearch(benchmarkExecutorImpl)
         val lowerBoundRestriction = LowerBoundRestriction(results)
         val strategy =
@@ -98,7 +98,7 @@ class CompositeStrategyTest {
         val results = Results()
         val benchmark = TestBenchmark()
         val sloChecker: BenchmarkExecution.Slo = BenchmarkExecution.Slo()
-        val benchmarkExecutor = TestBenchmarkExecutorImpl(mockResults, benchmark, results, sloChecker)
+        val benchmarkExecutor = TestBenchmarkExecutorImpl(mockResults, benchmark, results, sloChecker, 0)
         val binarySearch = BinarySearch(benchmarkExecutor)
         val lowerBoundRestriction = LowerBoundRestriction(results)
         val strategy =
diff --git a/theodolite-quarkus/src/test/kotlin/theodolite/TestBenchmarkExecutorImpl.kt b/theodolite-quarkus/src/test/kotlin/theodolite/TestBenchmarkExecutorImpl.kt
index 8930c4bda2adf0c2ac350ef9d406f97bfd7deb31..26ed5bf1c2601a663b1f48fc2e6fae57b7998a18 100644
--- a/theodolite-quarkus/src/test/kotlin/theodolite/TestBenchmarkExecutorImpl.kt
+++ b/theodolite-quarkus/src/test/kotlin/theodolite/TestBenchmarkExecutorImpl.kt
@@ -12,7 +12,8 @@ class TestBenchmarkExecutorImpl(
     private val mockResults: Array<Array<Boolean>>,
     benchmark: Benchmark,
     results: Results,
-    slo: BenchmarkExecution.Slo
+    slo: BenchmarkExecution.Slo,
+    executionId: Int
 ) :
     BenchmarkExecutor(
         benchmark,
@@ -20,7 +21,8 @@ class TestBenchmarkExecutorImpl(
         executionDuration = Duration.ofSeconds(1),
         configurationOverrides = emptyList(),
         slo = slo,
-        repetitions = 1
+        repetitions = 1,
+        executionId = executionId
     ) {
 
     override fun runExperiment(load: LoadDimension, res: Resource): Boolean {
diff --git a/theodolite-quarkus/src/test/kotlin/theodolite/strategies/restriction/LowerBoundRestrictionTest.kt b/theodolite-quarkus/src/test/kotlin/theodolite/strategies/restriction/LowerBoundRestrictionTest.kt
new file mode 100644
index 0000000000000000000000000000000000000000..b6b9eaecd9fc35b25b9447611835c3d8469cea0e
--- /dev/null
+++ b/theodolite-quarkus/src/test/kotlin/theodolite/strategies/restriction/LowerBoundRestrictionTest.kt
@@ -0,0 +1,116 @@
+package theodolite.strategies.restriction
+
+import org.junit.jupiter.api.Assertions.*
+import org.junit.jupiter.api.Disabled
+import org.junit.jupiter.api.Test
+import theodolite.util.LoadDimension
+import theodolite.util.Resource
+import theodolite.util.Results
+
+internal class LowerBoundRestrictionTest {
+
+    @Test
+    fun testNoPreviousResults() {
+        val results = Results()
+        val strategy = LowerBoundRestriction(results)
+        val load = buildLoadDimension(10000)
+        val resources = listOf(
+            buildResourcesDimension(1),
+            buildResourcesDimension(2),
+            buildResourcesDimension(3)
+        )
+        val restriction = strategy.apply(load, resources)
+
+        assertEquals(3, restriction.size)
+        assertEquals(resources, restriction)
+    }
+
+    @Test
+    fun testWithSuccessfulPreviousResults() {
+        val results = Results()
+        results.setResult(10000, 1, true)
+        results.setResult(20000, 1, false)
+        results.setResult(20000, 2, true)
+        val strategy = LowerBoundRestriction(results)
+        val load = buildLoadDimension(30000)
+        val resources = listOf(
+            buildResourcesDimension(1),
+            buildResourcesDimension(2),
+            buildResourcesDimension(3)
+        )
+        val restriction = strategy.apply(load, resources)
+
+        assertEquals(2, restriction.size)
+        assertEquals(resources.subList(1,3), restriction)
+    }
+
+    @Test
+    @Disabled
+    fun testWithNoSuccessfulPreviousResults() {
+        // This test is currently not implemented this way, but might later be the desired behavior.
+        val results = Results()
+        results.setResult(10000, 1, true)
+        results.setResult(20000, 1, false)
+        results.setResult(20000, 2, false)
+        results.setResult(20000, 3, false)
+        val strategy = LowerBoundRestriction(results)
+        val load = buildLoadDimension(30000)
+        val resources = listOf(
+            buildResourcesDimension(1),
+            buildResourcesDimension(2),
+            buildResourcesDimension(3)
+        )
+        val restriction = strategy.apply(load, resources)
+
+        assertEquals(0, restriction.size)
+        assertEquals(emptyList<Resource>(), restriction)
+    }
+
+
+    @Test
+    fun testNoPreviousResults2() {
+        val results = Results()
+        results.setResult(10000, 1, true)
+        results.setResult(20000, 2, true)
+        results.setResult(10000, 1, false)
+        results.setResult(20000, 2, true)
+
+        val minRequiredInstances = results.getMinRequiredInstances(LoadDimension(20000, emptyList()))
+
+        assertNotNull(minRequiredInstances)
+        assertEquals(2, minRequiredInstances!!.get())
+    }
+
+    @Test
+    @Disabled
+    fun testMinRequiredInstancesWhenNotSuccessful() {
+        // This test is currently not implemented this way, but might later be the desired behavior.
+        val results = Results()
+        results.setResult(10000, 1, true)
+        results.setResult(20000, 2, true)
+        results.setResult(10000, 1, false)
+        results.setResult(20000, 2, false)
+
+        val minRequiredInstances = results.getMinRequiredInstances(LoadDimension(20000, emptyList()))
+
+        assertNotNull(minRequiredInstances)
+        assertEquals(2, minRequiredInstances!!.get())
+    }
+
+    private fun buildLoadDimension(load: Int): LoadDimension {
+        return LoadDimension(load, emptyList())
+    }
+
+    private fun buildResourcesDimension(resources: Int): Resource {
+        return Resource(resources, emptyList())
+    }
+
+    private fun Results.setResult(load: Int, resources: Int, successful: Boolean) {
+        this.setResult(
+            Pair(
+                buildLoadDimension(load),
+                buildResourcesDimension(resources)
+            ),
+            successful)
+    }
+}
\ No newline at end of file
diff --git a/theodolite-quarkus/src/test/kotlin/theodolite/util/ResultsTest.kt b/theodolite-quarkus/src/test/kotlin/theodolite/util/ResultsTest.kt
new file mode 100644
index 0000000000000000000000000000000000000000..9cfc2ae78e7a8846e3f0fa136699509145e5de22
--- /dev/null
+++ b/theodolite-quarkus/src/test/kotlin/theodolite/util/ResultsTest.kt
@@ -0,0 +1,75 @@
+package theodolite.util
+
+import io.quarkus.test.junit.QuarkusTest
+import org.junit.jupiter.api.Assertions.assertEquals
+import org.junit.jupiter.api.Assertions.assertNotNull
+import org.junit.jupiter.api.Disabled
+import org.junit.jupiter.api.Test
+
+@QuarkusTest
+internal class ResultsTest {
+
+    @Test
+    fun testMinRequiredInstancesWhenSuccessful() {
+        val results = Results()
+        results.setResult(10000, 1, true)
+        results.setResult(10000, 2, true)
+        results.setResult(20000, 1, false)
+        results.setResult(20000, 2, true)
+
+        val minRequiredInstances = results.getMinRequiredInstances(LoadDimension(20000, emptyList()))
+
+        assertNotNull(minRequiredInstances)
+        assertEquals(2, minRequiredInstances!!.get())
+    }
+
+    @Test
+    @Disabled
+    fun testMinRequiredInstancesWhenNotSuccessful() {
+        // This test is currently not implemented this way, but might later be the desired behavior.
+        val results = Results()
+        results.setResult(10000, 1, true)
+        results.setResult(10000, 2, true)
+        results.setResult(20000, 1, false)
+        results.setResult(20000, 2, false)
+
+        val minRequiredInstances = results.getMinRequiredInstances(LoadDimension(20000, emptyList()))
+
+        assertNotNull(minRequiredInstances)
+        assertEquals(2, minRequiredInstances!!.get())
+    }
+
+    private fun Results.setResult(load: Int, resources: Int, successful: Boolean) {
+        this.setResult(
+            Pair(
+                LoadDimension(load, emptyList()),
+                Resource(resources, emptyList())
+            ),
+            successful
+        )
+    }
+
+
+    @Test
+    fun testGetMaxBenchmarkedLoadWhenAllSuccessful() {
+        val results = Results()
+        results.setResult(10000, 1, true)
+        results.setResult(10000, 2, true)
+
+        val test1 = results.getMaxBenchmarkedLoad(LoadDimension(100000, emptyList()))!!.get()
+
+        assertEquals(10000, test1)
+    }
+
+    @Test
+    fun testGetMaxBenchmarkedLoadWhenLargestNotSuccessful() {
+        val results = Results()
+        results.setResult(10000, 1, true)
+        results.setResult(10000, 2, true)
+        results.setResult(20000, 1, false)
+
+        val test2 = results.getMaxBenchmarkedLoad(LoadDimension(100000, emptyList()))!!.get()
+
+        assertEquals(20000, test2)
+    }
+}