From f3e6bf4411a766aa1f1a72d39d46f7f66b511a3a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?S=C3=B6ren=20Henning?= <soeren.henning@email.uni-kiel.de>
Date: Wed, 7 Dec 2022 15:44:18 +0100
Subject: [PATCH] Add ConfigMapPropertiesPatcher

---
 .../patcher/ConfigMapPropertiesPatcher.kt     | 37 ++++++++++++++
 .../kubernetes/patcher/PatcherFactory.kt      |  9 ++++
 .../patcher/ConfigMapPropertiesPatcherTest.kt | 50 +++++++++++++++++++
 .../patcher/ConfigMapYamlPatcherTest.kt       |  1 +
 4 files changed, 97 insertions(+)
 create mode 100644 theodolite/src/main/kotlin/rocks/theodolite/kubernetes/patcher/ConfigMapPropertiesPatcher.kt
 create mode 100644 theodolite/src/test/kotlin/rocks/theodolite/kubernetes/patcher/ConfigMapPropertiesPatcherTest.kt

diff --git a/theodolite/src/main/kotlin/rocks/theodolite/kubernetes/patcher/ConfigMapPropertiesPatcher.kt b/theodolite/src/main/kotlin/rocks/theodolite/kubernetes/patcher/ConfigMapPropertiesPatcher.kt
new file mode 100644
index 000000000..b1c168987
--- /dev/null
+++ b/theodolite/src/main/kotlin/rocks/theodolite/kubernetes/patcher/ConfigMapPropertiesPatcher.kt
@@ -0,0 +1,37 @@
+package rocks.theodolite.kubernetes.patcher
+
+import io.fabric8.kubernetes.api.model.ConfigMap
+import io.fabric8.kubernetes.api.model.HasMetadata
+import java.io.StringReader
+import java.io.StringWriter
+import java.util.Properties
+
+/**
+ * The ConfigMapYamlPatcher allows to add/modify a key-value pair in a .properties file of a ConfigMap
+ *
+ * @property fileName of the .properties file in the ConfigMap that should be modified.
+ * @property variableName Name of the environment variable to be patched.
+ */
+class ConfigMapPropertiesPatcher(
+    private val fileName: String,
+    private val variableName: String,
+) : AbstractStringPatcher() {
+
+    override fun patchSingleResource(resource: HasMetadata, value: String): HasMetadata {
+        if (resource is ConfigMap) {
+            val propertiesFile = resource.data[fileName]
+
+            // Read properties string
+            val properties = Properties().also { it.load(StringReader(propertiesFile)) }
+
+            // Change value
+            properties.setProperty(this.variableName, value)
+
+            // Convert back to String and set in Kubernetes resource
+            val writer = StringWriter()
+            properties.store(writer, null)
+            resource.data[fileName] = writer.toString()
+        }
+        return resource
+    }
+}
diff --git a/theodolite/src/main/kotlin/rocks/theodolite/kubernetes/patcher/PatcherFactory.kt b/theodolite/src/main/kotlin/rocks/theodolite/kubernetes/patcher/PatcherFactory.kt
index 4b75999e7..f469eaee3 100644
--- a/theodolite/src/main/kotlin/rocks/theodolite/kubernetes/patcher/PatcherFactory.kt
+++ b/theodolite/src/main/kotlin/rocks/theodolite/kubernetes/patcher/PatcherFactory.kt
@@ -78,6 +78,15 @@ class PatcherFactory {
                         suffix = patcher.properties["suffix"],
                         factor = patcher.properties["factor"]?.toInt(),
                     )
+                    "ConfigMapPropertiesPatcher" -> DecoratingPatcher(
+                        ConfigMapPropertiesPatcher(
+                            fileName = patcher.properties["fileName"] ?: throwInvalid(patcher),
+                            variableName = patcher.properties["variableName"] ?: throwInvalid(patcher)
+                        ),
+                        prefix = patcher.properties["prefix"],
+                        suffix = patcher.properties["suffix"],
+                        factor = patcher.properties["factor"]?.toInt(),
+                    )
                     "NamePatcher" -> NamePatcher()
                     "ServiceSelectorPatcher" -> ServiceSelectorPatcher(
                         variableName = patcher.properties["label"] ?: throwInvalid(patcher)
diff --git a/theodolite/src/test/kotlin/rocks/theodolite/kubernetes/patcher/ConfigMapPropertiesPatcherTest.kt b/theodolite/src/test/kotlin/rocks/theodolite/kubernetes/patcher/ConfigMapPropertiesPatcherTest.kt
new file mode 100644
index 000000000..493343baf
--- /dev/null
+++ b/theodolite/src/test/kotlin/rocks/theodolite/kubernetes/patcher/ConfigMapPropertiesPatcherTest.kt
@@ -0,0 +1,50 @@
+package rocks.theodolite.kubernetes.patcher
+
+import io.fabric8.kubernetes.api.model.ConfigMap
+import io.fabric8.kubernetes.api.model.ConfigMapBuilder
+import org.junit.jupiter.api.Assertions.*
+import org.junit.jupiter.api.BeforeEach
+import org.junit.jupiter.params.ParameterizedTest
+import org.junit.jupiter.params.provider.ValueSource
+
+internal class ConfigMapPropertiesPatcherTest {
+
+    private lateinit var configMap: ConfigMap
+
+    private val patcher = ConfigMapPropertiesPatcher("some-file.properties", "second.prop.value")
+
+    @BeforeEach
+    fun setUp() {
+        val data = mapOf(
+            "some-file.properties" to """
+                first.properties.value = some-test
+                second.prop.value = 1
+                third = 1234
+            """.trimIndent()
+        )
+
+        this.configMap = ConfigMapBuilder()
+            .withNewMetadata()
+            .withName("example")
+            .endMetadata()
+            .addToData(data)
+            .build()
+    }
+
+    @ParameterizedTest
+    @ValueSource(strings = ["some-string", "42", "42.42", "true"])
+    fun setSettingString(inputValue: String) {
+        val patched = patcher.patchSingleResource(this.configMap, inputValue)
+        assertTrue(patched is ConfigMap)
+        //patched.let { it as ConfigMap }.data
+        patched as ConfigMap
+        val properties = patched.data["some-file.properties"]
+        assertTrue(properties != null)
+        val matchLines = properties!!.lines().filter { it.startsWith("second.prop.value") }
+        assertEquals(1, matchLines.size)
+        val value = matchLines[0].split("=").getOrNull(1)
+        assertEquals(inputValue, value)
+    }
+
+
+}
\ No newline at end of file
diff --git a/theodolite/src/test/kotlin/rocks/theodolite/kubernetes/patcher/ConfigMapYamlPatcherTest.kt b/theodolite/src/test/kotlin/rocks/theodolite/kubernetes/patcher/ConfigMapYamlPatcherTest.kt
index 474080b5c..38e807dc4 100644
--- a/theodolite/src/test/kotlin/rocks/theodolite/kubernetes/patcher/ConfigMapYamlPatcherTest.kt
+++ b/theodolite/src/test/kotlin/rocks/theodolite/kubernetes/patcher/ConfigMapYamlPatcherTest.kt
@@ -20,6 +20,7 @@ internal class ConfigMapYamlPatcherTest {
         val data = mapOf(
             "some-file.yaml" to """
                 first: some-test
+                # some comment: with colon
                 second: 1
                 third: 1234
             """.trimIndent()
-- 
GitLab