From cf70616fa563e744fbb34d397ec977f1a5acb534 Mon Sep 17 00:00:00 2001
From: "stu126940@mail.uni-kiel.de" <stu126940@mail.uni-kiel.de>
Date: Wed, 24 Nov 2021 11:49:16 +0100
Subject: [PATCH] Add basic action functionality

---
 theodolite/crd/crd-benchmark.yaml             |  3 +
 .../kotlin/theodolite/benchmark/Action.kt     | 31 ++++++++
 .../theodolite/benchmark/ActionCommand.kt     | 70 +++++++++++++++++++
 .../kotlin/theodolite/benchmark/Resources.kt  |  2 +
 .../util/ActionCommandFailedException.kt      |  4 ++
 .../theodolite/benchmark/ActionCommandTest.kt | 24 +++++++
 6 files changed, 134 insertions(+)
 create mode 100644 theodolite/src/main/kotlin/theodolite/benchmark/Action.kt
 create mode 100644 theodolite/src/main/kotlin/theodolite/benchmark/ActionCommand.kt
 create mode 100644 theodolite/src/main/kotlin/theodolite/util/ActionCommandFailedException.kt
 create mode 100644 theodolite/src/test/kotlin/theodolite/benchmark/ActionCommandTest.kt

diff --git a/theodolite/crd/crd-benchmark.yaml b/theodolite/crd/crd-benchmark.yaml
index 7ab2e5f3b..60c777c7d 100644
--- a/theodolite/crd/crd-benchmark.yaml
+++ b/theodolite/crd/crd-benchmark.yaml
@@ -64,6 +64,9 @@ spec:
                               type: array
                               items:
                                 type: string
+                  beforeActions:
+                    type: array
+                    properties:
               sut:
                 description: The appResourceSets specifies all Kubernetes resources required to start the sut. A resourceSet can be either a configMap resourceSet or a fileSystem resourceSet.
                 type: object
