diff --git a/src/main/java/io/odh/test/framework/ExecutionListener.java b/src/main/java/io/odh/test/framework/ExecutionListener.java index 75e9195f..a7165944 100644 --- a/src/main/java/io/odh/test/framework/ExecutionListener.java +++ b/src/main/java/io/odh/test/framework/ExecutionListener.java @@ -13,13 +13,16 @@ public class ExecutionListener implements TestExecutionListener { static final Logger LOGGER = LoggerFactory.getLogger(TestSeparator.class); + static { + Environment.print(); + } + public void testPlanExecutionStarted(TestPlan testPlan) { LOGGER.info("======================================================================="); LOGGER.info("======================================================================="); LOGGER.info(" Test run started"); LOGGER.info("======================================================================="); LOGGER.info("======================================================================="); - Environment.print(); } public void testPlanExecutionFinished(TestPlan testPlan) { diff --git a/src/main/java/io/odh/test/framework/TestExceptionCallbackListener.java b/src/main/java/io/odh/test/framework/TestExceptionCallbackListener.java new file mode 100644 index 00000000..b552742e --- /dev/null +++ b/src/main/java/io/odh/test/framework/TestExceptionCallbackListener.java @@ -0,0 +1,53 @@ +/* + * Copyright Skodjob authors. + * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). + */ +package io.odh.test.framework; + +import org.junit.jupiter.api.extension.ExtensionContext; +import org.junit.jupiter.api.extension.LifecycleMethodExecutionExceptionHandler; +import org.junit.jupiter.api.extension.TestExecutionExceptionHandler; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * jUnit5 specific class which listening on test exception callbacks + */ +public class TestExceptionCallbackListener implements TestExecutionExceptionHandler, LifecycleMethodExecutionExceptionHandler { + static final Logger LOGGER = LoggerFactory.getLogger(TestExceptionCallbackListener.class); + + @Override + public void handleTestExecutionException(ExtensionContext context, Throwable throwable) throws Throwable { + LOGGER.error("Test failed at {} : {}", "Test execution", throwable.getMessage(), throwable); + //TODO collect proper logs + throw throwable; + } + + @Override + public void handleBeforeAllMethodExecutionException(ExtensionContext context, Throwable throwable) throws Throwable { + LOGGER.error("Test failed at {} : {}", "Test before all", throwable.getMessage(), throwable); + //TODO collect proper logs + throw throwable; + } + + @Override + public void handleBeforeEachMethodExecutionException(ExtensionContext context, Throwable throwable) throws Throwable { + LOGGER.error("Test failed at {} : {}", "Test before each", throwable.getMessage(), throwable); + //TODO collect proper logs + throw throwable; + } + + @Override + public void handleAfterEachMethodExecutionException(ExtensionContext context, Throwable throwable) throws Throwable { + LOGGER.error("Test failed at {} : {}", "Test after each", throwable.getMessage(), throwable); + //TODO collect proper logs + throw throwable; + } + + @Override + public void handleAfterAllMethodExecutionException(ExtensionContext context, Throwable throwable) throws Throwable { + LOGGER.error("Test failed at {} : {}", "Test after all", throwable.getMessage(), throwable); + //TODO collect proper logs + throw throwable; + } +} diff --git a/src/main/java/io/odh/test/platform/KubeClient.java b/src/main/java/io/odh/test/platform/KubeClient.java index 71a291a9..b3561bb9 100644 --- a/src/main/java/io/odh/test/platform/KubeClient.java +++ b/src/main/java/io/odh/test/platform/KubeClient.java @@ -5,6 +5,7 @@ package io.odh.test.platform; import io.fabric8.kubernetes.api.model.ConfigMap; +import io.fabric8.kubernetes.api.model.HasMetadata; import io.fabric8.kubernetes.api.model.KubernetesResourceList; import io.fabric8.kubernetes.api.model.LabelSelector; import io.fabric8.kubernetes.api.model.Namespace; @@ -30,7 +31,10 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.io.IOException; +import java.io.InputStream; import java.util.List; +import java.util.function.Function; import java.util.stream.Collectors; public class KubeClient { @@ -120,6 +124,20 @@ public boolean namespaceExists(String namespace) { .collect(Collectors.toList()).contains(namespace); } + // ============================================= + // ---------> Create multi-resoruces <--------- + // ============================================= + 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).inNamespace(namespace).create(); + } + }); + } + } + /** * 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 91e39e8a..165b161c 100644 --- a/src/main/java/io/odh/test/platform/KubeUtils.java +++ b/src/main/java/io/odh/test/platform/KubeUtils.java @@ -4,10 +4,23 @@ */ package io.odh.test.platform; +import org.slf4j.Logger; +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 { + static final Logger LOGGER = LoggerFactory.getLogger(KubeUtils.class); + public static io.opendatahub.datasciencecluster.v1.datascienceclusterstatus.Conditions getDscConditionByType(List conditions, String type) { return conditions.stream().filter(c -> c.getType().equals(type)).findFirst().orElseGet(null); } @@ -16,6 +29,53 @@ 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 58e564bb..e98a1125 100644 --- a/src/test/java/io/odh/test/e2e/Abstract.java +++ b/src/test/java/io/odh/test/e2e/Abstract.java @@ -4,11 +4,17 @@ */ package io.odh.test.e2e; +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.DisplayNameGeneration; +import org.junit.jupiter.api.DisplayNameGenerator; import org.junit.jupiter.api.TestInstance; +import org.junit.jupiter.api.extension.ExtendWith; +@DisplayNameGeneration(DisplayNameGenerator.IndicativeSentences.class) +@ExtendWith(TestExceptionCallbackListener.class) @TestInstance(TestInstance.Lifecycle.PER_CLASS) public class Abstract implements TestSeparator { protected KubeClient kubeClient = new KubeClient(TestConstants.ODH_NAMESPACE);