From 5a546d3404e8dde5341ed3ce574ac7ac0cc1d4f9 Mon Sep 17 00:00:00 2001 From: Chris Laprun Date: Fri, 4 Feb 2022 14:53:01 +0100 Subject: [PATCH] refactor: share more code --- .../junit/AbstractOperatorExtension.java | 97 ++++++++++++++++-- .../operator/junit/E2EOperatorExtension.java | 91 +++-------------- .../operator/junit/OperatorExtension.java | 98 +++---------------- 3 files changed, 116 insertions(+), 170 deletions(-) diff --git a/operator-framework-junit5/src/main/java/io/javaoperatorsdk/operator/junit/AbstractOperatorExtension.java b/operator-framework-junit5/src/main/java/io/javaoperatorsdk/operator/junit/AbstractOperatorExtension.java index fb204e8c57..839b550231 100644 --- a/operator-framework-junit5/src/main/java/io/javaoperatorsdk/operator/junit/AbstractOperatorExtension.java +++ b/operator-framework-junit5/src/main/java/io/javaoperatorsdk/operator/junit/AbstractOperatorExtension.java @@ -2,14 +2,20 @@ import java.time.Duration; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import java.util.Locale; import java.util.UUID; +import java.util.concurrent.TimeUnit; +import org.awaitility.Awaitility; import org.junit.jupiter.api.extension.*; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; 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; @@ -26,7 +32,9 @@ public abstract class AbstractOperatorExtension implements HasKubernetesClient, AfterAllCallback, AfterEachCallback { - protected final KubernetesClient kubernetesClient; + private static final Logger LOGGER = LoggerFactory.getLogger(AbstractOperatorExtension.class); + + private final KubernetesClient kubernetesClient; protected final ConfigurationService configurationService; protected final List infrastructure; protected Duration infrastructureTimeout; @@ -55,22 +63,22 @@ protected AbstractOperatorExtension( @Override - public void beforeAll(ExtensionContext context) throws Exception { + public void beforeAll(ExtensionContext context) { beforeAllImpl(context); } @Override - public void beforeEach(ExtensionContext context) throws Exception { + public void beforeEach(ExtensionContext context) { beforeEachImpl(context); } @Override - public void afterAll(ExtensionContext context) throws Exception { + public void afterAll(ExtensionContext context) { afterAllImpl(context); } @Override - public void afterEach(ExtensionContext context) throws Exception { + public void afterEach(ExtensionContext context) { afterEachImpl(context); } @@ -100,6 +108,7 @@ public T replace(Class type, T resource) { return kubernetesClient.resources(type).inNamespace(namespace).replace(resource); } + @SuppressWarnings("unchecked") public boolean delete(Class type, T resource) { return kubernetesClient.resources(type).inNamespace(namespace).delete(resource); } @@ -130,7 +139,20 @@ protected void beforeEachImpl(ExtensionContext context) { } } - protected abstract void before(ExtensionContext context); + protected void before(ExtensionContext context) { + LOGGER.info("Initializing integration test in namespace {}", namespace); + + kubernetesClient + .namespaces() + .create(new NamespaceBuilder().withNewMetadata().withName(namespace).endMetadata().build()); + + kubernetesClient + .resourceList(infrastructure) + .createOrReplace(); + kubernetesClient + .resourceList(infrastructure) + .waitUntilReady(infrastructureTimeout.toMillis(), TimeUnit.MILLISECONDS); + } protected void afterAllImpl(ExtensionContext context) { if (oneNamespacePerClass) { @@ -144,9 +166,32 @@ protected void afterEachImpl(ExtensionContext context) { } } - protected abstract void after(ExtensionContext context); + protected void after(ExtensionContext context) { + if (namespace != null) { + if (preserveNamespaceOnError && context.getExecutionException().isPresent()) { + LOGGER.info("Preserving namespace {}", namespace); + } else { + kubernetesClient.resourceList(infrastructure).delete(); + deleteOperator(); + 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(90, TimeUnit.SECONDS) + .until(() -> kubernetesClient.namespaces().withName(namespace).get() == null); + } + } + } + } + + protected void deleteOperator() { + // nothing to do by default: only needed if the operator is deployed to the cluster + } - public static abstract class AbstractBuilder { + @SuppressWarnings("unchecked") + public static abstract class AbstractBuilder> { protected ConfigurationService configurationService; protected final List infrastructure; protected Duration infrastructureTimeout; @@ -172,5 +217,41 @@ protected AbstractBuilder() { "josdk.it.oneNamespacePerClass", false); } + + public T preserveNamespaceOnError(boolean value) { + this.preserveNamespaceOnError = value; + return (T) this; + } + + public T waitForNamespaceDeletion(boolean value) { + this.waitForNamespaceDeletion = value; + return (T) this; + } + + public T oneNamespacePerClass(boolean value) { + this.oneNamespacePerClass = value; + return (T) this; + } + + public T withConfigurationService(ConfigurationService value) { + configurationService = value; + return (T) this; + } + + public T withInfrastructureTimeout(Duration value) { + infrastructureTimeout = value; + return (T) this; + } + + public T withInfrastructure(List hm) { + infrastructure.addAll(hm); + return (T) this; + } + + public T withInfrastructure(HasMetadata... hms) { + infrastructure.addAll(Arrays.asList(hms)); + return (T) this; + } + } } diff --git a/operator-framework-junit5/src/main/java/io/javaoperatorsdk/operator/junit/E2EOperatorExtension.java b/operator-framework-junit5/src/main/java/io/javaoperatorsdk/operator/junit/E2EOperatorExtension.java index 6710b1fc92..91fecafc2e 100644 --- a/operator-framework-junit5/src/main/java/io/javaoperatorsdk/operator/junit/E2EOperatorExtension.java +++ b/operator-framework-junit5/src/main/java/io/javaoperatorsdk/operator/junit/E2EOperatorExtension.java @@ -5,17 +5,17 @@ import java.io.InputStream; import java.time.Duration; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import java.util.Locale; +import java.util.Objects; import java.util.concurrent.TimeUnit; -import org.awaitility.Awaitility; import org.junit.jupiter.api.extension.ExtensionContext; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import io.fabric8.kubernetes.api.model.HasMetadata; -import io.fabric8.kubernetes.api.model.NamespaceBuilder; import io.fabric8.kubernetes.api.model.rbac.ClusterRoleBinding; import io.javaoperatorsdk.operator.api.config.ConfigurationService; @@ -51,25 +51,15 @@ public static Builder builder() { return new Builder(); } - @SuppressWarnings("unchecked") protected void before(ExtensionContext context) { - LOGGER.info("Initializing integration test in namespace {}", namespace); - - kubernetesClient - .namespaces() - .create(new NamespaceBuilder().withNewMetadata().withName(namespace).endMetadata().build()); - - kubernetesClient - .resourceList(infrastructure) - .createOrReplace(); - kubernetesClient - .resourceList(infrastructure) - .waitUntilReady(infrastructureTimeout.toMillis(), TimeUnit.MILLISECONDS); + super.before(context); final var crdPath = "./target/classes/META-INF/fabric8/"; final var crdSuffix = "-v1.yml"; - for (var crdFile : new File(crdPath).listFiles((ignored, name) -> name.endsWith(crdSuffix))) { + final var kubernetesClient = getKubernetesClient(); + for (var crdFile : Objects + .requireNonNull(new File(crdPath).listFiles((ignored, name) -> name.endsWith(crdSuffix)))) { try (InputStream is = new FileInputStream(crdFile)) { final var crd = kubernetesClient.load(is); crd.createOrReplace(); @@ -81,7 +71,7 @@ protected void before(ExtensionContext context) { } LOGGER.debug("Deploying the operator into Kubernetes"); - operatorDeployment.stream().forEach(hm -> { + operatorDeployment.forEach(hm -> { hm.getMetadata().setNamespace(namespace); if (hm.getKind().toLowerCase(Locale.ROOT).equals("clusterrolebinding")) { var crb = (ClusterRoleBinding) hm; @@ -100,88 +90,33 @@ protected void before(ExtensionContext context) { .waitUntilReady(operatorDeploymentTimeout.toMillis(), TimeUnit.MILLISECONDS); } - protected void after(ExtensionContext context) { - if (namespace != null) { - if (preserveNamespaceOnError && context.getExecutionException().isPresent()) { - LOGGER.info("Preserving namespace {}", namespace); - } else { - kubernetesClient.resourceList(infrastructure).delete(); - kubernetesClient.resourceList(operatorDeployment).inNamespace(namespace).delete(); - 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(90, TimeUnit.SECONDS) - .until(() -> kubernetesClient.namespaces().withName(namespace).get() == null); - } - } - } + @Override + protected void deleteOperator() { + getKubernetesClient().resourceList(operatorDeployment).inNamespace(namespace).delete(); } - @SuppressWarnings("rawtypes") - public static class Builder extends AbstractBuilder { + public static class Builder extends AbstractBuilder { private final List operatorDeployment; private Duration deploymentTimeout; protected Builder() { - super();; + super(); this.operatorDeployment = new ArrayList<>(); this.deploymentTimeout = Duration.ofMinutes(1); } - public Builder preserveNamespaceOnError(boolean value) { - this.preserveNamespaceOnError = value; - return this; - } - - public Builder waitForNamespaceDeletion(boolean value) { - this.waitForNamespaceDeletion = value; - return this; - } - - public Builder oneNamespacePerClass(boolean value) { - this.oneNamespacePerClass = value; - return this; - } - - public Builder withConfigurationService(ConfigurationService value) { - configurationService = value; - return this; - } - public Builder withDeploymentTimeout(Duration value) { deploymentTimeout = value; return this; } - public Builder withInfrastructureTimeout(Duration value) { - infrastructureTimeout = value; - return this; - } - - public Builder withInfrastructure(List hm) { - infrastructure.addAll(hm); - return this; - } - - public Builder withInfrastructure(HasMetadata... hms) { - for (HasMetadata hm : hms) { - infrastructure.add(hm); - } - return this; - } - public Builder withOperatorDeployment(List hm) { operatorDeployment.addAll(hm); return this; } public Builder withOperatorDeployment(HasMetadata... hms) { - for (HasMetadata hm : hms) { - operatorDeployment.add(hm); - } + operatorDeployment.addAll(Arrays.asList(hms)); return this; } 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 index 247ec9043b..76d848c896 100644 --- 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 @@ -6,24 +6,22 @@ import java.util.List; import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; +import java.util.stream.Stream; -import org.awaitility.Awaitility; import org.junit.jupiter.api.extension.ExtensionContext; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import io.fabric8.kubernetes.api.model.HasMetadata; -import io.fabric8.kubernetes.api.model.NamespaceBuilder; import io.javaoperatorsdk.operator.Operator; -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.api.reconciler.Reconciler; import io.javaoperatorsdk.operator.processing.Controller; import io.javaoperatorsdk.operator.processing.retry.Retry; import static io.javaoperatorsdk.operator.api.config.ControllerConfigurationOverrider.override; +@SuppressWarnings("rawtypes") public class OperatorExtension extends AbstractOperatorExtension { private static final Logger LOGGER = LoggerFactory.getLogger(OperatorExtension.class); @@ -43,7 +41,7 @@ private OperatorExtension( preserveNamespaceOnError, waitForNamespaceDeletion); this.reconcilers = reconcilers; - this.operator = new Operator(this.kubernetesClient, this.configurationService); + this.operator = new Operator(getKubernetesClient(), this.configurationService); } /** @@ -55,23 +53,20 @@ public static Builder builder() { return new Builder(); } - @SuppressWarnings({"rawtypes"}) + private Stream reconcilers() { + return operator.getControllers().stream().map(Controller::getReconciler); + } + public List getReconcilers() { - return operator.getControllers().stream() - .map(Controller::getReconciler) - .collect(Collectors.toUnmodifiableList()); + return reconcilers().collect(Collectors.toUnmodifiableList()); } public Reconciler getFirstReconciler() { - return operator.getControllers().stream() - .map(Controller::getReconciler) - .findFirst().orElseThrow(); + return reconcilers().findFirst().orElseThrow(); } - @SuppressWarnings({"rawtypes"}) public T getControllerOfType(Class type) { - return operator.getControllers().stream() - .map(Controller::getReconciler) + return reconcilers() .filter(type::isInstance) .map(type::cast) .findFirst() @@ -81,18 +76,7 @@ public T getControllerOfType(Class type) { @SuppressWarnings("unchecked") protected void before(ExtensionContext context) { - LOGGER.info("Initializing integration test in namespace {}", namespace); - - kubernetesClient - .namespaces() - .create(new NamespaceBuilder().withNewMetadata().withName(namespace).endMetadata().build()); - - kubernetesClient - .resourceList(infrastructure) - .createOrReplace(); - kubernetesClient - .resourceList(infrastructure) - .waitUntilReady(infrastructureTimeout.toMillis(), TimeUnit.MILLISECONDS); + super.before(context); for (var ref : reconcilers) { final var config = configurationService.getConfigurationFor(ref.reconciler); @@ -103,6 +87,7 @@ protected void before(ExtensionContext context) { oconfig.withRetry(ref.retry); } + final var kubernetesClient = getKubernetesClient(); try (InputStream is = getClass().getResourceAsStream(path)) { final var crd = kubernetesClient.load(is); crd.createOrReplace(); @@ -116,7 +101,6 @@ protected void before(ExtensionContext context) { ((KubernetesClientAware) ref.reconciler).setKubernetesClient(kubernetesClient); } - this.operator.register(ref.reconciler, oconfig.build()); } @@ -125,22 +109,7 @@ protected void before(ExtensionContext context) { } protected void after(ExtensionContext context) { - if (namespace != null) { - if (preserveNamespaceOnError && context.getExecutionException().isPresent()) { - LOGGER.info("Preserving namespace {}", namespace); - } else { - kubernetesClient.resourceList(infrastructure).delete(); - 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(90, TimeUnit.SECONDS) - .until(() -> kubernetesClient.namespaces().withName(namespace).get() == null); - } - } - } + super.after(context); try { this.operator.stop(); @@ -150,53 +119,14 @@ protected void after(ExtensionContext context) { } @SuppressWarnings("rawtypes") - public static class Builder extends AbstractBuilder { + public static class Builder extends AbstractBuilder { private final List reconcilers; - private ConfigurationService configurationService; protected Builder() { super(); - this.configurationService = new BaseConfigurationService(Version.UNKNOWN); this.reconcilers = new ArrayList<>(); } - public Builder preserveNamespaceOnError(boolean value) { - this.preserveNamespaceOnError = value; - return this; - } - - public Builder waitForNamespaceDeletion(boolean value) { - this.waitForNamespaceDeletion = value; - return this; - } - - public Builder oneNamespacePerClass(boolean value) { - this.oneNamespacePerClass = value; - return this; - } - - public Builder withConfigurationService(ConfigurationService value) { - configurationService = value; - return this; - } - - public Builder withInfrastructureTimeout(Duration value) { - infrastructureTimeout = value; - return this; - } - - public Builder withInfrastructure(List hm) { - infrastructure.addAll(hm); - return this; - } - - public Builder withInfrastructure(HasMetadata... hms) { - for (HasMetadata hm : hms) { - infrastructure.add(hm); - } - return this; - } - @SuppressWarnings("rawtypes") public Builder withReconciler(Reconciler value) { reconcilers.add(new ReconcilerSpec(value, null));