diff --git a/operator-framework-core/pom.xml b/operator-framework-core/pom.xml index 8d0515e545..5449e14728 100644 --- a/operator-framework-core/pom.xml +++ b/operator-framework-core/pom.xml @@ -88,16 +88,6 @@ test - - org.apache.logging.log4j - log4j-slf4j-impl - test - - - org.apache.logging.log4j - log4j-core - test - io.fabric8 kubernetes-server-mock diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/Operator.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/Operator.java index c3a2360be4..b2dfef58dd 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/Operator.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/Operator.java @@ -3,6 +3,7 @@ import java.io.Closeable; import java.io.IOException; import java.net.ConnectException; +import java.util.Collections; import java.util.LinkedList; import java.util.List; import java.util.concurrent.locks.ReentrantLock; @@ -57,6 +58,10 @@ public ConfigurationService getConfigurationService() { return configurationService; } + public List getControllers() { + return Collections.unmodifiableList(controllers.controllers); + } + /** * Finishes the operator startup process. This is mostly used in injection-aware applications * where there is no obvious entrypoint to the application which can trigger the injection process @@ -99,6 +104,8 @@ public void close() { "Operator SDK {} is shutting down...", configurationService.getVersion().getSdkVersion()); controllers.close(); + + k8sClient.close(); } /** diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/AbstractConfigurationService.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/AbstractConfigurationService.java index 6560ef9e55..e85a4e8c4b 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/AbstractConfigurationService.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/AbstractConfigurationService.java @@ -7,14 +7,8 @@ import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.stream.Stream; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; public abstract class AbstractConfigurationService implements ConfigurationService { - - public static final String LOGGER_NAME = "Default ConfigurationService implementation"; - protected static final Logger log = LoggerFactory.getLogger(LOGGER_NAME); - private final Map configurations = new ConcurrentHashMap<>(); private final Version version; @@ -60,12 +54,14 @@ public ControllerConfiguration getConfigurationFor final var key = keyFor(controller); final var configuration = configurations.get(key); if (configuration == null) { - log.warn( - "Configuration for controller '{}' was not found. {}", key, getControllersNameMessage()); + logMissingControllerWarning(key, getControllersNameMessage()); } return configuration; } + protected abstract void logMissingControllerWarning(String controllerKey, + String controllersNameMessage); + private String getControllersNameMessage() { return "Known controllers: " + getKnownControllerNames().stream().reduce((s, s2) -> s + ", " + s2).orElse("None") diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/BaseConfigurationService.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/BaseConfigurationService.java new file mode 100644 index 0000000000..5715d707a0 --- /dev/null +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/BaseConfigurationService.java @@ -0,0 +1,28 @@ +package io.javaoperatorsdk.operator.api.config; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class BaseConfigurationService extends AbstractConfigurationService { + + private static final String LOGGER_NAME = "Default ConfigurationService implementation"; + private static final Logger logger = LoggerFactory.getLogger(LOGGER_NAME); + + public BaseConfigurationService(Version version) { + super(version); + } + + @Override + protected void logMissingControllerWarning(String controllerKey, String controllersNameMessage) { + logger.warn("Configuration for controller '{}' was not found. {}", controllerKey, + controllersNameMessage); + } + + public String getLoggerName() { + return LOGGER_NAME; + } + + protected Logger getLogger() { + return logger; + } +} diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/Utils.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/Utils.java index 83a537cedc..ec1b6e3d72 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/Utils.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/Utils.java @@ -37,10 +37,14 @@ public static Version loadFromProperties() { Date builtTime; try { - builtTime = - // RFC 822 date is the default format used by git-commit-id-plugin - new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ") - .parse(properties.getProperty("git.build.time")); + String time = properties.getProperty("git.build.time"); + if (time != null) { + builtTime = + // RFC 822 date is the default format used by git-commit-id-plugin + new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ").parse(time); + } else { + builtTime = Date.from(Instant.EPOCH); + } } catch (Exception e) { log.debug("Couldn't parse git.build.time property", e); builtTime = Date.from(Instant.EPOCH); diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/Version.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/Version.java index b939f8e384..6bfb5bb2e5 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/Version.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/Version.java @@ -1,10 +1,13 @@ package io.javaoperatorsdk.operator.api.config; +import java.time.Instant; import java.util.Date; /** A class encapsulating the version information associated with this SDK instance. */ public class Version { + public static final Version UNKNOWN = new Version("unknown", "unknown", Date.from(Instant.EPOCH)); + private final String sdk; private final String commit; private final Date builtTime; diff --git a/operator-framework-junit5/pom.xml b/operator-framework-junit5/pom.xml new file mode 100644 index 0000000000..926abceadc --- /dev/null +++ b/operator-framework-junit5/pom.xml @@ -0,0 +1,46 @@ + + + + java-operator-sdk + io.javaoperatorsdk + 1.9.7-SNAPSHOT + + 4.0.0 + + operator-framework-junit-5 + Operator SDK - Framework - JUnit 5 extension + + + 11 + 11 + + + + + io.javaoperatorsdk + operator-framework-core + ${project.version} + + + org.junit.jupiter + junit-jupiter-api + + + org.junit.jupiter + junit-jupiter-engine + + + org.assertj + assertj-core + 3.20.2 + + + org.awaitility + awaitility + 4.1.0 + + + + \ No newline at end of file diff --git a/operator-framework-junit5/src/main/java/io/javaoperatorsdk/operator/junit/HasKubernetesClient.java b/operator-framework-junit5/src/main/java/io/javaoperatorsdk/operator/junit/HasKubernetesClient.java new file mode 100644 index 0000000000..d93032333f --- /dev/null +++ b/operator-framework-junit5/src/main/java/io/javaoperatorsdk/operator/junit/HasKubernetesClient.java @@ -0,0 +1,7 @@ +package io.javaoperatorsdk.operator.junit; + +import io.fabric8.kubernetes.client.KubernetesClient; + +public interface HasKubernetesClient { + KubernetesClient getKubernetesClient(); +} diff --git a/operator-framework-junit5/src/main/java/io/javaoperatorsdk/operator/junit/KubernetesClientAware.java b/operator-framework-junit5/src/main/java/io/javaoperatorsdk/operator/junit/KubernetesClientAware.java new file mode 100644 index 0000000000..8a1a702074 --- /dev/null +++ b/operator-framework-junit5/src/main/java/io/javaoperatorsdk/operator/junit/KubernetesClientAware.java @@ -0,0 +1,7 @@ +package io.javaoperatorsdk.operator.junit; + +import io.fabric8.kubernetes.client.KubernetesClient; + +public interface KubernetesClientAware extends HasKubernetesClient { + void setKubernetesClient(KubernetesClient kubernetesClient); +} diff --git a/operator-framework-junit5/src/main/java/io/javaoperatorsdk/operator/junit/OperatorExtension.java b/operator-framework-junit5/src/main/java/io/javaoperatorsdk/operator/junit/OperatorExtension.java new file mode 100644 index 0000000000..3a7ce8b0d2 --- /dev/null +++ b/operator-framework-junit5/src/main/java/io/javaoperatorsdk/operator/junit/OperatorExtension.java @@ -0,0 +1,275 @@ +package io.javaoperatorsdk.operator.junit; + +import static io.javaoperatorsdk.operator.api.config.ControllerConfigurationOverrider.override; + +import io.fabric8.kubernetes.api.model.HasMetadata; +import io.fabric8.kubernetes.api.model.KubernetesResourceList; +import io.fabric8.kubernetes.api.model.NamespaceBuilder; +import io.fabric8.kubernetes.client.DefaultKubernetesClient; +import io.fabric8.kubernetes.client.KubernetesClient; +import io.fabric8.kubernetes.client.dsl.NonNamespaceOperation; +import io.fabric8.kubernetes.client.dsl.Resource; +import io.fabric8.kubernetes.client.utils.KubernetesResourceUtil; +import io.fabric8.kubernetes.client.utils.Utils; +import io.javaoperatorsdk.operator.Operator; +import io.javaoperatorsdk.operator.api.ResourceController; +import io.javaoperatorsdk.operator.api.config.BaseConfigurationService; +import io.javaoperatorsdk.operator.api.config.ConfigurationService; +import io.javaoperatorsdk.operator.api.config.Version; +import io.javaoperatorsdk.operator.processing.ConfiguredController; +import io.javaoperatorsdk.operator.processing.retry.Retry; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; +import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; +import org.awaitility.Awaitility; +import org.junit.jupiter.api.extension.AfterAllCallback; +import org.junit.jupiter.api.extension.AfterEachCallback; +import org.junit.jupiter.api.extension.BeforeAllCallback; +import org.junit.jupiter.api.extension.BeforeEachCallback; +import org.junit.jupiter.api.extension.ExtensionContext; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class OperatorExtension + implements HasKubernetesClient, + BeforeAllCallback, + BeforeEachCallback, + AfterAllCallback, + AfterEachCallback { + + private static final Logger LOGGER = LoggerFactory.getLogger(OperatorExtension.class); + + private final KubernetesClient kubernetesClient; + private final ConfigurationService configurationService; + private final Operator operator; + private final List controllers; + private final boolean preserveNamespaceOnError; + private final boolean waitForNamespaceDeletion; + + private String namespace; + + private OperatorExtension( + ConfigurationService configurationService, + List controllers, + boolean preserveNamespaceOnError, + boolean waitForNamespaceDeletion) { + + this.kubernetesClient = new DefaultKubernetesClient(); + this.configurationService = configurationService; + this.controllers = controllers; + this.operator = new Operator(this.kubernetesClient, this.configurationService); + this.preserveNamespaceOnError = preserveNamespaceOnError; + this.waitForNamespaceDeletion = waitForNamespaceDeletion; + } + + public static Builder builder() { + return new Builder(); + } + + @Override + public void beforeAll(ExtensionContext context) throws Exception { + before(context); + } + + @Override + public void beforeEach(ExtensionContext context) throws Exception { + before(context); + } + + @Override + public void afterAll(ExtensionContext context) throws Exception { + after(context); + } + + @Override + public void afterEach(ExtensionContext context) throws Exception { + after(context); + } + + @Override + public KubernetesClient getKubernetesClient() { + return kubernetesClient; + } + + public String getNamespace() { + return namespace; + } + + @SuppressWarnings({"rawtypes"}) + public List getControllers() { + return operator.getControllers().stream() + .map(ConfiguredController::getController) + .collect(Collectors.toUnmodifiableList()); + } + + @SuppressWarnings({"rawtypes"}) + public T getControllerOfType(Class type) { + return operator.getControllers().stream() + .map(ConfiguredController::getController) + .filter(type::isInstance) + .map(type::cast) + .findFirst() + .orElseThrow( + () -> new IllegalArgumentException("Unable to find a controller of type: " + type)); + } + + public NonNamespaceOperation, Resource> resources( + Class type) { + return kubernetesClient.resources(type).inNamespace(namespace); + } + + public T getNamedResource(Class type, String name) { + return kubernetesClient.resources(type).inNamespace(namespace).withName(name).get(); + } + + public T create(Class type, T resource) { + return kubernetesClient.resources(type).inNamespace(namespace).create(resource); + } + + @SuppressWarnings("unchecked") + protected void before(ExtensionContext context) { + namespace = context.getRequiredTestClass().getSimpleName(); + namespace += "-"; + namespace += context.getRequiredTestMethod().getName(); + namespace = KubernetesResourceUtil.sanitizeName(namespace).toLowerCase(Locale.US); + namespace = namespace.substring(0, Math.min(namespace.length(), 63)); + + LOGGER.info("Initializing integration test in namespace {}", namespace); + + kubernetesClient + .namespaces() + .create(new NamespaceBuilder().withNewMetadata().withName(namespace).endMetadata().build()); + + + for (var ref : controllers) { + final var config = configurationService.getConfigurationFor(ref.controller); + final var oconfig = override(config).settingNamespace(namespace); + final var path = "/META-INF/fabric8/" + config.getCRDName() + "-v1.yml"; + + if (ref.retry != null) { + oconfig.withRetry(ref.retry); + } + + try (InputStream is = getClass().getResourceAsStream(path)) { + kubernetesClient.load(is).createOrReplace(); + } catch (Exception ex) { + throw new IllegalStateException("Cannot apply CRD yaml: " + path, ex); + } + + if (ref.controller instanceof KubernetesClientAware) { + ((KubernetesClientAware) ref.controller).setKubernetesClient(kubernetesClient); + } + + + this.operator.register(ref.controller, oconfig.build()); + } + + this.operator.start(); + } + + protected void after(ExtensionContext context) { + if (namespace != null) { + if (preserveNamespaceOnError && context.getExecutionException().isPresent()) { + LOGGER.info("Preserving namespace {}", namespace); + } else { + LOGGER.info("Deleting namespace {} and stopping operator", namespace); + kubernetesClient.namespaces().withName(namespace).delete(); + if (waitForNamespaceDeletion) { + LOGGER.info("Waiting for namespace {} to be deleted", namespace); + Awaitility.await("namespace deleted") + .pollInterval(50, TimeUnit.MILLISECONDS) + .atMost(60, TimeUnit.SECONDS) + .until(() -> kubernetesClient.namespaces().withName(namespace).get() == null); + } + } + } + + try { + this.operator.close(); + } catch (Exception e) { + // ignored + } + } + + @SuppressWarnings("rawtypes") + public static class Builder { + private final List controllers; + private ConfigurationService configurationService; + private boolean preserveNamespaceOnError; + private boolean waitForNamespaceDeletion; + + protected Builder() { + this.configurationService = new BaseConfigurationService(Version.UNKNOWN); + this.controllers = new ArrayList<>(); + + this.preserveNamespaceOnError = Utils.getSystemPropertyOrEnvVar( + "josdk.it.preserveNamespaceOnError", + false); + + this.waitForNamespaceDeletion = Utils.getSystemPropertyOrEnvVar( + "josdk.it.waitForNamespaceDeletion", + true); + } + + public Builder preserveNamespaceOnError(boolean value) { + this.preserveNamespaceOnError = value; + return this; + } + + public Builder waitForNamespaceDeletion(boolean value) { + this.waitForNamespaceDeletion = value; + return this; + } + + public Builder withConfigurationService(ConfigurationService value) { + configurationService = value; + return this; + } + + @SuppressWarnings("rawtypes") + public Builder withController(ResourceController value) { + controllers.add(new ControllerSpec(value, null)); + return this; + } + + @SuppressWarnings("rawtypes") + public Builder withController(ResourceController value, Retry retry) { + controllers.add(new ControllerSpec(value, retry)); + return this; + } + + @SuppressWarnings("rawtypes") + public Builder withController(Class value) { + try { + controllers.add(new ControllerSpec(value.getConstructor().newInstance(), null)); + } catch (Exception e) { + throw new RuntimeException(e); + } + return this; + } + + public OperatorExtension build() { + return new OperatorExtension( + configurationService, + controllers, + preserveNamespaceOnError, + waitForNamespaceDeletion); + } + } + + @SuppressWarnings("rawtypes") + private static class ControllerSpec { + final ResourceController controller; + final Retry retry; + + public ControllerSpec( + ResourceController controller, + Retry retry) { + this.controller = controller; + this.retry = retry; + } + } +} diff --git a/operator-framework/pom.xml b/operator-framework/pom.xml index 1e0a3f826e..90dff4f18d 100644 --- a/operator-framework/pom.xml +++ b/operator-framework/pom.xml @@ -79,6 +79,12 @@ test-jar test + + io.javaoperatorsdk + operator-framework-junit-5 + ${project.version} + test + \ No newline at end of file diff --git a/operator-framework/src/main/java/io/javaoperatorsdk/operator/config/runtime/DefaultConfigurationService.java b/operator-framework/src/main/java/io/javaoperatorsdk/operator/config/runtime/DefaultConfigurationService.java index 590b9fb746..099754acd1 100644 --- a/operator-framework/src/main/java/io/javaoperatorsdk/operator/config/runtime/DefaultConfigurationService.java +++ b/operator-framework/src/main/java/io/javaoperatorsdk/operator/config/runtime/DefaultConfigurationService.java @@ -2,11 +2,11 @@ import io.fabric8.kubernetes.client.CustomResource; import io.javaoperatorsdk.operator.api.ResourceController; -import io.javaoperatorsdk.operator.api.config.AbstractConfigurationService; +import io.javaoperatorsdk.operator.api.config.BaseConfigurationService; import io.javaoperatorsdk.operator.api.config.ControllerConfiguration; import io.javaoperatorsdk.operator.api.config.Utils; -public class DefaultConfigurationService extends AbstractConfigurationService { +public class DefaultConfigurationService extends BaseConfigurationService { private static final DefaultConfigurationService instance = new DefaultConfigurationService(); @@ -32,7 +32,7 @@ ControllerConfiguration getConfigurationFor( // create the configuration on demand and register it config = new AnnotationConfiguration<>(controller); register(config); - log.info( + getLogger().info( "Created configuration for controller {} with name {}", controller.getClass().getName(), config.getName()); diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/ConcurrencyIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/ConcurrencyIT.java index f8157c648b..bf06bc054a 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/ConcurrencyIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/ConcurrencyIT.java @@ -4,129 +4,98 @@ import static org.awaitility.Awaitility.await; import io.fabric8.kubernetes.api.model.ConfigMap; -import io.fabric8.kubernetes.client.DefaultKubernetesClient; -import io.fabric8.kubernetes.client.KubernetesClient; +import io.javaoperatorsdk.operator.config.runtime.DefaultConfigurationService; +import io.javaoperatorsdk.operator.junit.OperatorExtension; import io.javaoperatorsdk.operator.sample.simple.TestCustomResource; import io.javaoperatorsdk.operator.sample.simple.TestCustomResourceController; +import io.javaoperatorsdk.operator.support.TestUtils; import java.util.List; import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.TestInstance; +import org.junit.jupiter.api.extension.RegisterExtension; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -@TestInstance(TestInstance.Lifecycle.PER_CLASS) public class ConcurrencyIT { - public static final int NUMBER_OF_RESOURCES_CREATED = 50; public static final int NUMBER_OF_RESOURCES_DELETED = 30; public static final int NUMBER_OF_RESOURCES_UPDATED = 20; - private static final Logger log = LoggerFactory.getLogger(ConcurrencyIT.class); public static final String UPDATED_SUFFIX = "_updated"; - private IntegrationTestSupport integrationTest = new IntegrationTestSupport(); - - @BeforeAll - public void setup() { - KubernetesClient k8sClient = new DefaultKubernetesClient(); - integrationTest.initialize(k8sClient, new TestCustomResourceController(k8sClient, true)); - } + private static final Logger log = LoggerFactory.getLogger(ConcurrencyIT.class); - @BeforeEach - public void cleanup() { - integrationTest.cleanup(); - } + @RegisterExtension + OperatorExtension operator = + OperatorExtension.builder() + .withConfigurationService(DefaultConfigurationService.instance()) + .withController(new TestCustomResourceController(true)) + .build(); @Test - public void manyResourcesGetCreatedUpdatedAndDeleted() { - integrationTest.teardownIfSuccess( - () -> { - log.info("Creating {} new resources", NUMBER_OF_RESOURCES_CREATED); - for (int i = 0; i < NUMBER_OF_RESOURCES_CREATED; i++) { - TestCustomResource tcr = integrationTest.createTestCustomResource(String.valueOf(i)); - integrationTest - .getCrOperations() - .inNamespace(IntegrationTestSupport.TEST_NAMESPACE) - .create(tcr); - } + public void manyResourcesGetCreatedUpdatedAndDeleted() throws InterruptedException { + log.info("Creating {} new resources", NUMBER_OF_RESOURCES_CREATED); + for (int i = 0; i < NUMBER_OF_RESOURCES_CREATED; i++) { + TestCustomResource tcr = TestUtils.testCustomResourceWithPrefix(String.valueOf(i)); + operator.resources(TestCustomResource.class).create(tcr); + } - await() - .atMost(1, TimeUnit.MINUTES) - .untilAsserted( - () -> { - List items = - integrationTest - .getK8sClient() - .configMaps() - .inNamespace(IntegrationTestSupport.TEST_NAMESPACE) - .withLabel( - "managedBy", TestCustomResourceController.class.getSimpleName()) - .list() - .getItems(); - assertThat(items).hasSize(NUMBER_OF_RESOURCES_CREATED); - }); + await() + .atMost(1, TimeUnit.MINUTES) + .untilAsserted( + () -> { + List items = + operator.resources(ConfigMap.class) + .withLabel( + "managedBy", TestCustomResourceController.class.getSimpleName()) + .list() + .getItems(); + assertThat(items).hasSize(NUMBER_OF_RESOURCES_CREATED); + }); - log.info("Updating {} resources", NUMBER_OF_RESOURCES_UPDATED); - // update some resources - for (int i = 0; i < NUMBER_OF_RESOURCES_UPDATED; i++) { - TestCustomResource tcr = - (TestCustomResource) integrationTest - .getCrOperations() - .inNamespace(IntegrationTestSupport.TEST_NAMESPACE) - .withName(IntegrationTestSupport.TEST_CUSTOM_RESOURCE_PREFIX + i) - .get(); - tcr.getSpec().setValue(i + UPDATED_SUFFIX); - integrationTest - .getCrOperations() - .inNamespace(IntegrationTestSupport.TEST_NAMESPACE) - .createOrReplace(tcr); - } - // sleep for a short time to make variability to the test, so some updates are not - // executed before delete - Thread.sleep(300); + log.info("Updating {} resources", NUMBER_OF_RESOURCES_UPDATED); + // update some resources + for (int i = 0; i < NUMBER_OF_RESOURCES_UPDATED; i++) { + TestCustomResource tcr = + operator.getNamedResource(TestCustomResource.class, + TestUtils.TEST_CUSTOM_RESOURCE_PREFIX + i); + tcr.getSpec().setValue(i + UPDATED_SUFFIX); + operator.resources(TestCustomResource.class) + .createOrReplace(tcr); + } + // sleep for a short time to make variability to the test, so some updates are not + // executed before delete + Thread.sleep(300); - log.info("Deleting {} resources", NUMBER_OF_RESOURCES_DELETED); - for (int i = 0; i < NUMBER_OF_RESOURCES_DELETED; i++) { - TestCustomResource tcr = integrationTest.createTestCustomResource(String.valueOf(i)); - integrationTest - .getCrOperations() - .inNamespace(IntegrationTestSupport.TEST_NAMESPACE) - .delete(tcr); - } + log.info("Deleting {} resources", NUMBER_OF_RESOURCES_DELETED); + for (int i = 0; i < NUMBER_OF_RESOURCES_DELETED; i++) { + TestCustomResource tcr = TestUtils.testCustomResourceWithPrefix(String.valueOf(i)); + operator.resources(TestCustomResource.class).delete(tcr); + } - await() - .atMost(1, TimeUnit.MINUTES) - .untilAsserted( - () -> { - List items = - integrationTest - .getK8sClient() - .configMaps() - .inNamespace(IntegrationTestSupport.TEST_NAMESPACE) - .withLabel( - "managedBy", TestCustomResourceController.class.getSimpleName()) - .list() - .getItems(); - // reducing configmaps to names only - better for debugging - List itemDescs = - items.stream() - .map(configMap -> configMap.getMetadata().getName()) - .collect(Collectors.toList()); - assertThat(itemDescs) - .hasSize(NUMBER_OF_RESOURCES_CREATED - NUMBER_OF_RESOURCES_DELETED); + await() + .atMost(1, TimeUnit.MINUTES) + .untilAsserted( + () -> { + List items = + operator.resources(ConfigMap.class) + .withLabel( + "managedBy", TestCustomResourceController.class.getSimpleName()) + .list() + .getItems(); + // reducing configmaps to names only - better for debugging + List itemDescs = + items.stream() + .map(configMap -> configMap.getMetadata().getName()) + .collect(Collectors.toList()); + assertThat(itemDescs) + .hasSize(NUMBER_OF_RESOURCES_CREATED - NUMBER_OF_RESOURCES_DELETED); - List crs = - integrationTest - .getK8sClient() - .customResources(TestCustomResource.class) - .inNamespace(IntegrationTestSupport.TEST_NAMESPACE) - .list() - .getItems(); - assertThat(crs) - .hasSize(NUMBER_OF_RESOURCES_CREATED - NUMBER_OF_RESOURCES_DELETED); - }); - }); + List crs = + operator.resources(TestCustomResource.class) + .list() + .getItems(); + assertThat(crs) + .hasSize(NUMBER_OF_RESOURCES_CREATED - NUMBER_OF_RESOURCES_DELETED); + }); } } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/ControllerExecutionIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/ControllerExecutionIT.java index 843f7a9103..b4bfe376f2 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/ControllerExecutionIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/ControllerExecutionIT.java @@ -4,59 +4,44 @@ import static org.awaitility.Awaitility.await; import io.fabric8.kubernetes.api.model.ConfigMap; -import io.fabric8.kubernetes.client.DefaultKubernetesClient; -import io.fabric8.kubernetes.client.KubernetesClient; +import io.javaoperatorsdk.operator.config.runtime.DefaultConfigurationService; +import io.javaoperatorsdk.operator.junit.OperatorExtension; import io.javaoperatorsdk.operator.sample.simple.TestCustomResource; import io.javaoperatorsdk.operator.sample.simple.TestCustomResourceController; +import io.javaoperatorsdk.operator.support.TestUtils; import java.util.concurrent.TimeUnit; import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.TestInstance; +import org.junit.jupiter.api.extension.RegisterExtension; -@TestInstance(TestInstance.Lifecycle.PER_METHOD) public class ControllerExecutionIT { - - private IntegrationTestSupport integrationTestSupport = new IntegrationTestSupport(); - - public void initAndCleanup(boolean controllerStatusUpdate) { - KubernetesClient k8sClient = new DefaultKubernetesClient(); - integrationTestSupport.initialize( - k8sClient, new TestCustomResourceController(k8sClient, controllerStatusUpdate)); - integrationTestSupport.cleanup(); - } + @RegisterExtension + OperatorExtension operator = + OperatorExtension.builder() + .withConfigurationService(DefaultConfigurationService.instance()) + .withController(new TestCustomResourceController(true)) + .build(); @Test public void configMapGetsCreatedForTestCustomResource() { - initAndCleanup(true); - integrationTestSupport.teardownIfSuccess( - () -> { - TestCustomResource resource = TestUtils.testCustomResource(); + operator.getControllerOfType(TestCustomResourceController.class).setUpdateStatus(true); - integrationTestSupport - .getCrOperations() - .inNamespace(IntegrationTestSupport.TEST_NAMESPACE) - .create(resource); + TestCustomResource resource = TestUtils.testCustomResource(); + operator.create(TestCustomResource.class, resource); - awaitResourcesCreatedOrUpdated(); - awaitStatusUpdated(); - assertThat(integrationTestSupport.numberOfControllerExecutions()).isEqualTo(2); - }); + awaitResourcesCreatedOrUpdated(); + awaitStatusUpdated(); + assertThat(TestUtils.getNumberOfExecutions(operator)).isEqualTo(2); } @Test public void eventIsSkippedChangedOnMetadataOnlyUpdate() { - initAndCleanup(false); - integrationTestSupport.teardownIfSuccess( - () -> { - TestCustomResource resource = TestUtils.testCustomResource(); + operator.getControllerOfType(TestCustomResourceController.class).setUpdateStatus(false); - integrationTestSupport - .getCrOperations() - .inNamespace(IntegrationTestSupport.TEST_NAMESPACE) - .create(resource); + TestCustomResource resource = TestUtils.testCustomResource(); + operator.create(TestCustomResource.class, resource); - awaitResourcesCreatedOrUpdated(); - assertThat(integrationTestSupport.numberOfControllerExecutions()).isEqualTo(1); - }); + awaitResourcesCreatedOrUpdated(); + assertThat(TestUtils.getNumberOfExecutions(operator)).isEqualTo(1); } void awaitResourcesCreatedOrUpdated() { @@ -65,12 +50,7 @@ void awaitResourcesCreatedOrUpdated() { .untilAsserted( () -> { ConfigMap configMap = - integrationTestSupport - .getK8sClient() - .configMaps() - .inNamespace(IntegrationTestSupport.TEST_NAMESPACE) - .withName("test-config-map") - .get(); + operator.getNamedResource(ConfigMap.class, "test-config-map"); assertThat(configMap).isNotNull(); assertThat(configMap.getData().get("test-key")).isEqualTo("test-value"); }); @@ -86,11 +66,8 @@ void awaitStatusUpdated(int timeout) { .untilAsserted( () -> { TestCustomResource cr = - (TestCustomResource) integrationTestSupport - .getCrOperations() - .inNamespace(IntegrationTestSupport.TEST_NAMESPACE) - .withName(TestUtils.TEST_CUSTOM_RESOURCE_NAME) - .get(); + operator.getNamedResource(TestCustomResource.class, + TestUtils.TEST_CUSTOM_RESOURCE_NAME); assertThat(cr).isNotNull(); assertThat(cr.getStatus()).isNotNull(); assertThat(cr.getStatus().getConfigMapStatus()).isEqualTo("ConfigMap Ready"); diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/EventSourceIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/EventSourceIT.java index 05a4a3df8f..771c5c763b 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/EventSourceIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/EventSourceIT.java @@ -1,52 +1,42 @@ package io.javaoperatorsdk.operator; import static org.assertj.core.api.Assertions.assertThat; +import static org.awaitility.Awaitility.await; import io.fabric8.kubernetes.api.model.ObjectMetaBuilder; -import io.fabric8.kubernetes.client.DefaultKubernetesClient; -import io.fabric8.kubernetes.client.KubernetesClient; +import io.javaoperatorsdk.operator.config.runtime.DefaultConfigurationService; +import io.javaoperatorsdk.operator.junit.OperatorExtension; import io.javaoperatorsdk.operator.sample.event.EventSourceTestCustomResource; import io.javaoperatorsdk.operator.sample.event.EventSourceTestCustomResourceController; import io.javaoperatorsdk.operator.sample.event.EventSourceTestCustomResourceSpec; -import org.junit.jupiter.api.BeforeEach; +import io.javaoperatorsdk.operator.support.TestUtils; +import java.util.concurrent.TimeUnit; import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.TestInstance; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import org.junit.jupiter.api.extension.RegisterExtension; -@TestInstance(TestInstance.Lifecycle.PER_CLASS) public class EventSourceIT { - - private static final Logger log = LoggerFactory.getLogger(EventSourceIT.class); - - public static final int EXPECTED_TIMER_EVENT_COUNT = 3; - private final IntegrationTestSupport integrationTestSupport = new IntegrationTestSupport(); - - @BeforeEach - public void initAndCleanup() { - KubernetesClient k8sClient = new DefaultKubernetesClient(); - integrationTestSupport.initialize(k8sClient, new EventSourceTestCustomResourceController()); - integrationTestSupport.cleanup(); - } + @RegisterExtension + OperatorExtension operator = + OperatorExtension.builder() + .withConfigurationService(DefaultConfigurationService.instance()) + .withController(EventSourceTestCustomResourceController.class) + .build(); @Test public void receivingPeriodicEvents() { - integrationTestSupport.teardownIfSuccess( - () -> { - EventSourceTestCustomResource resource = createTestCustomResource("1"); - integrationTestSupport - .getCrOperations() - .inNamespace(IntegrationTestSupport.TEST_NAMESPACE) - .create(resource); - - Thread.sleep( - EventSourceTestCustomResourceController.TIMER_DELAY - + EXPECTED_TIMER_EVENT_COUNT - * EventSourceTestCustomResourceController.TIMER_PERIOD); - - assertThat(integrationTestSupport.numberOfControllerExecutions()) - .isGreaterThanOrEqualTo(EXPECTED_TIMER_EVENT_COUNT + 1); - }); + EventSourceTestCustomResource resource = createTestCustomResource("1"); + + operator.create(EventSourceTestCustomResource.class, resource); + + await() + .atMost(5, TimeUnit.SECONDS) + .pollInterval( + EventSourceTestCustomResourceController.TIMER_PERIOD / 2, TimeUnit.MILLISECONDS) + .untilAsserted( + () -> { + assertThat(TestUtils.getNumberOfExecutions(operator)) + .isGreaterThanOrEqualTo(4); + }); } public EventSourceTestCustomResource createTestCustomResource(String id) { @@ -54,7 +44,7 @@ public EventSourceTestCustomResource createTestCustomResource(String id) { resource.setMetadata( new ObjectMetaBuilder() .withName("eventsource-" + id) - .withNamespace(IntegrationTestSupport.TEST_NAMESPACE) + .withNamespace(operator.getNamespace()) .withFinalizers(EventSourceTestCustomResourceController.FINALIZER_NAME) .build()); resource.setKind("Eventsourcesample"); diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/IntegrationTestSupport.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/IntegrationTestSupport.java deleted file mode 100644 index 271c50514b..0000000000 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/IntegrationTestSupport.java +++ /dev/null @@ -1,198 +0,0 @@ -package io.javaoperatorsdk.operator; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.awaitility.Awaitility.await; - -import java.io.IOException; -import java.io.InputStream; -import java.util.concurrent.TimeUnit; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import io.fabric8.kubernetes.api.model.KubernetesResourceList; -import io.fabric8.kubernetes.api.model.Namespace; -import io.fabric8.kubernetes.api.model.NamespaceBuilder; -import io.fabric8.kubernetes.api.model.ObjectMetaBuilder; -import io.fabric8.kubernetes.api.model.apiextensions.v1.CustomResourceDefinition; -import io.fabric8.kubernetes.client.CustomResource; -import io.fabric8.kubernetes.client.KubernetesClient; -import io.fabric8.kubernetes.client.dsl.MixedOperation; -import io.fabric8.kubernetes.client.dsl.Resource; -import io.fabric8.kubernetes.client.utils.Serialization; -import io.javaoperatorsdk.operator.api.ResourceController; -import io.javaoperatorsdk.operator.api.config.ControllerConfigurationOverrider; -import io.javaoperatorsdk.operator.config.runtime.DefaultConfigurationService; -import io.javaoperatorsdk.operator.processing.retry.Retry; -import io.javaoperatorsdk.operator.sample.simple.TestCustomResource; -import io.javaoperatorsdk.operator.sample.simple.TestCustomResourceSpec; - -public class IntegrationTestSupport { - - public static final String TEST_NAMESPACE = "java-operator-sdk-int-test"; - public static final String TEST_CUSTOM_RESOURCE_PREFIX = "test-custom-resource-"; - private static final Logger log = LoggerFactory.getLogger(IntegrationTestSupport.class); - private KubernetesClient k8sClient; - private MixedOperation, Resource> crOperations; - private Operator operator; - private ResourceController controller; - - public void initialize(KubernetesClient k8sClient, ResourceController controller) { - initialize(k8sClient, controller, null); - } - - public void initialize(KubernetesClient k8sClient, ResourceController controller, Retry retry) { - log.info("Initializing integration test in namespace {}", TEST_NAMESPACE); - this.k8sClient = k8sClient; - this.controller = controller; - - final var configurationService = DefaultConfigurationService.instance(); - - final var config = configurationService.getConfigurationFor(controller); - // load generated CRD - final var crdPath = "/META-INF/fabric8/" + config.getCRDName() + "-v1.yml"; - loadCRDAndApplyToCluster(crdPath); - - final var customResourceClass = config.getCustomResourceClass(); - this.crOperations = k8sClient.resources(customResourceClass); - - final var namespaces = k8sClient.namespaces(); - if (namespaces.withName(TEST_NAMESPACE).get() == null) { - namespaces.create( - new NamespaceBuilder().withNewMetadata().withName(TEST_NAMESPACE).endMetadata().build()); - } - operator = new Operator(k8sClient, configurationService); - final var overriddenConfig = - ControllerConfigurationOverrider.override(config).settingNamespace(TEST_NAMESPACE); - if (retry != null) { - overriddenConfig.withRetry(retry); - } - operator.register(controller, overriddenConfig.build()); - operator.start(); - log.info("Operator is running with {}", controller.getClass().getCanonicalName()); - } - - public void loadCRDAndApplyToCluster(String classPathYaml) { - var crd = loadYaml(CustomResourceDefinition.class, classPathYaml); - if ("apiextensions.k8s.io/v1".equals(crd.getApiVersion())) { - k8sClient.apiextensions().v1().customResourceDefinitions().createOrReplace(crd); - } else { - var crd2 = - loadYaml( - io.fabric8.kubernetes.api.model.apiextensions.v1beta1.CustomResourceDefinition.class, - classPathYaml); - k8sClient.apiextensions().v1beta1().customResourceDefinitions().createOrReplace(crd2); - } - } - - public void cleanup() { - log.info("Cleaning up namespace {}", TEST_NAMESPACE); - - // we depend on the actual operator from the startup to handle the finalizers and clean up - // resources from previous test runs - crOperations.inNamespace(TEST_NAMESPACE).delete(crOperations.list().getItems()); - - await("all CRs cleaned up") - .atMost(60, TimeUnit.SECONDS) - .untilAsserted( - () -> assertThat(crOperations.inNamespace(TEST_NAMESPACE).list().getItems()).isEmpty()); - - k8sClient - .configMaps() - .inNamespace(TEST_NAMESPACE) - .withLabel("managedBy", controller.getClass().getSimpleName()) - .delete(); - - await("all config maps cleaned up") - .atMost(60, TimeUnit.SECONDS) - .untilAsserted( - () -> { - assertThat( - k8sClient - .configMaps() - .inNamespace(TEST_NAMESPACE) - .withLabel("managedBy", controller.getClass().getSimpleName()) - .list() - .getItems() - .isEmpty()); - }); - - log.info("Cleaned up namespace " + TEST_NAMESPACE); - } - - /** - * Use this method to execute the cleanup of the integration test namespace only in case the test - * was successful. This is useful to keep the Kubernetes resources around to debug a failed test - * run. Unfortunately I couldn't make this work with standard JUnit methods as the @AfterAll - * method doesn't know if the tests succeeded or not. - * - * @param test The code of the actual test. - * @throws Exception if the test threw an exception. - */ - public void teardownIfSuccess(TestRun test) { - try { - test.run(); - - log.info("Deleting namespace {} and stopping operator", TEST_NAMESPACE); - Namespace namespace = k8sClient.namespaces().withName(TEST_NAMESPACE).get(); - if (namespace.getStatus().getPhase().equals("Active")) { - k8sClient.namespaces().withName(TEST_NAMESPACE).delete(); - } - await("namespace deleted") - .atMost(45, TimeUnit.SECONDS) - .until(() -> k8sClient.namespaces().withName(TEST_NAMESPACE).get() == null); - } catch (Exception e) { - throw new IllegalStateException(e); - } finally { - k8sClient.close(); - } - } - - public int numberOfControllerExecutions() { - return ((TestExecutionInfoProvider) controller).getNumberOfExecutions(); - } - - private T loadYaml(Class clazz, String yaml) { - try (InputStream is = getClass().getResourceAsStream(yaml)) { - return Serialization.unmarshal(is, clazz); - } catch (IOException ex) { - throw new IllegalStateException("Cannot find yaml on classpath: " + yaml); - } - } - - public TestCustomResource createTestCustomResource(String id) { - TestCustomResource resource = new TestCustomResource(); - resource.setMetadata( - new ObjectMetaBuilder() - .withName(TEST_CUSTOM_RESOURCE_PREFIX + id) - .withNamespace(TEST_NAMESPACE) - .build()); - resource.setKind("CustomService"); - resource.setSpec(new TestCustomResourceSpec()); - resource.getSpec().setConfigMapName("test-config-map-" + id); - resource.getSpec().setKey("test-key"); - resource.getSpec().setValue(id); - return resource; - } - - public KubernetesClient getK8sClient() { - return k8sClient; - } - - public MixedOperation, Resource> getCrOperations() { - return crOperations; - } - - public CustomResource getCustomResource(String name) { - return getCrOperations().inNamespace(TEST_NAMESPACE).withName(name).get(); - } - - public Operator getOperator() { - return operator; - } - - public interface TestRun { - - void run() throws Exception; - } -} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/RetryIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/RetryIT.java index bdbb5f561c..b3b4be5f95 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/RetryIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/RetryIT.java @@ -1,54 +1,57 @@ package io.javaoperatorsdk.operator; -import static io.javaoperatorsdk.operator.IntegrationTestSupport.TEST_NAMESPACE; import static org.assertj.core.api.Assertions.assertThat; +import static org.awaitility.Awaitility.await; import io.fabric8.kubernetes.api.model.ObjectMetaBuilder; -import io.fabric8.kubernetes.client.DefaultKubernetesClient; -import io.fabric8.kubernetes.client.KubernetesClient; +import io.javaoperatorsdk.operator.config.runtime.DefaultConfigurationService; +import io.javaoperatorsdk.operator.junit.OperatorExtension; import io.javaoperatorsdk.operator.processing.retry.GenericRetry; -import io.javaoperatorsdk.operator.processing.retry.Retry; import io.javaoperatorsdk.operator.sample.retry.RetryTestCustomResource; import io.javaoperatorsdk.operator.sample.retry.RetryTestCustomResourceController; import io.javaoperatorsdk.operator.sample.retry.RetryTestCustomResourceSpec; import io.javaoperatorsdk.operator.sample.retry.RetryTestCustomResourceStatus; -import org.junit.jupiter.api.BeforeEach; +import io.javaoperatorsdk.operator.support.TestUtils; +import java.util.concurrent.TimeUnit; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; public class RetryIT { - public static final int RETRY_INTERVAL = 150; - private IntegrationTestSupport integrationTestSupport = new IntegrationTestSupport(); - @BeforeEach - public void initAndCleanup() { - Retry retry = - new GenericRetry().setInitialInterval(RETRY_INTERVAL).withLinearRetry().setMaxAttempts(5); - KubernetesClient k8sClient = new DefaultKubernetesClient(); - integrationTestSupport.initialize(k8sClient, new RetryTestCustomResourceController(), retry); - integrationTestSupport.cleanup(); - } + @RegisterExtension + OperatorExtension operator = + OperatorExtension.builder() + .withConfigurationService(DefaultConfigurationService.instance()) + .withController( + new RetryTestCustomResourceController(), + new GenericRetry().setInitialInterval(RETRY_INTERVAL).withLinearRetry() + .setMaxAttempts(5)) + .build(); + @Test public void retryFailedExecution() { - integrationTestSupport.teardownIfSuccess( - () -> { - RetryTestCustomResource resource = createTestCustomResource("1"); - integrationTestSupport.getCrOperations().inNamespace(TEST_NAMESPACE).create(resource); + RetryTestCustomResource resource = createTestCustomResource("1"); - Thread.sleep( - RETRY_INTERVAL * (RetryTestCustomResourceController.NUMBER_FAILED_EXECUTIONS + 2)); + operator.create(RetryTestCustomResource.class, resource); - assertThat(integrationTestSupport.numberOfControllerExecutions()) - .isGreaterThanOrEqualTo( - RetryTestCustomResourceController.NUMBER_FAILED_EXECUTIONS + 1); + await("cr status updated") + .pollDelay( + RETRY_INTERVAL * (RetryTestCustomResourceController.NUMBER_FAILED_EXECUTIONS + 2), + TimeUnit.MILLISECONDS) + .pollInterval( + RETRY_INTERVAL, + TimeUnit.MILLISECONDS) + .atMost(5, TimeUnit.SECONDS) + .untilAsserted(() -> { + assertThat( + TestUtils.getNumberOfExecutions(operator)) + .isEqualTo(RetryTestCustomResourceController.NUMBER_FAILED_EXECUTIONS + 1); RetryTestCustomResource finalResource = - (RetryTestCustomResource) integrationTestSupport - .getCrOperations() - .inNamespace(TEST_NAMESPACE) - .withName(resource.getMetadata().getName()) - .get(); + operator.getNamedResource(RetryTestCustomResource.class, + resource.getMetadata().getName()); assertThat(finalResource.getStatus().getState()) .isEqualTo(RetryTestCustomResourceStatus.State.SUCCESS); }); @@ -59,7 +62,6 @@ public RetryTestCustomResource createTestCustomResource(String id) { resource.setMetadata( new ObjectMetaBuilder() .withName("retrysource-" + id) - .withNamespace(TEST_NAMESPACE) .withFinalizers(RetryTestCustomResourceController.FINALIZER_NAME) .build()); resource.setKind("retrysample"); diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/SubResourceUpdateIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/SubResourceUpdateIT.java index 0a7cbada4d..ac7fa95c86 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/SubResourceUpdateIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/SubResourceUpdateIT.java @@ -1,81 +1,73 @@ package io.javaoperatorsdk.operator; -import static io.javaoperatorsdk.operator.IntegrationTestSupport.TEST_NAMESPACE; -import static org.assertj.core.api.Assertions.assertThat; -import static org.awaitility.Awaitility.await; +import io.javaoperatorsdk.operator.support.TestUtils; +import java.util.Collections; +import java.util.concurrent.TimeUnit; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; import io.fabric8.kubernetes.api.model.ObjectMetaBuilder; -import io.fabric8.kubernetes.client.DefaultKubernetesClient; -import io.fabric8.kubernetes.client.KubernetesClient; +import io.javaoperatorsdk.operator.config.runtime.DefaultConfigurationService; +import io.javaoperatorsdk.operator.junit.OperatorExtension; import io.javaoperatorsdk.operator.sample.subresource.SubResourceTestCustomResource; import io.javaoperatorsdk.operator.sample.subresource.SubResourceTestCustomResourceController; import io.javaoperatorsdk.operator.sample.subresource.SubResourceTestCustomResourceSpec; import io.javaoperatorsdk.operator.sample.subresource.SubResourceTestCustomResourceStatus; -import java.util.Collections; -import java.util.concurrent.TimeUnit; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.TestInstance; -@TestInstance(TestInstance.Lifecycle.PER_CLASS) -public class SubResourceUpdateIT { +import static org.assertj.core.api.Assertions.assertThat; +import static org.awaitility.Awaitility.await; - private IntegrationTestSupport integrationTestSupport = new IntegrationTestSupport(); +public class SubResourceUpdateIT { - @BeforeEach - public void initAndCleanup() { - KubernetesClient k8sClient = new DefaultKubernetesClient(); - integrationTestSupport.initialize(k8sClient, new SubResourceTestCustomResourceController()); - integrationTestSupport.cleanup(); - } + @RegisterExtension + OperatorExtension operator = + OperatorExtension.builder() + .withConfigurationService(DefaultConfigurationService.instance()) + .withController(SubResourceTestCustomResourceController.class) + .build(); @Test public void updatesSubResourceStatus() { - integrationTestSupport.teardownIfSuccess( - () -> { - SubResourceTestCustomResource resource = createTestCustomResource("1"); - integrationTestSupport.getCrOperations().inNamespace(TEST_NAMESPACE).create(resource); - - awaitStatusUpdated(resource.getMetadata().getName()); - // wait for sure, there are no more events - waitXms(200); - // there is no event on status update processed - assertThat(integrationTestSupport.numberOfControllerExecutions()).isEqualTo(2); - }); + SubResourceTestCustomResource resource = createTestCustomResource("1"); + operator.create(SubResourceTestCustomResource.class, resource); + + awaitStatusUpdated(resource.getMetadata().getName()); + // wait for sure, there are no more events + waitXms(200); + // there is no event on status update processed + assertThat(TestUtils.getNumberOfExecutions(operator)) + .isEqualTo(2); } @Test public void updatesSubResourceStatusNoFinalizer() { - integrationTestSupport.teardownIfSuccess( - () -> { - SubResourceTestCustomResource resource = createTestCustomResource("1"); - resource.getMetadata().setFinalizers(Collections.EMPTY_LIST); - - integrationTestSupport.getCrOperations().inNamespace(TEST_NAMESPACE).create(resource); - - awaitStatusUpdated(resource.getMetadata().getName()); - // wait for sure, there are no more events - waitXms(200); - // there is no event on status update processed - assertThat(integrationTestSupport.numberOfControllerExecutions()).isEqualTo(2); - }); + SubResourceTestCustomResource resource = createTestCustomResource("1"); + resource.getMetadata().setFinalizers(Collections.emptyList()); + + operator.create(SubResourceTestCustomResource.class, resource); + + awaitStatusUpdated(resource.getMetadata().getName()); + // wait for sure, there are no more events + waitXms(200); + // there is no event on status update processed + assertThat(TestUtils.getNumberOfExecutions(operator)) + .isEqualTo(2); } /** Note that we check on controller impl if there is finalizer on execution. */ @Test public void ifNoFinalizerPresentFirstAddsTheFinalizerThenExecutesControllerAgain() { - integrationTestSupport.teardownIfSuccess( - () -> { - SubResourceTestCustomResource resource = createTestCustomResource("1"); - resource.getMetadata().getFinalizers().clear(); - integrationTestSupport.getCrOperations().inNamespace(TEST_NAMESPACE).create(resource); - - awaitStatusUpdated(resource.getMetadata().getName()); - // wait for sure, there are no more events - waitXms(200); - // there is no event on status update processed - assertThat(integrationTestSupport.numberOfControllerExecutions()).isEqualTo(2); - }); + SubResourceTestCustomResource resource = createTestCustomResource("1"); + resource.getMetadata().getFinalizers().clear(); + operator.create(SubResourceTestCustomResource.class, resource); + + awaitStatusUpdated(resource.getMetadata().getName()); + // wait for sure, there are no more events + waitXms(200); + // there is no event on status update processed + assertThat(TestUtils.getNumberOfExecutions(operator)) + .isEqualTo(2); } /** @@ -86,24 +78,19 @@ public void ifNoFinalizerPresentFirstAddsTheFinalizerThenExecutesControllerAgain */ @Test public void updateCustomResourceAfterSubResourceChange() { - integrationTestSupport.teardownIfSuccess( - () -> { - SubResourceTestCustomResource resource = createTestCustomResource("1"); - integrationTestSupport.getCrOperations().inNamespace(TEST_NAMESPACE).create(resource); - - resource.getSpec().setValue("new value"); - integrationTestSupport - .getCrOperations() - .inNamespace(TEST_NAMESPACE) - .createOrReplace(resource); - - awaitStatusUpdated(resource.getMetadata().getName()); - - // wait for sure, there are no more events - waitXms(200); - // there is no event on status update processed - assertThat(integrationTestSupport.numberOfControllerExecutions()).isEqualTo(3); - }); + SubResourceTestCustomResource resource = createTestCustomResource("1"); + operator.create(SubResourceTestCustomResource.class, resource); + + resource.getSpec().setValue("new value"); + operator.resources(SubResourceTestCustomResource.class).createOrReplace(resource); + + awaitStatusUpdated(resource.getMetadata().getName()); + + // wait for sure, there are no more events + waitXms(200); + // there is no event on status update processed + assertThat(TestUtils.getNumberOfExecutions(operator)) + .isEqualTo(3); } void awaitStatusUpdated(String name) { @@ -112,11 +99,7 @@ void awaitStatusUpdated(String name) { .untilAsserted( () -> { SubResourceTestCustomResource cr = - (SubResourceTestCustomResource) integrationTestSupport - .getCrOperations() - .inNamespace(TEST_NAMESPACE) - .withName(name) - .get(); + operator.getNamedResource(SubResourceTestCustomResource.class, name); assertThat(cr.getMetadata().getFinalizers()).hasSize(1); assertThat(cr).isNotNull(); assertThat(cr.getStatus()).isNotNull(); @@ -130,7 +113,6 @@ public SubResourceTestCustomResource createTestCustomResource(String id) { resource.setMetadata( new ObjectMetaBuilder() .withName("subresource-" + id) - .withNamespace(TEST_NAMESPACE) .withFinalizers(SubResourceTestCustomResourceController.FINALIZER_NAME) .build()); resource.setKind("SubresourceSample"); diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/UpdatingResAndSubResIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/UpdatingResAndSubResIT.java index f29fb7c4f9..9d4e0fd30c 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/UpdatingResAndSubResIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/UpdatingResAndSubResIT.java @@ -1,57 +1,54 @@ package io.javaoperatorsdk.operator; -import static io.javaoperatorsdk.operator.IntegrationTestSupport.TEST_NAMESPACE; -import static org.assertj.core.api.Assertions.assertThat; -import static org.awaitility.Awaitility.await; +import io.javaoperatorsdk.operator.support.TestUtils; +import java.util.concurrent.TimeUnit; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; import io.fabric8.kubernetes.api.model.ObjectMetaBuilder; -import io.fabric8.kubernetes.client.DefaultKubernetesClient; -import io.fabric8.kubernetes.client.KubernetesClient; +import io.javaoperatorsdk.operator.config.runtime.DefaultConfigurationService; +import io.javaoperatorsdk.operator.junit.OperatorExtension; import io.javaoperatorsdk.operator.sample.doubleupdate.DoubleUpdateTestCustomResource; import io.javaoperatorsdk.operator.sample.doubleupdate.DoubleUpdateTestCustomResourceController; import io.javaoperatorsdk.operator.sample.doubleupdate.DoubleUpdateTestCustomResourceSpec; import io.javaoperatorsdk.operator.sample.doubleupdate.DoubleUpdateTestCustomResourceStatus; -import java.util.concurrent.TimeUnit; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.TestInstance; -@TestInstance(TestInstance.Lifecycle.PER_CLASS) -public class UpdatingResAndSubResIT { - - private IntegrationTestSupport integrationTestSupport = new IntegrationTestSupport(); +import static org.assertj.core.api.Assertions.assertThat; +import static org.awaitility.Awaitility.await; - @BeforeEach - public void initAndCleanup() { - KubernetesClient k8sClient = new DefaultKubernetesClient(); - integrationTestSupport.initialize(k8sClient, new DoubleUpdateTestCustomResourceController()); - integrationTestSupport.cleanup(); - } +public class UpdatingResAndSubResIT { + @RegisterExtension + OperatorExtension operator = + OperatorExtension.builder() + .withConfigurationService(DefaultConfigurationService.instance()) + .withController(DoubleUpdateTestCustomResourceController.class) + .build(); @Test public void updatesSubResourceStatus() { - integrationTestSupport.teardownIfSuccess( - () -> { - DoubleUpdateTestCustomResource resource = createTestCustomResource("1"); - integrationTestSupport.getCrOperations().inNamespace(TEST_NAMESPACE).create(resource); + DoubleUpdateTestCustomResource resource = createTestCustomResource("1"); + operator.create(DoubleUpdateTestCustomResource.class, resource); + + awaitStatusUpdated(resource.getMetadata().getName()); + // wait for sure, there are no more events + TestUtils.waitXms(300); - awaitStatusUpdated(resource.getMetadata().getName()); - // wait for sure, there are no more events - TestUtils.waitXms(300); + DoubleUpdateTestCustomResource customResource = + operator + .getNamedResource(DoubleUpdateTestCustomResource.class, + resource.getMetadata().getName()); - DoubleUpdateTestCustomResource customResource = - (DoubleUpdateTestCustomResource) integrationTestSupport - .getCustomResource(resource.getMetadata().getName()); - assertThat(integrationTestSupport.numberOfControllerExecutions()).isEqualTo(1); - assertThat(customResource.getStatus().getState()) - .isEqualTo(DoubleUpdateTestCustomResourceStatus.State.SUCCESS); - assertThat( - customResource - .getMetadata() - .getAnnotations() - .get(DoubleUpdateTestCustomResourceController.TEST_ANNOTATION)) - .isNotNull(); - }); + assertThat(TestUtils.getNumberOfExecutions(operator)) + .isEqualTo(1); + assertThat(customResource.getStatus().getState()) + .isEqualTo(DoubleUpdateTestCustomResourceStatus.State.SUCCESS); + assertThat( + customResource + .getMetadata() + .getAnnotations() + .get(DoubleUpdateTestCustomResourceController.TEST_ANNOTATION)) + .isNotNull(); } void awaitStatusUpdated(String name) { @@ -60,14 +57,14 @@ void awaitStatusUpdated(String name) { .untilAsserted( () -> { DoubleUpdateTestCustomResource cr = - (DoubleUpdateTestCustomResource) integrationTestSupport - .getCrOperations() - .inNamespace(TEST_NAMESPACE) - .withName(name) - .get(); - assertThat(cr.getMetadata().getFinalizers()).hasSize(1); - assertThat(cr).isNotNull(); - assertThat(cr.getStatus()).isNotNull(); + operator.getNamedResource(DoubleUpdateTestCustomResource.class, name); + + assertThat(cr) + .isNotNull(); + assertThat(cr.getMetadata().getFinalizers()) + .hasSize(1); + assertThat(cr.getStatus()) + .isNotNull(); assertThat(cr.getStatus().getState()) .isEqualTo(DoubleUpdateTestCustomResourceStatus.State.SUCCESS); }); @@ -75,11 +72,7 @@ void awaitStatusUpdated(String name) { public DoubleUpdateTestCustomResource createTestCustomResource(String id) { DoubleUpdateTestCustomResource resource = new DoubleUpdateTestCustomResource(); - resource.setMetadata( - new ObjectMetaBuilder() - .withName("doubleupdateresource-" + id) - .withNamespace(TEST_NAMESPACE) - .build()); + resource.setMetadata(new ObjectMetaBuilder().withName("doubleupdateresource-" + id).build()); resource.setKind("DoubleUpdateSample"); resource.setSpec(new DoubleUpdateTestCustomResourceSpec()); resource.getSpec().setValue(id); diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/config/runtime/DefaultConfigurationServiceTest.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/config/runtime/DefaultConfigurationServiceTest.java index b0205bd4ef..0252ae8ac1 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/config/runtime/DefaultConfigurationServiceTest.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/config/runtime/DefaultConfigurationServiceTest.java @@ -28,6 +28,8 @@ public class DefaultConfigurationServiceTest { @Test void attemptingToRetrieveAnUnknownControllerShouldLogWarning() { + final var configurationService = DefaultConfigurationService.instance(); + final LoggerContext context = LoggerContext.getContext(false); final PatternLayout layout = PatternLayout.createDefaultLayout(context.getConfiguration()); final ListAppender appender = new ListAppender("list", null, layout, false, false); @@ -37,11 +39,12 @@ void attemptingToRetrieveAnUnknownControllerShouldLogWarning() { context.getConfiguration().addAppender(appender); AppenderRef ref = AppenderRef.createAppenderRef("list", null, null); + final var loggerName = configurationService.getLoggerName(); LoggerConfig loggerConfig = LoggerConfig.createLogger( false, Level.valueOf("info"), - AbstractConfigurationService.LOGGER_NAME, + loggerName, "false", new AppenderRef[] {ref}, null, @@ -49,12 +52,12 @@ void attemptingToRetrieveAnUnknownControllerShouldLogWarning() { null); loggerConfig.addAppender(appender, null, null); - context.getConfiguration().addLogger(AbstractConfigurationService.LOGGER_NAME, loggerConfig); + context.getConfiguration().addLogger(loggerName, loggerConfig); context.updateLoggers(); try { final var config = - DefaultConfigurationService.instance() + configurationService .getConfigurationFor(new NotAutomaticallyCreated(), false); assertThat(config).isNull(); @@ -64,7 +67,7 @@ void attemptingToRetrieveAnUnknownControllerShouldLogWarning() { } finally { appender.stop(); - context.getConfiguration().removeLogger(AbstractConfigurationService.LOGGER_NAME); + context.getConfiguration().removeLogger(loggerName); context.updateLoggers(); } } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/doubleupdate/DoubleUpdateTestCustomResourceController.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/doubleupdate/DoubleUpdateTestCustomResourceController.java index e3959681d9..37dcec276c 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/doubleupdate/DoubleUpdateTestCustomResourceController.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/doubleupdate/DoubleUpdateTestCustomResourceController.java @@ -1,6 +1,6 @@ package io.javaoperatorsdk.operator.sample.doubleupdate; -import io.javaoperatorsdk.operator.TestExecutionInfoProvider; +import io.javaoperatorsdk.operator.support.TestExecutionInfoProvider; import io.javaoperatorsdk.operator.api.*; import java.util.HashMap; import java.util.concurrent.atomic.AtomicInteger; diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/event/EventSourceTestCustomResourceController.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/event/EventSourceTestCustomResourceController.java index 7f42429b5f..394b78bc94 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/event/EventSourceTestCustomResourceController.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/event/EventSourceTestCustomResourceController.java @@ -2,7 +2,7 @@ import io.fabric8.kubernetes.client.CustomResource; import io.javaoperatorsdk.operator.ControllerUtils; -import io.javaoperatorsdk.operator.TestExecutionInfoProvider; +import io.javaoperatorsdk.operator.support.TestExecutionInfoProvider; import io.javaoperatorsdk.operator.api.Context; import io.javaoperatorsdk.operator.api.Controller; import io.javaoperatorsdk.operator.api.ResourceController; diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/retry/RetryTestCustomResourceController.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/retry/RetryTestCustomResourceController.java index 512a0a55a4..847f247872 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/retry/RetryTestCustomResourceController.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/retry/RetryTestCustomResourceController.java @@ -2,7 +2,7 @@ import io.fabric8.kubernetes.client.CustomResource; import io.javaoperatorsdk.operator.ControllerUtils; -import io.javaoperatorsdk.operator.TestExecutionInfoProvider; +import io.javaoperatorsdk.operator.support.TestExecutionInfoProvider; import io.javaoperatorsdk.operator.api.Context; import io.javaoperatorsdk.operator.api.Controller; import io.javaoperatorsdk.operator.api.ResourceController; diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/simple/TestCustomResourceController.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/simple/TestCustomResourceController.java index 0ca5413f31..e26b629a74 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/simple/TestCustomResourceController.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/simple/TestCustomResourceController.java @@ -6,12 +6,13 @@ import io.fabric8.kubernetes.client.CustomResource; import io.fabric8.kubernetes.client.KubernetesClient; import io.javaoperatorsdk.operator.ControllerUtils; -import io.javaoperatorsdk.operator.TestExecutionInfoProvider; +import io.javaoperatorsdk.operator.support.TestExecutionInfoProvider; import io.javaoperatorsdk.operator.api.Context; import io.javaoperatorsdk.operator.api.Controller; import io.javaoperatorsdk.operator.api.DeleteControl; import io.javaoperatorsdk.operator.api.ResourceController; import io.javaoperatorsdk.operator.api.UpdateControl; +import io.javaoperatorsdk.operator.junit.KubernetesClientAware; import java.util.HashMap; import java.util.Map; import java.util.concurrent.atomic.AtomicInteger; @@ -20,26 +21,44 @@ @Controller(generationAwareEventProcessing = false) public class TestCustomResourceController - implements ResourceController, TestExecutionInfoProvider { + implements ResourceController, TestExecutionInfoProvider, + KubernetesClientAware { private static final Logger log = LoggerFactory.getLogger(TestCustomResourceController.class); public static final String FINALIZER_NAME = ControllerUtils.getDefaultFinalizerName(CustomResource.getCRDName(TestCustomResource.class)); - private final KubernetesClient kubernetesClient; - private final boolean updateStatus; private final AtomicInteger numberOfExecutions = new AtomicInteger(0); + private KubernetesClient kubernetesClient; + private boolean updateStatus; - public TestCustomResourceController(KubernetesClient kubernetesClient) { - this(kubernetesClient, true); + public TestCustomResourceController() { + this(true); } - public TestCustomResourceController(KubernetesClient kubernetesClient, boolean updateStatus) { - this.kubernetesClient = kubernetesClient; + public TestCustomResourceController(boolean updateStatus) { this.updateStatus = updateStatus; } + public boolean isUpdateStatus() { + return updateStatus; + } + + public void setUpdateStatus(boolean updateStatus) { + this.updateStatus = updateStatus; + } + + @Override + public KubernetesClient getKubernetesClient() { + return kubernetesClient; + } + + @Override + public void setKubernetesClient(KubernetesClient kubernetesClient) { + this.kubernetesClient = kubernetesClient; + } + @Override public DeleteControl deleteResource( TestCustomResource resource, Context context) { diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/subresource/SubResourceTestCustomResourceController.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/subresource/SubResourceTestCustomResourceController.java index 50f0285b22..8084ad7cfc 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/subresource/SubResourceTestCustomResourceController.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/subresource/SubResourceTestCustomResourceController.java @@ -2,7 +2,7 @@ import io.fabric8.kubernetes.client.CustomResource; import io.javaoperatorsdk.operator.ControllerUtils; -import io.javaoperatorsdk.operator.TestExecutionInfoProvider; +import io.javaoperatorsdk.operator.support.TestExecutionInfoProvider; import io.javaoperatorsdk.operator.api.Context; import io.javaoperatorsdk.operator.api.Controller; import io.javaoperatorsdk.operator.api.ResourceController; diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/TestExecutionInfoProvider.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/support/TestExecutionInfoProvider.java similarity index 64% rename from operator-framework/src/test/java/io/javaoperatorsdk/operator/TestExecutionInfoProvider.java rename to operator-framework/src/test/java/io/javaoperatorsdk/operator/support/TestExecutionInfoProvider.java index caf0211df4..f2f634041d 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/TestExecutionInfoProvider.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/support/TestExecutionInfoProvider.java @@ -1,4 +1,4 @@ -package io.javaoperatorsdk.operator; +package io.javaoperatorsdk.operator.support; public interface TestExecutionInfoProvider { diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/TestUtils.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/support/TestUtils.java similarity index 60% rename from operator-framework/src/test/java/io/javaoperatorsdk/operator/TestUtils.java rename to operator-framework/src/test/java/io/javaoperatorsdk/operator/support/TestUtils.java index 2029970f0f..76ae1c4991 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/TestUtils.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/support/TestUtils.java @@ -1,6 +1,7 @@ -package io.javaoperatorsdk.operator; +package io.javaoperatorsdk.operator.support; import io.fabric8.kubernetes.api.model.ObjectMetaBuilder; +import io.javaoperatorsdk.operator.junit.OperatorExtension; import io.javaoperatorsdk.operator.sample.simple.TestCustomResource; import io.javaoperatorsdk.operator.sample.simple.TestCustomResourceSpec; import java.util.HashMap; @@ -8,6 +9,7 @@ public class TestUtils { + public static final String TEST_CUSTOM_RESOURCE_PREFIX = "test-custom-resource-"; public static final String TEST_CUSTOM_RESOURCE_NAME = "test-custom-resource"; public static final String TEST_NAMESPACE = "java-operator-sdk-int-test"; @@ -22,7 +24,6 @@ public static TestCustomResource testCustomResource(String uid) { .withName(TEST_CUSTOM_RESOURCE_NAME) .withUid(uid) .withGeneration(1L) - .withNamespace(TEST_NAMESPACE) .build()); resource.getMetadata().setAnnotations(new HashMap<>()); resource.setKind("CustomService"); @@ -33,6 +34,20 @@ public static TestCustomResource testCustomResource(String uid) { return resource; } + public static TestCustomResource testCustomResourceWithPrefix(String id) { + TestCustomResource resource = new TestCustomResource(); + resource.setMetadata( + new ObjectMetaBuilder() + .withName(TEST_CUSTOM_RESOURCE_PREFIX + id) + .build()); + resource.setKind("CustomService"); + resource.setSpec(new TestCustomResourceSpec()); + resource.getSpec().setConfigMapName("test-config-map-" + id); + resource.getSpec().setKey("test-key"); + resource.getSpec().setValue(id); + return resource; + } + public static void waitXms(int x) { try { Thread.sleep(x); @@ -40,4 +55,8 @@ public static void waitXms(int x) { throw new IllegalStateException(e); } } + + public static int getNumberOfExecutions(OperatorExtension extension) { + return ((TestExecutionInfoProvider) extension.getControllers().get(0)).getNumberOfExecutions(); + } } diff --git a/pom.xml b/pom.xml index 18914e47c7..901dd73ee4 100644 --- a/pom.xml +++ b/pom.xml @@ -74,6 +74,7 @@ operator-framework-core + operator-framework-junit5 operator-framework samples @@ -202,6 +203,9 @@ org.apache.maven.plugins maven-surefire-plugin ${maven-surefire-plugin.version} + + 3 + org.apache.maven.plugins