Skip to content

Commit

Permalink
Add ResourceManager for OLM handling
Browse files Browse the repository at this point in the history
Signed-off-by: Jakub Stejskal <[email protected]>
  • Loading branch information
Frawless committed Dec 4, 2023
1 parent 837e276 commit a53076d
Show file tree
Hide file tree
Showing 16 changed files with 878 additions and 11 deletions.
38 changes: 37 additions & 1 deletion src/main/java/io/odh/test/Environment.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@

import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.function.Function;

/**
Expand All @@ -23,6 +24,28 @@ public class Environment {
private static final String PASSWORD_ENV = "KUBE_PASSWORD";
private static final String TOKEN_ENV = "KUBE_TOKEN";
private static final String URL_ENV = "KUBE_URL";
/**
* OLM env variables
*/
private static final String OLM_OPERATOR_NAME_ENV = "OLM_OPERATOR_NAME";
private static final String OLM_OPERATOR_NAMESPACE_ENV = "OLM_OPERATOR_NAMESPACE";
private static final String OLM_OPERATOR_DEPLOYMENT_NAME_ENV = "OLM_OPERATOR_DEPLOYMENT_NAME";
private static final String OLM_SOURCE_NAME_ENV = "OLM_SOURCE_NAME";
private static final String OLM_SOURCE_NAMESPACE_ENV = "OLM_SOURCE_NAMESPACE";
private static final String OLM_APP_BUNDLE_PREFIX_ENV = "OLM_APP_BUNDLE_PREFIX";
private static final String OLM_OPERATOR_VERSION_ENV = "OLM_OPERATOR_VERSION";
private static final String OLM_OPERATOR_CHANNEL_ENV = "OLM_OPERATOR_CHANNEL";

/**
* Defaults
*/
public static final String OLM_OPERATOR_NAME_DEFAULT = "opendatahub-operator";
public static final String OLM_OPERATOR_NAMESPACE_DEFAULT = "openshift-operators";
public static final String OLM_OPERATOR_DEPLOYMENT_NAME_DEFAULT = "opendatahub-operator-controller-manager";
public static final String OLM_SOURCE_NAME_DEFAULT = "community-operators";
public static final String OLM_APP_BUNDLE_PREFIX_DEFAULT = "opendatahub-operator";
public static final String OLM_OPERATOR_CHANNEL_DEFAULT = "fast";
public static final String OLM_OPERATOR_VERSION_DEFAULT = "2.4.0";

/**
* Set values
Expand All @@ -33,14 +56,27 @@ public class Environment {
public static final String KUBE_TOKEN = getOrDefault(TOKEN_ENV, null);
public static final String KUBE_URL = getOrDefault(URL_ENV, null);

// 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);
public static final String OLM_OPERATOR_DEPLOYMENT_NAME = getOrDefault(OLM_OPERATOR_DEPLOYMENT_NAME_ENV, OLM_OPERATOR_DEPLOYMENT_NAME_DEFAULT);
public static final String OLM_SOURCE_NAME = getOrDefault(OLM_SOURCE_NAME_ENV, OLM_SOURCE_NAME_DEFAULT);
public static final String OLM_SOURCE_NAMESPACE = getOrDefault(OLM_SOURCE_NAMESPACE_ENV, "openshift-marketplace");
public static final String OLM_APP_BUNDLE_PREFIX = getOrDefault(OLM_APP_BUNDLE_PREFIX_ENV, OLM_APP_BUNDLE_PREFIX_DEFAULT);
public static final String OLM_OPERATOR_CHANNEL = getOrDefault(OLM_OPERATOR_CHANNEL_ENV, OLM_OPERATOR_CHANNEL_DEFAULT);
public static final String OLM_OPERATOR_VERSION = getOrDefault(OLM_OPERATOR_VERSION_ENV, OLM_OPERATOR_VERSION_DEFAULT);
private Environment() { }

static {
String debugFormat = "{}: {}";
LOGGER.info("Used environment variables:");
VALUES.entrySet().stream()
.sorted(Map.Entry.comparingByKey())
.forEach(entry -> LOGGER.info(debugFormat, entry.getKey(), entry.getValue()));
.forEach(entry -> {
if (!Objects.equals(entry.getValue(), "null")) {
LOGGER.info(debugFormat, entry.getKey(), entry.getValue());
}
});
}

public static void print() { }
Expand Down
11 changes: 11 additions & 0 deletions src/main/java/io/odh/test/TestConstants.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,19 @@
*/
package io.odh.test;

import java.time.Duration;

