From 7cf3de302a9247975bdfe9108f63a356ebbcdcca Mon Sep 17 00:00:00 2001
From: "stu126940@mail.uni-kiel.de" <stu126940@mail.uni-kiel.de>
Date: Mon, 8 Mar 2021 12:37:47 +0100
Subject: [PATCH] Minor code changes, add test for ResourcePatcher

---
 .../patcher/ResourceLimitPatcher.kt           | 17 ++--
 .../patcher/ResourceRequestPatcher.kt         | 16 ++--
 .../resources/testYaml/cpu-deployment.yaml    | 56 +++++++++++++
 .../testYaml/cpu-memory-deployment.yaml       | 58 +++++++++++++
 .../resources/testYaml/memory-deployment.yaml | 56 +++++++++++++
 .../testYaml/no-resources-deployment.yaml     | 51 +++++++++++
 .../theodolite/ResourceLimitPatcherTest.kt    | 84 +++++++++++++++++++
 .../theodolite/ResourceRequestPatcherTest.kt  | 84 +++++++++++++++++++
 8 files changed, 408 insertions(+), 14 deletions(-)
 create mode 100644 theodolite-quarkus/src/main/resources/testYaml/cpu-deployment.yaml
 create mode 100644 theodolite-quarkus/src/main/resources/testYaml/cpu-memory-deployment.yaml
 create mode 100644 theodolite-quarkus/src/main/resources/testYaml/memory-deployment.yaml
 create mode 100644 theodolite-quarkus/src/main/resources/testYaml/no-resources-deployment.yaml
 create mode 100644 theodolite-quarkus/src/test/kotlin/theodolite/ResourceLimitPatcherTest.kt
 create mode 100644 theodolite-quarkus/src/test/kotlin/theodolite/ResourceRequestPatcherTest.kt

diff --git a/theodolite-quarkus/src/main/kotlin/theodolite/patcher/ResourceLimitPatcher.kt b/theodolite-quarkus/src/main/kotlin/theodolite/patcher/ResourceLimitPatcher.kt
index d3ca8e656..752e4703b 100644
--- a/theodolite-quarkus/src/main/kotlin/theodolite/patcher/ResourceLimitPatcher.kt
+++ b/theodolite-quarkus/src/main/kotlin/theodolite/patcher/ResourceLimitPatcher.kt
@@ -4,6 +4,7 @@ import io.fabric8.kubernetes.api.model.KubernetesResource
 import io.fabric8.kubernetes.api.model.Quantity
 import io.fabric8.kubernetes.api.model.ResourceRequirements
 import io.fabric8.kubernetes.api.model.apps.Deployment
