From c11eba9f8beb3740b8048e1874c4af3e49f507d2 Mon Sep 17 00:00:00 2001 From: David Kornel Date: Wed, 31 Jul 2024 11:53:44 +0200 Subject: [PATCH] Add StrimziClusterOperator manifests installer Signed-off-by: David Kornel --- .github/workflows/verify.yaml | 5 +- pom.xml | 11 +- src/main/java/io/streams/Environment.java | 2 +- .../java/io/streams/constants/TestTags.java | 1 + .../manifests/StrimziManifestInstaller.java | 124 ++++++++++++++++++ .../java/io/streams/e2e/dummy/DummyST.java | 26 +++- 6 files changed, 164 insertions(+), 5 deletions(-) create mode 100644 src/main/java/io/streams/operators/manifests/StrimziManifestInstaller.java diff --git a/.github/workflows/verify.yaml b/.github/workflows/verify.yaml index e77e07c..e36e153 100644 --- a/.github/workflows/verify.yaml +++ b/.github/workflows/verify.yaml @@ -26,5 +26,8 @@ jobs: - name: Create k8s Kind Cluster uses: helm/kind-action@v1 + - name: Get yaml install files + run: ./mvnw install -P get-operator-files + - name: Verify - run: ./mvnw verify -P test -Dgroups=smoke + run: ./mvnw verify -P test -Dgroups=dummy diff --git a/pom.xml b/pom.xml index f6aeb62..7d5ea3e 100644 --- a/pom.xml +++ b/pom.xml @@ -100,7 +100,16 @@ io.fabric8 kubernetes-client-api ${fabric8.version} - test + + + io.fabric8 + kubernetes-model-apps + ${fabric8.version} + + + io.fabric8 + kubernetes-model-rbac + ${fabric8.version} org.junit.jupiter diff --git a/src/main/java/io/streams/Environment.java b/src/main/java/io/streams/Environment.java index ff66d81..6bd2747 100644 --- a/src/main/java/io/streams/Environment.java +++ b/src/main/java/io/streams/Environment.java @@ -18,7 +18,7 @@ public class Environment { private static final TestEnvironmentVariables ENVIRONMENT_VARIABLES = new TestEnvironmentVariables(); private static final DateTimeFormatter DATE_FORMAT = DateTimeFormatter.ofPattern("yyyy-MM-dd_HH-mm"); - private static final String USER_PATH = System.getProperty("user.dir"); + public static final String USER_PATH = System.getProperty("user.dir"); public static final Path LOG_DIR = ENVIRONMENT_VARIABLES.getOrDefault("LOG_DIR", diff --git a/src/main/java/io/streams/constants/TestTags.java b/src/main/java/io/streams/constants/TestTags.java index 0084298..87140bb 100644 --- a/src/main/java/io/streams/constants/TestTags.java +++ b/src/main/java/io/streams/constants/TestTags.java @@ -6,4 +6,5 @@ public interface TestTags { String SMOKE = "smoke"; + String DUMMY = "dummy"; } diff --git a/src/main/java/io/streams/operators/manifests/StrimziManifestInstaller.java b/src/main/java/io/streams/operators/manifests/StrimziManifestInstaller.java new file mode 100644 index 0000000..188e091 --- /dev/null +++ b/src/main/java/io/streams/operators/manifests/StrimziManifestInstaller.java @@ -0,0 +1,124 @@ +/* + * Copyright Skodjob authors. + * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). + */ +package io.streams.operators.manifests; + +import io.fabric8.kubernetes.api.model.Container; +import io.fabric8.kubernetes.api.model.EnvVar; +import io.fabric8.kubernetes.api.model.EnvVarBuilder; +import io.fabric8.kubernetes.api.model.HasMetadata; +import io.fabric8.kubernetes.api.model.Namespace; +import io.fabric8.kubernetes.api.model.NamespaceBuilder; +import io.fabric8.kubernetes.api.model.Namespaced; +import io.fabric8.kubernetes.api.model.apps.Deployment; +import io.fabric8.kubernetes.api.model.rbac.ClusterRoleBinding; +import io.fabric8.kubernetes.api.model.rbac.ClusterRoleBindingBuilder; +import io.fabric8.kubernetes.api.model.rbac.RoleBinding; +import io.skodjob.testframe.TestFrameConstants; +import io.skodjob.testframe.resources.KubeResourceManager; +import io.skodjob.testframe.wait.Wait; +import io.streams.Environment; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.Collections; +import java.util.LinkedList; +import java.util.List; +import java.util.UUID; +import java.util.concurrent.CompletableFuture; + +/** + * Installer of strimzi operator using yaml manifests files + */ +public class StrimziManifestInstaller { + + private static final Logger LOGGER = LoggerFactory.getLogger(StrimziManifestInstaller.class); + private static Path filesDir = Paths.get(Environment.USER_PATH, "operator-install-files", + "strimzi-kafka-operator", "install", "cluster-operator"); + + /** + * Deployment name for strimzi operator + */ + public static final String DEPLOYMENT_NAME = "strimzi-cluster-operator"; + + /** + * Operator namespace + */ + public static final String OPERATOR_NS = DEPLOYMENT_NAME; + + /** + * Installs operator from yaml manifests files + * + * @return async waiter for deployment complete + * @throws IOException io exception + */ + public static CompletableFuture install() throws IOException { + LOGGER.info("Installing Strimzi into namespace: {}", OPERATOR_NS); + + Namespace namespace = new NamespaceBuilder().withNewMetadata().withName(OPERATOR_NS).endMetadata().build(); + KubeResourceManager.getInstance().createOrUpdateResourceWithWait(namespace); + + // modify namespaces, convert rolebinding to clusterrolebindings, update deployment if needed + String crbID = UUID.randomUUID().toString().substring(0, 5); + + List strimziResoruces = new LinkedList<>(); + Files.list(filesDir).sorted().forEach(file -> { + try { + strimziResoruces.addAll(KubeResourceManager.getInstance().readResourcesFromFile(file)); + } catch (IOException e) { + throw new RuntimeException(e); + } + }); + + strimziResoruces.forEach(res -> { + if (res instanceof Namespaced) { + res.getMetadata().setNamespace(OPERATOR_NS); + } + if (res instanceof ClusterRoleBinding) { + ClusterRoleBinding crb = (ClusterRoleBinding) res; + crb.getSubjects().forEach(sbj -> sbj.setNamespace(OPERATOR_NS)); + crb.getMetadata().setName(crb.getMetadata().getName() + "." + OPERATOR_NS); + } else if (res instanceof RoleBinding) { + RoleBinding rb = (RoleBinding) res; + rb.getSubjects().forEach(sbj -> sbj.setNamespace(OPERATOR_NS)); + + ClusterRoleBinding crb = new ClusterRoleBindingBuilder() + .withNewMetadata() + .withName(rb.getMetadata().getName() + "-all-ns-" + crbID) + .withAnnotations(rb.getMetadata().getAnnotations()) + .withLabels(rb.getMetadata().getLabels()) + .endMetadata() + .withRoleRef(rb.getRoleRef()) + .withSubjects(rb.getSubjects()) + .build(); + + KubeResourceManager.getInstance().createOrUpdateResourceWithoutWait(crb); + } else if (res instanceof Deployment && "strimzi-cluster-operator".equals(res.getMetadata().getName())) { + modifyDeployment((Deployment) res); + } + KubeResourceManager.getInstance().createOrUpdateResourceWithoutWait(res); + }); + LOGGER.info("Done installing Strimzi : {}", OPERATOR_NS); + return Wait.untilAsync("Strimzi operator ready", 1_000, + TestFrameConstants.GLOBAL_TIMEOUT, StrimziManifestInstaller::isReady); + } + + private static void modifyDeployment(Deployment deployment) { + Container container = deployment.getSpec().getTemplate().getSpec().getContainers().get(0); + List env = new ArrayList<>(container.getEnv() == null ? Collections.emptyList() : container.getEnv()); + env.removeIf(envVar -> envVar.getName().equals("STRIMZI_NAMESPACE")); + env.add(new EnvVarBuilder().withName("STRIMZI_NAMESPACE").withValue("*").build()); + container.setEnv(env); + } + + private static boolean isReady() { + return KubeResourceManager.getKubeClient().getClient().apps() + .deployments().inNamespace(OPERATOR_NS).withName(DEPLOYMENT_NAME).isReady(); + } +} diff --git a/src/test/java/io/streams/e2e/dummy/DummyST.java b/src/test/java/io/streams/e2e/dummy/DummyST.java index b02dcab..5822573 100644 --- a/src/test/java/io/streams/e2e/dummy/DummyST.java +++ b/src/test/java/io/streams/e2e/dummy/DummyST.java @@ -4,17 +4,26 @@ import io.skodjob.testframe.resources.KubeResourceManager; import io.streams.constants.TestTags; import io.streams.e2e.Abstract; +import io.streams.operators.manifests.StrimziManifestInstaller; import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.condition.EnabledIf; + +import java.io.IOException; +import java.util.concurrent.CompletableFuture; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; -@Tag(TestTags.SMOKE) +/** + * Test class aimed for test that test suite support methods works as expected + */ +@Tag(TestTags.DUMMY) +@EnabledIf(value = "enabled") public class DummyST extends Abstract { @Test - void dummyTest() { + void kubeResourceManagerImplTest() { KubeResourceManager.getInstance().createOrUpdateResourceWithWait( new NamespaceBuilder().withNewMetadata().withName("test").endMetadata().build() ); @@ -22,4 +31,17 @@ void dummyTest() { assertEquals("true", KubeResourceManager.getKubeClient().getClient().namespaces() .withName("test").get().getMetadata().getLabels().get("streams-e2e")); } + + @Test + void installStrimziFromManifestsTest() throws IOException { + CompletableFuture.allOf( + StrimziManifestInstaller.install()).join(); + assertTrue(KubeResourceManager.getKubeClient().getClient().apps() + .deployments().inNamespace(StrimziManifestInstaller.OPERATOR_NS) + .withName(StrimziManifestInstaller.DEPLOYMENT_NAME).isReady()); + } + + static boolean enabled() { + return System.getProperty("groups") != null && System.getProperty("groups").toLowerCase().contains("dummy"); + } }