public class TestConstants {
public static final String ODH_NAMESPACE = "opendatahub";
public static final String DEFAULT_NAMESPACE = "default";

public static final String SUBSCRIPTION = "Subscription";
public static final String OPERATOR_GROUP = "OperatorGroup";

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();
public static final long GLOBAL_TIMEOUT = Duration.ofMinutes(5).toMillis();

private TestConstants() {
}
Expand Down
108 changes: 108 additions & 0 deletions src/main/java/io/odh/test/TestUtils.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
/*
* 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;

import io.odh.test.framework.WaitException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.time.Duration;
import java.util.concurrent.TimeUnit;
import java.util.function.BooleanSupplier;

@SuppressWarnings({"checkstyle:ClassFanOutComplexity"})
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.
*/
public static final int DEFAULT_TIMEOUT_DURATION = 30;

/**
* Default timeout unit for asynchronous tests.
*/
public static final TimeUnit DEFAULT_TIMEOUT_UNIT = TimeUnit.SECONDS;

private TestUtils() {
// All static methods
}

/**
* 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, () -> { });
}

public static long waitFor(String description, long pollIntervalMs, long timeoutMs, BooleanSupplier ready, Runnable onTimeout) {
LOGGER.debug("Waiting for {}", description);
long deadline = System.currentTimeMillis() + timeoutMs;

String exceptionMessage = null;
String previousExceptionMessage = null;

// in case we are polling every 1s, we want to print exception after x tries, not on the first try
// for minutes poll interval will 2 be enough
int exceptionAppearanceCount = Duration.ofMillis(pollIntervalMs).toMinutes() > 0 ? 2 : Math.max((int) (timeoutMs / pollIntervalMs) / 4, 2);
int exceptionCount = 0;
int newExceptionAppearance = 0;

StringWriter stackTraceError = new StringWriter();

while (true) {
boolean result;
try {
result = ready.getAsBoolean();
} catch (Exception e) {
exceptionMessage = e.getMessage();

if (++exceptionCount == exceptionAppearanceCount && exceptionMessage != null && exceptionMessage.equals(previousExceptionMessage)) {
LOGGER.error("While waiting for {} exception occurred: {}", description, exceptionMessage);
// log the stacktrace
e.printStackTrace(new PrintWriter(stackTraceError));
} else if (exceptionMessage != null && !exceptionMessage.equals(previousExceptionMessage) && ++newExceptionAppearance == 2) {
previousExceptionMessage = exceptionMessage;
}

result = false;
}
long timeLeft = deadline - System.currentTimeMillis();
if (result) {
return timeLeft;
}
if (timeLeft <= 0) {
if (exceptionCount > 1) {
LOGGER.error("Exception waiting for {}, {}", description, exceptionMessage);

if (!stackTraceError.toString().isEmpty()) {
// printing handled stacktrace
LOGGER.error(stackTraceError.toString());
}
}
onTimeout.run();
WaitException waitException = new WaitException("Timeout after " + timeoutMs + " ms waiting for " + description);
waitException.printStackTrace();
throw waitException;
}
long sleepTime = Math.min(pollIntervalMs, timeLeft);
if (LOGGER.isTraceEnabled()) {
LOGGER.trace("{} not ready, will try again in {} ms ({}ms till timeout)", description, sleepTime, timeLeft);
}
try {
Thread.sleep(sleepTime);
} catch (InterruptedException e) {
return deadline - System.currentTimeMillis();
}
}
}
}
15 changes: 15 additions & 0 deletions src/main/java/io/odh/test/framework/WaitException.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
/*
* 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;

public class WaitException extends RuntimeException {
public WaitException(String message) {
super(message);
}

public WaitException(Throwable cause) {
super(cause);
}
}
37 changes: 37 additions & 0 deletions src/main/java/io/odh/test/framework/manager/ResourceCondition.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/*
* 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.manager;

import io.fabric8.kubernetes.api.model.HasMetadata;

import java.util.Objects;
import java.util.function.Predicate;

public class ResourceCondition<T extends HasMetadata> {
private final Predicate<T> predicate;
private final String conditionName;

public ResourceCondition(Predicate<T> predicate, String conditionName) {
this.predicate = predicate;
this.conditionName = conditionName;
}

public String getConditionName() {
return conditionName;
}

public Predicate<T> getPredicate() {
return predicate;
}

public static <T extends HasMetadata> ResourceCondition<T> readiness(ResourceType<T> type) {
return new ResourceCondition<>(type::waitForReadiness, "readiness");
}

public static <T extends HasMetadata> ResourceCondition<T> deletion() {
return new ResourceCondition<>(Objects::isNull, "deletion");
}
}
29 changes: 29 additions & 0 deletions src/main/java/io/odh/test/framework/manager/ResourceItem.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/*
* 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.manager;

import io.fabric8.kubernetes.api.model.HasMetadata;

public final class ResourceItem<T extends HasMetadata> {

ThrowableRunner throwableRunner;
T resource;

public ResourceItem(ThrowableRunner throwableRunner, T resource) {
this.throwableRunner = throwableRunner;
this.resource = resource;
}

public ResourceItem(ThrowableRunner throwableRunner) {
this.throwableRunner = throwableRunner;
}

public ThrowableRunner getThrowableRunner() {
return throwableRunner;
}
public T getResource() {
return resource;
}
}
Loading

0 comments on commit a53076d

Please sign in to comment.