+import javax.validation.constraints.Null
 
 class ResourceLimitPatcher(
     private val k8sResource: KubernetesResource,
@@ -14,19 +15,21 @@ class ResourceLimitPatcher(
     override fun <String> patch(value: String) {
         if (k8sResource is Deployment) {
             k8sResource.spec.template.spec.containers.filter { it.name == container }.forEach {
-                try {
-                    if (it.resources.limits.isEmpty()) {
+                when {
+                    it.resources == null -> {
+                        val resource = ResourceRequirements()
+                        resource.limits = mapOf(limitedResource to Quantity(value as kotlin.String))
+                        it.resources = resource
+                    }
+                    it.resources.limits.isEmpty() -> {
                         it.resources.limits = mapOf(limitedResource to Quantity(value as kotlin.String))
-                    } else {
+                    }
+                    else -> {
                         val values = mutableMapOf<kotlin.String, Quantity>()
                         it.resources.limits.forEach { entry -> values[entry.key] = entry.value }
                         values[limitedResource] = Quantity(value as kotlin.String)
                         it.resources.limits = values
                     }
-                } catch (e: IllegalStateException) {
-                    val resource = ResourceRequirements()
-                    resource.limits = mapOf(limitedResource to Quantity(value as kotlin.String))
-                    it.resources = resource
                 }
             }
         }
diff --git a/theodolite-quarkus/src/main/kotlin/theodolite/patcher/ResourceRequestPatcher.kt b/theodolite-quarkus/src/main/kotlin/theodolite/patcher/ResourceRequestPatcher.kt
index d6090875e..865d350bb 100644
--- a/theodolite-quarkus/src/main/kotlin/theodolite/patcher/ResourceRequestPatcher.kt
+++ b/theodolite-quarkus/src/main/kotlin/theodolite/patcher/ResourceRequestPatcher.kt
@@ -14,19 +14,21 @@ class ResourceRequestPatcher(
     override fun <String> patch(value: String) {
         if (k8sResource is Deployment) {
             k8sResource.spec.template.spec.containers.filter { it.name == container }.forEach {
-                try {
-                    if (it.resources.requests.isEmpty()) {
+                when {
+                    it.resources == null -> {
+                        val resource = ResourceRequirements()
+                        resource.requests = mapOf(requestedResource to Quantity(value as kotlin.String))
+                        it.resources = resource
+                    }
+                    it.resources.requests.isEmpty() -> {
                         it.resources.requests = mapOf(requestedResource to Quantity(value as kotlin.String))
-                    } else {
+                    }
+                    else -> {
                         val values = mutableMapOf<kotlin.String, Quantity>()
                         it.resources.requests.forEach { entry -> values[entry.key] = entry.value }
                         values[requestedResource] = Quantity(value as kotlin.String)
                         it.resources.requests = values
                     }
-                } catch (e: IllegalStateException) {
-                    val resource = ResourceRequirements()
-                    resource.requests = mapOf(requestedResource to Quantity(value as kotlin.String))
-                    it.resources = resource
                 }
             }
         }
diff --git a/theodolite-quarkus/src/main/resources/testYaml/cpu-deployment.yaml b/theodolite-quarkus/src/main/resources/testYaml/cpu-deployment.yaml
new file mode 100644
index 000000000..984564894
--- /dev/null
+++ b/theodolite-quarkus/src/main/resources/testYaml/cpu-deployment.yaml
@@ -0,0 +1,56 @@
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+  name: titan-ccp-aggregation
+spec:
+  selector:
+    matchLabels:
+      app: titan-ccp-aggregation
+  replicas: 1
+  template:
+    metadata:
+      labels:
+        app: titan-ccp-aggregation
+    spec:
+      terminationGracePeriodSeconds: 0
+      containers:
+        - name: uc-application
+          image: ghcr.io/cau-se/theodolite-uc1-kstreams-app:latest
+          ports:
+            - containerPort: 5555
+              name: jmx
+          env:
+            - name: KAFKA_BOOTSTRAP_SERVERS
+              value: "my-confluent-cp-kafka:9092"
+            - name: SCHEMA_REGISTRY_URL
+              value: "http://my-confluent-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
+              value: "100"
+          resources:
+            limits:
+              cpu: 1000m
+            requests:
+              cpu: 500m
+        - name: prometheus-jmx-exporter
+          image: "solsson/kafka-prometheus-jmx-exporter@sha256:6f82e2b0464f50da8104acd7363fb9b995001ddff77d248379f8788e78946143"
+          command:
+            - java
+            - -XX:+UnlockExperimentalVMOptions
+            - -XX:+UseCGroupMemoryLimitForHeap
+            - -XX:MaxRAMFraction=1
+            - -XshowSettings:vm
+            - -jar
+            - jmx_prometheus_httpserver.jar
+            - "5556"
+            - /etc/jmx-aggregation/jmx-kafka-prometheus.yml
+          ports:
+            - containerPort: 5556
+          volumeMounts:
+            - name: jmx-config
+              mountPath: /etc/jmx-aggregation
+      volumes:
+        - name: jmx-config
+          configMap:
+            name: aggregation-jmx-configmap
\ No newline at end of file
diff --git a/theodolite-quarkus/src/main/resources/testYaml/cpu-memory-deployment.yaml b/theodolite-quarkus/src/main/resources/testYaml/cpu-memory-deployment.yaml
new file mode 100644
index 000000000..eaae989ab
--- /dev/null
+++ b/theodolite-quarkus/src/main/resources/testYaml/cpu-memory-deployment.yaml
@@ -0,0 +1,58 @@
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+  name: titan-ccp-aggregation
+spec:
+  selector:
+    matchLabels:
+      app: titan-ccp-aggregation
+  replicas: 1
+  template:
+    metadata:
+      labels:
+        app: titan-ccp-aggregation
+    spec:
+      terminationGracePeriodSeconds: 0
+      containers:
+        - name: uc-application
+          image: ghcr.io/cau-se/theodolite-uc1-kstreams-app:latest
+          ports:
+            - containerPort: 5555
+              name: jmx
+          env:
+            - name: KAFKA_BOOTSTRAP_SERVERS
+              value: "my-confluent-cp-kafka:9092"
+            - name: SCHEMA_REGISTRY_URL
+              value: "http://my-confluent-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
+              value: "100"
+          resources:
+            limits:
+              memory: 4Gi
+              cpu: 1000m
+            requests:
+              memory: 2Gi
+              cpu: 500m
+        - name: prometheus-jmx-exporter
+          image: "solsson/kafka-prometheus-jmx-exporter@sha256:6f82e2b0464f50da8104acd7363fb9b995001ddff77d248379f8788e78946143"
+          command:
+            - java
+            - -XX:+UnlockExperimentalVMOptions
+            - -XX:+UseCGroupMemoryLimitForHeap
+            - -XX:MaxRAMFraction=1
+            - -XshowSettings:vm
+            - -jar
+            - jmx_prometheus_httpserver.jar
+            - "5556"
+            - /etc/jmx-aggregation/jmx-kafka-prometheus.yml
+          ports:
+            - containerPort: 5556
+          volumeMounts:
+            - name: jmx-config
+              mountPath: /etc/jmx-aggregation
+      volumes:
+        - name: jmx-config
+          configMap:
+            name: aggregation-jmx-configmap
\ No newline at end of file
diff --git a/theodolite-quarkus/src/main/resources/testYaml/memory-deployment.yaml b/theodolite-quarkus/src/main/resources/testYaml/memory-deployment.yaml
new file mode 100644
index 000000000..7af278b8c
--- /dev/null
+++ b/theodolite-quarkus/src/main/resources/testYaml/memory-deployment.yaml
@@ -0,0 +1,56 @@
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+  name: titan-ccp-aggregation
+spec:
+  selector:
+    matchLabels:
+      app: titan-ccp-aggregation
+  replicas: 1
+  template:
+    metadata:
+      labels:
+        app: titan-ccp-aggregation
+    spec:
+      terminationGracePeriodSeconds: 0
+      containers:
+        - name: uc-application
+          image: ghcr.io/cau-se/theodolite-uc1-kstreams-app:latest
+          ports:
+            - containerPort: 5555
+              name: jmx
+          env:
+            - name: KAFKA_BOOTSTRAP_SERVERS
+              value: "my-confluent-cp-kafka:9092"
+            - name: SCHEMA_REGISTRY_URL
+              value: "http://my-confluent-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
+              value: "100"
+          resources:
+            limits:
+              memory: 4Gi
+            requests:
+              memory: 2Gi
+        - name: prometheus-jmx-exporter
+          image: "solsson/kafka-prometheus-jmx-exporter@sha256:6f82e2b0464f50da8104acd7363fb9b995001ddff77d248379f8788e78946143"
+          command:
+            - java
+            - -XX:+UnlockExperimentalVMOptions
+            - -XX:+UseCGroupMemoryLimitForHeap
+            - -XX:MaxRAMFraction=1
+            - -XshowSettings:vm
+            - -jar
+            - jmx_prometheus_httpserver.jar
+            - "5556"
+            - /etc/jmx-aggregation/jmx-kafka-prometheus.yml
+          ports:
+            - containerPort: 5556
+          volumeMounts:
+            - name: jmx-config
+              mountPath: /etc/jmx-aggregation
+      volumes:
+        - name: jmx-config
+          configMap:
+            name: aggregation-jmx-configmap
\ No newline at end of file
diff --git a/theodolite-quarkus/src/main/resources/testYaml/no-resources-deployment.yaml b/theodolite-quarkus/src/main/resources/testYaml/no-resources-deployment.yaml
new file mode 100644
index 000000000..0687a3e04
--- /dev/null
+++ b/theodolite-quarkus/src/main/resources/testYaml/no-resources-deployment.yaml
@@ -0,0 +1,51 @@
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+  name: titan-ccp-aggregation
+spec:
+  selector:
+    matchLabels:
+      app: titan-ccp-aggregation
+  replicas: 1
+  template:
+    metadata:
+      labels:
+        app: titan-ccp-aggregation
+    spec:
+      terminationGracePeriodSeconds: 0
+      containers:
+        - name: uc-application
+          image: ghcr.io/cau-se/theodolite-uc1-kstreams-app:latest
+          ports:
+            - containerPort: 5555
+              name: jmx
+          env:
+            - name: KAFKA_BOOTSTRAP_SERVERS
+              value: "my-confluent-cp-kafka:9092"
+            - name: SCHEMA_REGISTRY_URL
+              value: "http://my-confluent-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
+              value: "100"
+        - name: prometheus-jmx-exporter
+          image: "solsson/kafka-prometheus-jmx-exporter@sha256:6f82e2b0464f50da8104acd7363fb9b995001ddff77d248379f8788e78946143"
+          command:
+            - java
+            - -XX:+UnlockExperimentalVMOptions
+            - -XX:+UseCGroupMemoryLimitForHeap
+            - -XX:MaxRAMFraction=1
+            - -XshowSettings:vm
+            - -jar
+            - jmx_prometheus_httpserver.jar
+            - "5556"
+            - /etc/jmx-aggregation/jmx-kafka-prometheus.yml
+          ports:
+            - containerPort: 5556
+          volumeMounts:
+            - name: jmx-config
+              mountPath: /etc/jmx-aggregation
+      volumes:
+        - name: jmx-config
+          configMap:
+            name: aggregation-jmx-configmap
\ No newline at end of file
diff --git a/theodolite-quarkus/src/test/kotlin/theodolite/ResourceLimitPatcherTest.kt b/theodolite-quarkus/src/test/kotlin/theodolite/ResourceLimitPatcherTest.kt
new file mode 100644
index 000000000..3e7be5bd9
--- /dev/null
+++ b/theodolite-quarkus/src/test/kotlin/theodolite/ResourceLimitPatcherTest.kt
@@ -0,0 +1,84 @@
+package theodolite
+
+import io.fabric8.kubernetes.api.model.apps.Deployment
+import io.fabric8.kubernetes.client.DefaultKubernetesClient
+import io.quarkus.test.junit.QuarkusTest
+import io.smallrye.common.constraint.Assert.assertTrue
+import org.junit.jupiter.api.Test
+import theodolite.k8s.K8sResourceLoader
+import theodolite.patcher.PatcherManager
+import theodolite.util.PatcherDefinition
+
+/**
+ * Resource patcher test
+ *
+ * This class tested 4 scenarios for the ResourceLimitPatcher and the ResourceRequestPatcher. The different test cases specifies four possible situations:
+ * Case 1:  In the given YAML declaration memory and cpu are defined
+ * Case 2:  In the given YAML declaration only cpu is defined
+ * Case 3:  In the given YAML declaration only memory is defined
+ * Case 4:  In the given YAML declaration neither `Resource Request` nor `Request Limit` is defined
+ */
+@QuarkusTest
+class ResourceLimitPatcherTest {
+    val testPath = "./src/main/resources/testYaml/"
+    val loader = K8sResourceLoader(DefaultKubernetesClient().inNamespace(""))
+    val manager = PatcherManager()
+
+
+    fun applyTest(fileName: String) {
+        val CPUValue = "50m"
+        val MEMValue = "3Gi"
+        val k8sResource = loader.loadK8sResource("Deployment", testPath + fileName) as Deployment
+
+        val defCPU = PatcherDefinition()
+        defCPU.variableName = "cpu"
+        defCPU.resource = "cpu-memory-deployment.yaml"
+        defCPU.container = "uc-application"
+        defCPU.type = "ResourceLimitPatcher"
+
+        val defMEM = PatcherDefinition()
+        defMEM.variableName = "memory"
+        defMEM.resource = "cpu-memory-deployment.yaml"
+        defMEM.container = "uc-application"
+        defMEM.type = "ResourceLimitPatcher"
+
+        manager.applyPatcher(
+            patcherDefinition = listOf(defCPU),
+            resources = listOf(Pair("cpu-memory-deployment.yaml", k8sResource)),
+            value = CPUValue
+        )
+        manager.applyPatcher(
+            patcherDefinition = listOf(defMEM),
+            resources = listOf(Pair("cpu-memory-deployment.yaml", k8sResource)),
+            value = MEMValue
+        )
+
+        k8sResource.spec.template.spec.containers.filter { it.name == defCPU.container }
+            .forEach {
+                println(it)
+                assertTrue(it.resources.limits["cpu"].toString() == "$CPUValue")
+                assertTrue(it.resources.limits["memory"].toString() == "$MEMValue")
+            }
+    }
+
+    @Test
+    fun case1() {
+        // Case 1: In the given YAML declaration memory and cpu are defined
+        applyTest("cpu-memory-deployment.yaml")
+    }
+    @Test
+    fun case2() {
+        // Case 2:  In the given YAML declaration only cpu is defined
+        applyTest("cpu-deployment.yaml")
+    }
+    @Test
+    fun case3() {
+        //  Case 3:  In the given YAML declaration only memory is defined
+        applyTest("memory-deployment.yaml")
+    }
+    @Test
+    fun case4() {
+        // Case 4: In the given YAML declaration neither `Resource Request` nor `Request Limit` is defined
+        applyTest("no-resources-deployment.yaml")
+    }
+}
\ No newline at end of file
diff --git a/theodolite-quarkus/src/test/kotlin/theodolite/ResourceRequestPatcherTest.kt b/theodolite-quarkus/src/test/kotlin/theodolite/ResourceRequestPatcherTest.kt
new file mode 100644
index 000000000..10ff5320e
--- /dev/null
+++ b/theodolite-quarkus/src/test/kotlin/theodolite/ResourceRequestPatcherTest.kt
@@ -0,0 +1,84 @@
+package theodolite
+
+import io.fabric8.kubernetes.api.model.apps.Deployment
+import io.fabric8.kubernetes.client.DefaultKubernetesClient
+import io.quarkus.test.junit.QuarkusTest
+import io.smallrye.common.constraint.Assert.assertTrue
+import org.junit.jupiter.api.Test
+import theodolite.k8s.K8sResourceLoader
+import theodolite.patcher.PatcherManager
+import theodolite.util.PatcherDefinition
+
+/**
+ * Resource patcher test
+ *
+ * This class tested 4 scenarios for the ResourceLimitPatcher and the ResourceRequestPatcher. The different test cases specifies four possible situations:
+ * Case 1:  In the given YAML declaration memory and cpu are defined
+ * Case 2:  In the given YAML declaration only cpu is defined
+ * Case 3:  In the given YAML declaration only memory is defined
+ * Case 4:  In the given YAML declaration neither `Resource Request` nor `Request Limit` is defined
+ */
+@QuarkusTest
+class ResourceRequestPatcherTest {
+    val testPath = "./src/main/resources/testYaml/"
+    val loader = K8sResourceLoader(DefaultKubernetesClient().inNamespace(""))
+    val manager = PatcherManager()
+
+
+    fun applyTest(fileName: String) {
+        val CPUValue = "50m"
+        val MEMValue = "3Gi"
+        val k8sResource = loader.loadK8sResource("Deployment", testPath + fileName) as Deployment
+
+        val defCPU = PatcherDefinition()
+        defCPU.variableName = "cpu"
+        defCPU.resource = "cpu-memory-deployment.yaml"
+        defCPU.container = "uc-application"
+        defCPU.type = "ResourceRequestPatcher"
+
+        val defMEM = PatcherDefinition()
+        defMEM.variableName = "memory"
+        defMEM.resource = "cpu-memory-deployment.yaml"
+        defMEM.container = "uc-application"
+        defMEM.type = "ResourceRequestPatcher"
+
+        manager.applyPatcher(
+            patcherDefinition = listOf(defCPU),
+            resources = listOf(Pair("cpu-memory-deployment.yaml", k8sResource)),
+            value = CPUValue
+        )
+        manager.applyPatcher(
+            patcherDefinition = listOf(defMEM),
+            resources = listOf(Pair("cpu-memory-deployment.yaml", k8sResource)),
+            value = MEMValue
+        )
+
+        k8sResource.spec.template.spec.containers.filter { it.name == defCPU.container }
+            .forEach {
+                println(it)
+                assertTrue(it.resources.requests["cpu"].toString() == "$CPUValue")
+                assertTrue(it.resources.requests["memory"].toString() == "$MEMValue")
+            }
+    }
+
+    @Test
+    fun case1() {
+        // Case 1: In the given YAML declaration memory and cpu are defined
+        applyTest("cpu-memory-deployment.yaml")
+    }
+    @Test
+    fun case2() {
+        // Case 2:  In the given YAML declaration only cpu is defined
+        applyTest("cpu-deployment.yaml")
+    }
+    @Test
+    fun case3() {
+        //  Case 3:  In the given YAML declaration only memory is defined
+        applyTest("memory-deployment.yaml")
+    }
+    @Test
+    fun case4() {
+        // Case 4: In the given YAML declaration neither `Resource Request` nor `Request Limit` is defined
+        applyTest("no-resources-deployment.yaml")
+    }
+}
\ No newline at end of file
-- 
GitLab