From 005012bbff874133bff4ee78b92895e54b76b89c Mon Sep 17 00:00:00 2001 From: David Kornel Date: Tue, 5 Dec 2023 13:16:22 +0100 Subject: [PATCH] Add method for read resoruces from yaml files + fixes --- src/main/java/io/odh/test/Environment.java | 13 ++++ src/main/java/io/odh/test/TestConstants.java | 3 + src/main/java/io/odh/test/TestUtils.java | 75 ++++++++++++++++++- .../framework/manager/ResourceManager.java | 8 +- .../io/odh/test/install/BundleInstall.java | 43 +++++++++++ .../java/io/odh/test/install/OlmInstall.java | 2 +- .../java/io/odh/test/platform/KubeClient.java | 29 +++++-- .../java/io/odh/test/platform/KubeUtils.java | 55 -------------- src/test/java/io/odh/test/e2e/Abstract.java | 3 - .../e2e/continuous/DataScienceClusterIT.java | 5 +- .../e2e/continuous/DataScienceProjectIT.java | 7 +- .../e2e/standard/DataScienceClusterIT.java | 9 ++- .../io/odh/test/e2e/standard/OdhInstall.java | 12 ++- 13 files changed, 182 insertions(+), 82 deletions(-) diff --git a/src/main/java/io/odh/test/Environment.java b/src/main/java/io/odh/test/Environment.java index 6c74fb35..4ff5d115 100644 --- a/src/main/java/io/odh/test/Environment.java +++ b/src/main/java/io/odh/test/Environment.java @@ -20,10 +20,19 @@ public class Environment { private static final Logger LOGGER = LoggerFactory.getLogger(Environment.class); private static final Map VALUES = new HashMap<>(); + public static final String USER_PATH = System.getProperty("user.dir"); + private static final String USERNAME_ENV = "KUBE_USERNAME"; private static final String PASSWORD_ENV = "KUBE_PASSWORD"; private static final String TOKEN_ENV = "KUBE_TOKEN"; private static final String URL_ENV = "KUBE_URL"; + + /** + * Install bundle files + */ + private static final String INSTALL_FILE_ENV = "INSTALL_FILE"; + private static final String INSTALL_FILE_RELEASED_ENV = "INSTALL_FILE_PREVIOUS"; + /** * OLM env variables */ @@ -56,6 +65,10 @@ public class Environment { public static final String KUBE_TOKEN = getOrDefault(TOKEN_ENV, null); public static final String KUBE_URL = getOrDefault(URL_ENV, null); + // Bundle + public static final String INSTALL_FILE_PATH = getOrDefault(INSTALL_FILE_ENV, TestConstants.LATEST_BUNDLE_DEPLOY_FILE); + public static final String INSTALL_FILE_PREVIOUS_PATH = getOrDefault(INSTALL_FILE_RELEASED_ENV, TestConstants.RELEASED_BUNDLE_DEPLOY_FILE); + // OLM env variables public static final String OLM_OPERATOR_NAME = getOrDefault(OLM_OPERATOR_NAME_ENV, OLM_OPERATOR_NAME_DEFAULT); public static final String OLM_OPERATOR_NAMESPACE = getOrDefault(OLM_OPERATOR_NAMESPACE_ENV, OLM_OPERATOR_NAMESPACE_DEFAULT); diff --git a/src/main/java/io/odh/test/TestConstants.java b/src/main/java/io/odh/test/TestConstants.java index e51b12ae..91c1d563 100644 --- a/src/main/java/io/odh/test/TestConstants.java +++ b/src/main/java/io/odh/test/TestConstants.java @@ -13,6 +13,9 @@ public class TestConstants { public static final String SUBSCRIPTION = "Subscription"; public static final String OPERATOR_GROUP = "OperatorGroup"; + public static final String LATEST_BUNDLE_DEPLOY_FILE = "install-files/latest.yaml"; + public static final String RELEASED_BUNDLE_DEPLOY_FILE = "install-files/released.yaml"; + public static final long GLOBAL_POLL_INTERVAL = Duration.ofSeconds(10).toMillis(); public static final long GLOBAL_POLL_INTERVAL_MEDIUM = Duration.ofSeconds(5).toMillis(); public static final long GLOBAL_POLL_INTERVAL_SHORT = Duration.ofSeconds(1).toMillis(); diff --git a/src/main/java/io/odh/test/TestUtils.java b/src/main/java/io/odh/test/TestUtils.java index 448e0889..28127461 100644 --- a/src/main/java/io/odh/test/TestUtils.java +++ b/src/main/java/io/odh/test/TestUtils.java @@ -7,10 +7,18 @@ import io.odh.test.framework.WaitException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; + +import java.io.InputStream; import java.io.PrintWriter; import java.io.StringWriter; import java.time.Duration; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Executor; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.ThreadFactory; import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; import java.util.function.BooleanSupplier; @SuppressWarnings({"checkstyle:ClassFanOutComplexity"}) @@ -18,8 +26,6 @@ public final class TestUtils { private static final Logger LOGGER = LoggerFactory.getLogger(TestUtils.class); - public static final String USER_PATH = System.getProperty("user.dir"); - /** * Default timeout for asynchronous tests. */ @@ -37,9 +43,10 @@ private TestUtils() { /** * Poll the given {@code ready} function every {@code pollIntervalMs} milliseconds until it returns true, * or throw a WaitException if it doesn't return true within {@code timeoutMs} milliseconds. + * * @return The remaining time left until timeout occurs * (helpful if you have several calls which need to share a common timeout), - * */ + */ public static long waitFor(String description, long pollIntervalMs, long timeoutMs, BooleanSupplier ready) { return waitFor(description, pollIntervalMs, timeoutMs, ready, () -> { }); } @@ -105,4 +112,66 @@ public static long waitFor(String description, long pollIntervalMs, long timeout } } } + + private static final ExecutorService EXECUTOR = Executors.newCachedThreadPool(new ThreadFactory() { + final ThreadFactory defaultThreadFactory = Executors.defaultThreadFactory(); + + @Override + public Thread newThread(Runnable r) { + Thread result = defaultThreadFactory.newThread(r); + result.setDaemon(true); + return result; + } + }); + + public static CompletableFuture asyncWaitFor(String description, long pollIntervalMs, long timeoutMs, BooleanSupplier ready) { + LOGGER.info("Waiting for {}", description); + long deadline = System.currentTimeMillis() + timeoutMs; + CompletableFuture future = new CompletableFuture<>(); + Executor delayed = CompletableFuture.delayedExecutor(pollIntervalMs, TimeUnit.MILLISECONDS, EXECUTOR); + Runnable r = new Runnable() { + @Override + public void run() { + boolean result; + try { + result = ready.getAsBoolean(); + } catch (Exception e) { + future.completeExceptionally(e); + return; + } + long timeLeft = deadline - System.currentTimeMillis(); + if (!future.isDone()) { + if (!result) { + if (timeLeft >= 0) { + if (LOGGER.isTraceEnabled()) { + LOGGER.trace("{} not ready, will try again ({}ms till timeout)", description, timeLeft); + } + delayed.execute(this); + } else { + future.completeExceptionally(new TimeoutException(String.format("Waiting for %s timeout %s exceeded", description, timeoutMs))); + } + } else { + future.complete(null); + } + } + } + }; + r.run(); + return future; + } + + public static InputStream getFileFromResourceAsStream(String fileName) { + + // The class loader that loaded the class + ClassLoader classLoader = TestUtils.class.getClassLoader(); + InputStream inputStream = classLoader.getResourceAsStream(fileName); + + // the stream holding the file content + if (inputStream == null) { + throw new IllegalArgumentException("file not found! " + fileName); + } else { + return inputStream; + } + + } } diff --git a/src/main/java/io/odh/test/framework/manager/ResourceManager.java b/src/main/java/io/odh/test/framework/manager/ResourceManager.java index 0028c57e..5019276c 100644 --- a/src/main/java/io/odh/test/framework/manager/ResourceManager.java +++ b/src/main/java/io/odh/test/framework/manager/ResourceManager.java @@ -76,7 +76,7 @@ private void createResource(boolean waitReady, T... reso if (type == null) { if (resource instanceof Deployment) { Deployment deployment = (Deployment) resource; - ResourceManager.getClient().getClient().apps().deployments().resource(deployment).create(); + client.getClient().apps().deployments().resource(deployment).create(); if (waitReady) { DeploymentUtils.waitForDeploymentReady(resource.getMetadata().getNamespace(), resource.getMetadata().getName()); } @@ -111,7 +111,7 @@ public final void deleteResource(T... resources) { if (type == null) { if (resource instanceof Deployment) { Deployment deployment = (Deployment) resource; - ResourceManager.getClient().getClient().apps().deployments().resource(deployment).delete(); + client.getClient().apps().deployments().resource(deployment).delete(); DeploymentUtils.waitForDeploymentDeletion(resource.getMetadata().getNamespace(), resource.getMetadata().getName()); } else { LOGGER.error("Invalid resource {} {}/{}. Please implement it in ResourceManager", @@ -130,7 +130,7 @@ public final void deleteResource(T... resources) { type.delete(resource); assertTrue(waitResourceCondition(resource, ResourceCondition.deletion()), String.format("Timed out deleting %s %s/%s", resource.getKind(), resource.getMetadata().getNamespace(), resource.getMetadata().getName())); - } catch (Exception e) { + } catch (Exception e) { if (resource.getMetadata().getNamespace() == null) { LOGGER.error("Failed to delete {} {}", resource.getKind(), resource.getMetadata().getName(), e); } else { @@ -167,7 +167,7 @@ public final boolean waitResourceCondition(T resource, R TestConstants.GLOBAL_POLL_INTERVAL, TestConstants.GLOBAL_TIMEOUT, () -> { T res = type.get(resource.getMetadata().getNamespace(), resource.getMetadata().getName()); - resourceReady[0] = condition.getPredicate().test(res); + resourceReady[0] = condition.getPredicate().test(res); if (!resourceReady[0]) { type.delete(res); } diff --git a/src/main/java/io/odh/test/install/BundleInstall.java b/src/main/java/io/odh/test/install/BundleInstall.java index bef4e54c..b2ce43e7 100644 --- a/src/main/java/io/odh/test/install/BundleInstall.java +++ b/src/main/java/io/odh/test/install/BundleInstall.java @@ -4,5 +4,48 @@ */ package io.odh.test.install; +import io.fabric8.kubernetes.api.model.HasMetadata; +import io.odh.test.Environment; +import io.odh.test.TestConstants; +import io.odh.test.TestUtils; +import io.odh.test.framework.manager.ResourceManager; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.List; + public class BundleInstall { + + private static final Logger LOGGER = LoggerFactory.getLogger(BundleInstall.class); + + List resources; + + public BundleInstall(String installFilePath) throws IOException { + InputStream is; + if (installFilePath.equals(TestConstants.LATEST_BUNDLE_DEPLOY_FILE) + || installFilePath.equals(TestConstants.RELEASED_BUNDLE_DEPLOY_FILE)) { + is = TestUtils.getFileFromResourceAsStream(installFilePath); + } else { + is = new FileInputStream(installFilePath); + } + resources = ResourceManager.getClient().readResourcesFromYaml(is); + } + + public BundleInstall() throws IOException { + //use default latest + this(Environment.INSTALL_FILE_PATH); + } + + public void printResources() { + resources.forEach(r -> { + LOGGER.info("Kind: {}, Name: {}", r.getKind(), r.getMetadata().getName()); + }); + } + + public void installBundle() { + //TODO implement using RM + } } diff --git a/src/main/java/io/odh/test/install/OlmInstall.java b/src/main/java/io/odh/test/install/OlmInstall.java index 07f823dc..45f45568 100644 --- a/src/main/java/io/odh/test/install/OlmInstall.java +++ b/src/main/java/io/odh/test/install/OlmInstall.java @@ -19,7 +19,7 @@ import java.util.Collections; public class OlmInstall { - private static final Logger LOGGER = LoggerFactory.getLogger(ResourceManager.class); + private static final Logger LOGGER = LoggerFactory.getLogger(OlmInstall.class); private String namespace = Environment.OLM_OPERATOR_NAMESPACE; private String channel = Environment.OLM_OPERATOR_CHANNEL; diff --git a/src/main/java/io/odh/test/platform/KubeClient.java b/src/main/java/io/odh/test/platform/KubeClient.java index 9f04702f..0e12898c 100644 --- a/src/main/java/io/odh/test/platform/KubeClient.java +++ b/src/main/java/io/odh/test/platform/KubeClient.java @@ -87,7 +87,7 @@ public KubeClient inNamespace(String namespace) { } private Config getConfig() { - if (Environment.KUBE_PASSWORD != null + if (Environment.KUBE_USERNAME != null && Environment.KUBE_PASSWORD != null && Environment.KUBE_URL != null) { return new ConfigBuilder() @@ -117,13 +117,13 @@ public Namespace getNamespace(String namespace) { public boolean namespaceExists(String namespace) { return client.namespaces().list().getItems().stream().map(n -> n.getMetadata().getName()) - .collect(Collectors.toList()).contains(namespace); + .toList().contains(namespace); } - // ============================================= - // ---------> Create multi-resoruces <--------- - // ============================================= - public void apply(String namespace, InputStream is, Function modifier) throws IOException { + // ================================================== + // ---------> Create/read multi-resoruces <--------- + // ================================================== + public void create(String namespace, InputStream is, Function modifier) throws IOException { try (is) { client.load(is).get().forEach(i -> { HasMetadata h = modifier.apply(i); @@ -134,6 +134,23 @@ public void apply(String namespace, InputStream is, Function modifier) throws IOException { + try (is) { + client.load(is).get().forEach(i -> { + HasMetadata h = modifier.apply(i); + if (h != null) { + client.resource(h).create(); + } + }); + } + } + + public List readResourcesFromYaml(InputStream is) throws IOException { + try (is) { + return client.load(is).items(); + } + } + /** * Gets namespace status */ diff --git a/src/main/java/io/odh/test/platform/KubeUtils.java b/src/main/java/io/odh/test/platform/KubeUtils.java index 165b161c..73f470e6 100644 --- a/src/main/java/io/odh/test/platform/KubeUtils.java +++ b/src/main/java/io/odh/test/platform/KubeUtils.java @@ -8,14 +8,6 @@ import org.slf4j.LoggerFactory; import java.util.List; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.Executor; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.ThreadFactory; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.TimeoutException; -import java.util.function.BooleanSupplier; public class KubeUtils { @@ -29,53 +21,6 @@ public static org.kubeflow.v1.notebookstatus.Conditions getNotebookConditionByTy return conditions.stream().filter(c -> c.getType().equals(type)).findFirst().orElseGet(null); } - private static final ExecutorService EXECUTOR = Executors.newCachedThreadPool(new ThreadFactory() { - final ThreadFactory defaultThreadFactory = Executors.defaultThreadFactory(); - - @Override - public Thread newThread(Runnable r) { - Thread result = defaultThreadFactory.newThread(r); - result.setDaemon(true); - return result; - } - }); - - public static CompletableFuture asyncWaitFor(String description, long pollIntervalMs, long timeoutMs, BooleanSupplier ready) { - LOGGER.info("Waiting for {}", description); - long deadline = System.currentTimeMillis() + timeoutMs; - CompletableFuture future = new CompletableFuture<>(); - Executor delayed = CompletableFuture.delayedExecutor(pollIntervalMs, TimeUnit.MILLISECONDS, EXECUTOR); - Runnable r = new Runnable() { - @Override - public void run() { - boolean result; - try { - result = ready.getAsBoolean(); - } catch (Exception e) { - future.completeExceptionally(e); - return; - } - long timeLeft = deadline - System.currentTimeMillis(); - if (!future.isDone()) { - if (!result) { - if (timeLeft >= 0) { - if (LOGGER.isTraceEnabled()) { - LOGGER.trace("{} not ready, will try again ({}ms till timeout)", description, timeLeft); - } - delayed.execute(this); - } else { - future.completeExceptionally(new TimeoutException(String.format("Waiting for %s timeout %s exceeded", description, timeoutMs))); - } - } else { - future.complete(null); - } - } - } - }; - r.run(); - return future; - } - private KubeUtils() { } } diff --git a/src/test/java/io/odh/test/e2e/Abstract.java b/src/test/java/io/odh/test/e2e/Abstract.java index c8155993..7ff8a288 100644 --- a/src/test/java/io/odh/test/e2e/Abstract.java +++ b/src/test/java/io/odh/test/e2e/Abstract.java @@ -6,8 +6,6 @@ import io.odh.test.framework.manager.ResourceManager; import io.odh.test.framework.TestExceptionCallbackListener; -import io.odh.test.platform.KubeClient; -import io.odh.test.TestConstants; import io.odh.test.framework.TestSeparator; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.DisplayNameGeneration; @@ -19,7 +17,6 @@ @ExtendWith(TestExceptionCallbackListener.class) @TestInstance(TestInstance.Lifecycle.PER_CLASS) public class Abstract implements TestSeparator { - protected KubeClient kubeClient = new KubeClient(TestConstants.ODH_NAMESPACE); static { ResourceManager.getInstance(); diff --git a/src/test/java/io/odh/test/e2e/continuous/DataScienceClusterIT.java b/src/test/java/io/odh/test/e2e/continuous/DataScienceClusterIT.java index e5480e4e..2ba9faf0 100644 --- a/src/test/java/io/odh/test/e2e/continuous/DataScienceClusterIT.java +++ b/src/test/java/io/odh/test/e2e/continuous/DataScienceClusterIT.java @@ -9,6 +9,7 @@ import io.fabric8.kubernetes.client.dsl.Resource; import io.odh.test.TestConstants; import io.odh.test.e2e.Abstract; +import io.odh.test.framework.manager.ResourceManager; import io.odh.test.platform.KubeUtils; import io.opendatahub.datasciencecluster.v1.DataScienceCluster; import io.opendatahub.datasciencecluster.v1.datascienceclusterspec.components.Codeflare; @@ -39,8 +40,8 @@ public class DataScienceClusterIT extends Abstract { @BeforeAll void init() { - dataScienceProjectCli = kubeClient.dataScienceClusterClient(); - dashboardConfigCli = kubeClient.dashboardConfigClient(); + dataScienceProjectCli = ResourceManager.getClient().dataScienceClusterClient(); + dashboardConfigCli = ResourceManager.getClient().dashboardConfigClient(); } @Test diff --git a/src/test/java/io/odh/test/e2e/continuous/DataScienceProjectIT.java b/src/test/java/io/odh/test/e2e/continuous/DataScienceProjectIT.java index c052d3ee..69b4339a 100644 --- a/src/test/java/io/odh/test/e2e/continuous/DataScienceProjectIT.java +++ b/src/test/java/io/odh/test/e2e/continuous/DataScienceProjectIT.java @@ -9,6 +9,7 @@ import io.fabric8.kubernetes.client.dsl.Resource; import io.odh.test.e2e.Abstract; import io.odh.test.framework.TestSeparator; +import io.odh.test.framework.manager.ResourceManager; import io.odh.test.platform.KubeUtils; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Tag; @@ -40,16 +41,16 @@ private static Stream getDsProjects() { @BeforeAll void init() { - notebookCli = kubeClient.notebookClient(); + notebookCli = ResourceManager.getClient().notebookClient(); } @ParameterizedTest(name = "checkDataScienceProjects-{0}") @MethodSource("getDsProjects") void checkDataScienceProjects(String dsProjectName) { - assertTrue(kubeClient.namespaceExists(dsProjectName)); + assertTrue(ResourceManager.getClient().namespaceExists(dsProjectName)); assertEquals("true", - kubeClient.getNamespace(dsProjectName).getMetadata().getLabels().getOrDefault("opendatahub.io/dashboard", "false")); + ResourceManager.getClient().getNamespace(dsProjectName).getMetadata().getLabels().getOrDefault("opendatahub.io/dashboard", "false")); notebookCli.inNamespace(dsProjectName).list().getItems().forEach(notebook -> { LOGGER.info("Found notebook {} in datascience project {}", notebook.getMetadata().getName(), dsProjectName); diff --git a/src/test/java/io/odh/test/e2e/standard/DataScienceClusterIT.java b/src/test/java/io/odh/test/e2e/standard/DataScienceClusterIT.java index 03392d87..46848745 100644 --- a/src/test/java/io/odh/test/e2e/standard/DataScienceClusterIT.java +++ b/src/test/java/io/odh/test/e2e/standard/DataScienceClusterIT.java @@ -9,6 +9,7 @@ import io.fabric8.kubernetes.client.dsl.MixedOperation; import io.fabric8.kubernetes.client.dsl.Resource; import io.odh.test.e2e.Abstract; +import io.odh.test.framework.manager.ResourceManager; import io.opendatahub.datasciencecluster.v1.DataScienceCluster; import io.opendatahub.datasciencecluster.v1.DataScienceClusterBuilder; import io.opendatahub.datasciencecluster.v1.datascienceclusterspec.ComponentsBuilder; @@ -38,19 +39,19 @@ public class DataScienceClusterIT extends Abstract { @BeforeAll void init() { - cli = kubeClient.dataScienceClusterClient(); + cli = ResourceManager.getClient().dataScienceClusterClient(); } @AfterAll void clean() { cli.inNamespace(DS_PROJECT_NAMESPACE).withName(DS_PROJECT_NAME).delete(); - kubeClient.getClient().namespaces().withName(DS_PROJECT_NAMESPACE).delete(); + ResourceManager.getClient().getClient().namespaces().withName(DS_PROJECT_NAMESPACE).delete(); } @Test void createDataScienceCluster() { - if (!kubeClient.namespaceExists(DS_PROJECT_NAMESPACE)) { - kubeClient.getClient() + if (!ResourceManager.getClient().namespaceExists(DS_PROJECT_NAMESPACE)) { + ResourceManager.getClient().getClient() .namespaces() .resource(new NamespaceBuilder().withNewMetadata().withName(DS_PROJECT_NAMESPACE).endMetadata().build()) .create(); diff --git a/src/test/java/io/odh/test/e2e/standard/OdhInstall.java b/src/test/java/io/odh/test/e2e/standard/OdhInstall.java index 0485ff8d..9113ea4c 100644 --- a/src/test/java/io/odh/test/e2e/standard/OdhInstall.java +++ b/src/test/java/io/odh/test/e2e/standard/OdhInstall.java @@ -7,19 +7,29 @@ import io.fabric8.kubernetes.api.model.apps.Deployment; import io.odh.test.e2e.Abstract; import io.odh.test.framework.manager.ResourceManager; +import io.odh.test.install.BundleInstall; import io.odh.test.install.OlmInstall; import org.junit.jupiter.api.Test; +import java.io.IOException; + import static org.junit.jupiter.api.Assertions.assertNotNull; public class OdhInstall extends Abstract { @Test - void testInstallOdh() { + void testInstallOdhOlm() { OlmInstall olmInstall = new OlmInstall(); olmInstall.create(); Deployment dep = ResourceManager.getClient().getDeployment(olmInstall.getNamespace(), olmInstall.getDeploymentName()); assertNotNull(dep); } + + @Test + void testInstallOdhBundle() throws IOException { + BundleInstall bundle = new BundleInstall(); + bundle.printResources(); + //TODO complete + } }