diff --git a/theodolite/src/main/kotlin/theodolite/benchmark/Action.kt b/theodolite/src/main/kotlin/theodolite/benchmark/Action.kt
new file mode 100644
index 000000000..55add765e
--- /dev/null
+++ b/theodolite/src/main/kotlin/theodolite/benchmark/Action.kt
@@ -0,0 +1,31 @@
+package theodolite.benchmark
+
+import io.fabric8.kubernetes.client.NamespacedKubernetesClient
+
+class Action {
+
+    lateinit var selector: ActionSelector
+    lateinit var exec: Command
+
+    fun exec(client: NamespacedKubernetesClient) {
+        ActionCommand(client = client)
+            .exec(
+                matchLabels = selector.pod.matchLabels,
+                container = selector.container,
+                command = exec.command
+            )
+    }
+}
+
+class ActionSelector {
+    lateinit var pod: PodSelector
+    lateinit var container: String
+}
+
+class PodSelector {
+    lateinit var matchLabels: MutableMap<String, String>
+}
+
+class Command {
+    lateinit var command: String
+}
\ No newline at end of file
diff --git a/theodolite/src/main/kotlin/theodolite/benchmark/ActionCommand.kt b/theodolite/src/main/kotlin/theodolite/benchmark/ActionCommand.kt
new file mode 100644
index 000000000..762487c6d
--- /dev/null
+++ b/theodolite/src/main/kotlin/theodolite/benchmark/ActionCommand.kt
@@ -0,0 +1,70 @@
+package theodolite.benchmark
+
+import io.fabric8.kubernetes.client.NamespacedKubernetesClient
+import io.fabric8.kubernetes.client.dsl.ExecListener
+import io.fabric8.kubernetes.client.dsl.ExecWatch
+import mu.KotlinLogging
+import okhttp3.Response
+import theodolite.util.ActionCommandFailedException
+import java.io.ByteArrayOutputStream
+import java.util.concurrent.CountDownLatch
+import java.util.concurrent.TimeUnit
+
+private val logger = KotlinLogging.logger {}
+
+class ActionCommand(val client: NamespacedKubernetesClient) {
+    var out: ByteArrayOutputStream = ByteArrayOutputStream()
+    var error: ByteArrayOutputStream = ByteArrayOutputStream()
+    private val execLatch = CountDownLatch(1);
+
+    fun exec(matchLabels: MutableMap<String, String>, container: String, command: String): Pair<String, String> {
+        try {
+            val execWatch: ExecWatch = client.pods()
+                .withName(getPodName(matchLabels))
+                .inContainer(container)
+                .writingOutput(out)
+                .writingError(error)
+                .usingListener(MyPodExecListener(execLatch))
+                .exec(*command.split(" ").toTypedArray())
+
+            val latchTerminationStatus = execLatch.await(5, TimeUnit.SECONDS);
+            if (!latchTerminationStatus) {
+                logger.warn("Latch could not terminate within specified time");
+            }
+            execWatch.close();
+        } catch (e: InterruptedException) {
+            Thread.currentThread().interrupt();
+            throw ActionCommandFailedException("Interrupted while waiting for the exec", e)
+        }
+        return Pair(out.toString(), error.toString())
+    }
+
+    private fun getPodName(matchLabels: MutableMap<String, String>): String {
+        return try {
+            this.client
+                .pods()
+                .withLabels(matchLabels)
+                .list()
+                .items
+                .first()
+                .metadata
+                .name
+        } catch (e: Exception) {
+            throw ActionCommandFailedException("Couldn't find any pod that matches the specified labels.", e)
+        }
+    }
+
+    private class MyPodExecListener(val execLatch: CountDownLatch) : ExecListener {
+        override fun onOpen(response: Response) {
+        }
+
+        override fun onFailure(throwable: Throwable, response: Response) {
+            logger.warn("Some error encountered while executing action")
+            execLatch.countDown()
+        }
+
+        override fun onClose(i: Int, s: String) {
+            execLatch.countDown()
+        }
+    }
+}
diff --git a/theodolite/src/main/kotlin/theodolite/benchmark/Resources.kt b/theodolite/src/main/kotlin/theodolite/benchmark/Resources.kt
index 0187735b8..738fb6014 100644
--- a/theodolite/src/main/kotlin/theodolite/benchmark/Resources.kt
+++ b/theodolite/src/main/kotlin/theodolite/benchmark/Resources.kt
@@ -9,5 +9,7 @@ import io.quarkus.runtime.annotations.RegisterForReflection
 class Resources {
 
     lateinit var resources: List<ResourceSets>
+    lateinit var beforeActions: List<Action>
+    lateinit var afterActions: List<Action>
 
 }
\ No newline at end of file
diff --git a/theodolite/src/main/kotlin/theodolite/util/ActionCommandFailedException.kt b/theodolite/src/main/kotlin/theodolite/util/ActionCommandFailedException.kt
new file mode 100644
index 000000000..c1a8fc401
--- /dev/null
+++ b/theodolite/src/main/kotlin/theodolite/util/ActionCommandFailedException.kt
@@ -0,0 +1,4 @@
+package theodolite.util
+
+class ActionCommandFailedException(message: String, e: Exception? = null) : DeploymentFailedException(message,e) {
+}
\ No newline at end of file
diff --git a/theodolite/src/test/kotlin/theodolite/benchmark/ActionCommandTest.kt b/theodolite/src/test/kotlin/theodolite/benchmark/ActionCommandTest.kt
new file mode 100644
index 000000000..0b9f89aa0
--- /dev/null
+++ b/theodolite/src/test/kotlin/theodolite/benchmark/ActionCommandTest.kt
@@ -0,0 +1,24 @@
+package theodolite.benchmark
+
+import io.fabric8.kubernetes.client.DefaultKubernetesClient
+import io.quarkus.test.junit.QuarkusTest
+import org.junit.jupiter.api.Test
+
+
+@QuarkusTest
+class ActionCommandTest {
+
+    @Test
+    fun testAction() {
+        val action = ActionCommand(DefaultKubernetesClient().inNamespace("default"))
+        val result = action.exec(mutableMapOf(
+            Pair("app.kubernetes.io/name","grafana")
+        ),
+            container = "grafana",
+            command = "ls /")
+        println("out is: " + result.first)
+        println("error is: " + result.second)
+
+
+    }
+}
\ No newline at end of file
-- 
GitLab